Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-05-10 10:25:57 +02:00
commit f786d9c96e
174 changed files with 9811 additions and 5525 deletions

View file

@ -268,8 +268,6 @@ set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)
include_directories(${LIBDIR}) include_directories(${LIBDIR})
# For generated header files # For generated header files
include_directories(${LIBDIR_BIN}/platform) include_directories(${LIBDIR_BIN}/platform)
# For libslic3r.h
include_directories(${LIBDIR}/clipper)
if(WIN32) if(WIN32)
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)

View file

@ -5,7 +5,7 @@ use warnings;
require Exporter; require Exporter;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT_OK = qw( our @EXPORT_OK = qw(
offset offset2 offset
offset_ex offset2_ex offset_ex offset2_ex
diff_ex diff union_ex intersection_ex diff_ex diff union_ex intersection_ex
JT_ROUND JT_MITER JT_SQUARE JT_ROUND JT_MITER JT_SQUARE

View file

@ -7,7 +7,7 @@ use List::Util qw(min max sum first);
use Slic3r::Flow ':roles'; use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(scale epsilon); use Slic3r::Geometry qw(scale epsilon);
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
offset offset2 offset_ex offset2_ex JT_MITER); offset offset_ex offset2_ex JT_MITER);
use Slic3r::Print::State ':steps'; use Slic3r::Print::State ':steps';
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Before After
Before After

View file

@ -1,13 +1,14 @@
min_slic3r_version = 2.3.1-beta min_slic3r_version = 2.3.1-beta
0.0.9 Updated bed textures 0.0.10 Various updates for Anycubic Mega. Added filament profiles.
min_slic3r_version = 2.3.0-beta2 0.0.9 Updated bed textures
0.0.8 Updated start and end g-code for Anycubic Mega. min_slic3r_version = 2.3.0-beta2
0.0.7 Updated start g-code for Anycubic Mega. 0.0.8 Updated start and end g-code for Anycubic Mega.
0.0.6 Reduced max print height for Predator. Updated end g-code, before layer change g-code and output filename format for Kossel. 0.0.7 Updated start g-code for Anycubic Mega.
0.0.5 Updated end g-code. 0.0.6 Reduced max print height for Predator. Updated end g-code, before layer change g-code and output filename format for Kossel.
min_slic3r_version = 2.3.0-alpha2 0.0.5 Updated end g-code.
0.0.4 Fixed predator output filename format, infill overlap, start gcode adjustments. min_slic3r_version = 2.3.0-alpha2
0.0.3 Fixed infill_overlap, start_gcode, end_gcode for Anycubic Predator 0.0.4 Fixed predator output filename format, infill overlap, start gcode adjustments.
0.0.2 Added Anycubic Predator 0.0.3 Fixed infill_overlap, start_gcode, end_gcode for Anycubic Predator
min_slic3r_version = 2.3.0-alpha0 0.0.2 Added Anycubic Predator
0.0.1 Initial Version min_slic3r_version = 2.3.0-alpha0
0.0.1 Initial Version

View file

@ -5,7 +5,7 @@
name = Anycubic name = Anycubic
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 0.0.9 config_version = 0.0.10
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anycubic/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anycubic/
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -76,7 +76,7 @@ bridge_flow_ratio = 0.8
bridge_speed = 30 bridge_speed = 30
brim_width = 0 brim_width = 0
clip_multipart_objects = 1 clip_multipart_objects = 1
compatible_printers = compatible_printers =
complete_objects = 0 complete_objects = 0
dont_support_bridges = 1 dont_support_bridges = 1
elefant_foot_compensation = 0 elefant_foot_compensation = 0
@ -108,7 +108,7 @@ max_volumetric_extrusion_rate_slope_negative = 0
max_volumetric_extrusion_rate_slope_positive = 0 max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0 max_volumetric_speed = 0
min_skirt_length = 4 min_skirt_length = 4
notes = notes =
overhangs = 0 overhangs = 0
only_retract_when_crossing_perimeters = 0 only_retract_when_crossing_perimeters = 0
ooze_prevention = 0 ooze_prevention = 0
@ -117,8 +117,8 @@ perimeters = 2
perimeter_extruder = 1 perimeter_extruder = 1
perimeter_extrusion_width = 0.45 perimeter_extrusion_width = 0.45
perimeter_speed = 45 perimeter_speed = 45
post_process = post_process =
print_settings_id = print_settings_id =
raft_layers = 0 raft_layers = 0
resolution = 0 resolution = 0
seam_position = nearest seam_position = nearest
@ -290,7 +290,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_AK(|LP).*/ and n
# Common filament preset # Common filament preset
[filament:*common_akossel*] [filament:*common_akossel*]
cooling = 0 cooling = 0
compatible_printers = compatible_printers =
extrusion_multiplier = 1 extrusion_multiplier = 1
filament_cost = 0 filament_cost = 0
filament_density = 0 filament_density = 0
@ -375,9 +375,9 @@ filament_vendor = Generic
# Common printer preset # Common printer preset
[printer:*common_akossel*] [printer:*common_akossel*]
printer_technology = FFF printer_technology = FFF
bed_shape = bed_shape =
before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0\n;[layer_z] before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0\n;[layer_z]
between_objects_gcode = between_objects_gcode =
deretract_speed = 40 deretract_speed = 40
extruder_colour = #FFFF00 extruder_colour = #FFFF00
extruder_offset = 0x0 extruder_offset = 0x0
@ -405,8 +405,8 @@ max_layer_height = 0.3
min_layer_height = 0.08 min_layer_height = 0.08
max_print_height = 300 max_print_height = 300
nozzle_diameter = 0.4 nozzle_diameter = 0.4
printer_notes = printer_notes =
printer_settings_id = printer_settings_id =
retract_before_travel = 2 retract_before_travel = 2
retract_before_wipe = 70% retract_before_wipe = 70%
retract_layer_change = 1 retract_layer_change = 1
@ -419,9 +419,9 @@ retract_restart_extra = 0
retract_restart_extra_toolchange = 0 retract_restart_extra_toolchange = 0
retract_speed = 60 retract_speed = 60
single_extruder_multi_material = 0 single_extruder_multi_material = 0
start_gcode = start_gcode =
end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG28 ; home\nM84 ; disable motors end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG28 ; home\nM84 ; disable motors
toolchange_gcode = toolchange_gcode =
use_firmware_retraction = 0 use_firmware_retraction = 0
use_relative_e_distances = 1 use_relative_e_distances = 1
use_volumetric_e = 0 use_volumetric_e = 0
@ -435,7 +435,7 @@ default_filament_profile = Generic PLA @AKOSSEL
inherits = *common_akossel* inherits = *common_akossel*
printer_model = AKLP printer_model = AKLP
printer_variant = 0.4 printer_variant = 0.4
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_Anycubic\nPRINTER_MODEL_AKLP\nPRINTER_HAS_BOWDEN\n printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_Anycubic\nPRINTER_MODEL_AKLP\nPRINTER_HAS_BOWDEN\n
bed_shape = 114.562x10.0229,113.253x19.9695,111.081x29.7642,108.065x39.3323,104.225x48.6011,99.5929x57.5,94.2025x65.9613,88.0951x73.9206,81.3173x81.3173,73.9206x88.0951,65.9613x94.2025,57.5x99.5929,48.6011x104.225,39.3323x108.065,29.7642x111.081,19.9695x113.253,10.0229x114.562,7.04172e-15x115,-10.0229x114.562,-19.9695x113.253,-29.7642x111.081,-39.3323x108.065,-48.6011x104.225,-57.5x99.5929,-65.9613x94.2025,-73.9206x88.0951,-81.3173x81.3173,-88.0951x73.9206,-94.2025x65.9613,-99.5929x57.5,-104.225x48.6011,-108.065x39.3323,-111.081x29.7642,-113.253x19.9695,-114.562x10.0229,-115x1.40834e-14,-114.562x-10.0229,-113.253x-19.9695,-111.081x-29.7642,-108.065x-39.3323,-104.225x-48.6011,-99.5929x-57.5,-94.2025x-65.9613,-88.0951x-73.9206,-81.3173x-81.3173,-73.9206x-88.0951,-65.9613x-94.2025,-57.5x-99.5929,-48.6011x-104.225,-39.3323x-108.065,-29.7642x-111.081,-19.9695x-113.253,-10.0229x-114.562,-2.11252e-14x-115,10.0229x-114.562,19.9695x-113.253,29.7642x-111.081,39.3323x-108.065,48.6011x-104.225,57.5x-99.5929,65.9613x-94.2025,73.9206x-88.0951,81.3173x-81.3173,88.0951x-73.9206,94.2025x-65.9613,99.5929x-57.5,104.225x-48.6011,108.065x-39.3323,111.081x-29.7642,113.253x-19.9695,114.562x-10.0229,115x-2.81669e-14 bed_shape = 114.562x10.0229,113.253x19.9695,111.081x29.7642,108.065x39.3323,104.225x48.6011,99.5929x57.5,94.2025x65.9613,88.0951x73.9206,81.3173x81.3173,73.9206x88.0951,65.9613x94.2025,57.5x99.5929,48.6011x104.225,39.3323x108.065,29.7642x111.081,19.9695x113.253,10.0229x114.562,7.04172e-15x115,-10.0229x114.562,-19.9695x113.253,-29.7642x111.081,-39.3323x108.065,-48.6011x104.225,-57.5x99.5929,-65.9613x94.2025,-73.9206x88.0951,-81.3173x81.3173,-88.0951x73.9206,-94.2025x65.9613,-99.5929x57.5,-104.225x48.6011,-108.065x39.3323,-111.081x29.7642,-113.253x19.9695,-114.562x10.0229,-115x1.40834e-14,-114.562x-10.0229,-113.253x-19.9695,-111.081x-29.7642,-108.065x-39.3323,-104.225x-48.6011,-99.5929x-57.5,-94.2025x-65.9613,-88.0951x-73.9206,-81.3173x-81.3173,-73.9206x-88.0951,-65.9613x-94.2025,-57.5x-99.5929,-48.6011x-104.225,-39.3323x-108.065,-29.7642x-111.081,-19.9695x-113.253,-10.0229x-114.562,-2.11252e-14x-115,10.0229x-114.562,19.9695x-113.253,29.7642x-111.081,39.3323x-108.065,48.6011x-104.225,57.5x-99.5929,65.9613x-94.2025,73.9206x-88.0951,81.3173x-81.3173,88.0951x-73.9206,94.2025x-65.9613,99.5929x-57.5,104.225x-48.6011,108.065x-39.3323,111.081x-29.7642,113.253x-19.9695,114.562x-10.0229,115x-2.81669e-14
start_gcode = G90 ; use absolute coordinates\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 ; home\nG1 X-54.672 Y95.203 Z0.3 F9000\nG92 E0.0\nG1 F1000\nG1 X-52.931 Y96.185 E0.300\nG1 X-50.985 Y97.231 E0.331\nG1 X-49.018 Y98.238 E0.331\nG1 X-47.032 Y99.205 E0.331\nG1 X-45.026 Y100.132 E0.331\nG1 X-43.003 Y101.019 E0.331\nG1 X-40.961 Y101.864 E0.331\nG1 X-38.904 Y102.668 E0.331\nG1 X-36.83 Y103.431 E0.331\nG1 X-34.742 Y104.152 E0.331\nG1 X-32.639 Y104.83 E0.331\nG1 X-30.523 Y105.466 E0.331\nG1 X-28.395 Y106.06 E0.331\nG1 X-26.255 Y106.61 E0.331\nG1 X-24.105 Y107.117 E0.331\nG1 X-21.945 Y107.581 E0.331\nG1 X-19.776 Y108.001 E0.331\nG1 X-17.599 Y108.377 E0.331\nG1 X-15.415 Y108.71 E0.331\nG1 X-13.224 Y108.998 E0.331\nG1 X-11.028 Y109.242 E0.331\nG1 X-8.828 Y109.442 E0.331\nG1 X-6.624 Y109.598 E0.331\nG1 X-4.418 Y109.709 E0.331\nG1 X-2.209 Y109.776 E0.332\nG1 X0 Y109.798 E0.331\nG1 X2.209 Y109.776 E0.690\nG1 X4.418 Y109.709 E0.691\nG1 X6.624 Y109.598 E0.690\nG1 X8.828 Y109.442 E0.690\nG1 X11.028 Y109.242 E0.690\nG1 X13.224 Y108.998 E0.690\nG1 X15.415 Y108.71 E0.691\nG1 X17.599 Y108.377 E0.690\nG1 X19.776 Y108.001 E0.690\nG1 X21.945 Y107.581 E0.690\nG1 X24.105 Y107.117 E0.690\nG1 X26.255 Y106.61 E0.690\nG1 X28.395 Y106.06 E0.690\nG1 X30.523 Y105.466 E0.690\nG1 X32.639 Y104.83 E0.690\nG1 X34.742 Y104.152 E0.690\nG1 X36.83 Y103.431 E0.690\nG1 X38.904 Y102.668 E0.691\nG1 X40.961 Y101.864 E0.690\nG1 X43.003 Y101.019 E0.691\nG1 X45.026 Y100.132 E0.690\nG1 X47.032 Y99.205 E0.691\nG1 X49.018 Y98.238 E0.690\nG1 X50.985 Y97.231 E0.691\nG1 X52.931 Y96.185 E0.690\nG1 X54.672 Y95.203 E0.625\nG92 E0.0\nG1 E-5 F3000 ; retract 5mm\nG1 X52.931 Y96.185 F1000 ; wipe\nG1 X50.985 Y97.231 F1000 ; wipe\nG1 X49.018 Y98.238 F1000 ; wipe\nG1 X0 Y109.798 F1000\nG1 E4.8 F1500; de-retract\nG92 E0.0 ; reset extrusion distance\nM221 S{if layer_height<0.075}100{else}95{endif} start_gcode = G90 ; use absolute coordinates\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 ; home\nG1 X-54.672 Y95.203 Z0.3 F9000\nG92 E0.0\nG1 F1000\nG1 X-52.931 Y96.185 E0.300\nG1 X-50.985 Y97.231 E0.331\nG1 X-49.018 Y98.238 E0.331\nG1 X-47.032 Y99.205 E0.331\nG1 X-45.026 Y100.132 E0.331\nG1 X-43.003 Y101.019 E0.331\nG1 X-40.961 Y101.864 E0.331\nG1 X-38.904 Y102.668 E0.331\nG1 X-36.83 Y103.431 E0.331\nG1 X-34.742 Y104.152 E0.331\nG1 X-32.639 Y104.83 E0.331\nG1 X-30.523 Y105.466 E0.331\nG1 X-28.395 Y106.06 E0.331\nG1 X-26.255 Y106.61 E0.331\nG1 X-24.105 Y107.117 E0.331\nG1 X-21.945 Y107.581 E0.331\nG1 X-19.776 Y108.001 E0.331\nG1 X-17.599 Y108.377 E0.331\nG1 X-15.415 Y108.71 E0.331\nG1 X-13.224 Y108.998 E0.331\nG1 X-11.028 Y109.242 E0.331\nG1 X-8.828 Y109.442 E0.331\nG1 X-6.624 Y109.598 E0.331\nG1 X-4.418 Y109.709 E0.331\nG1 X-2.209 Y109.776 E0.332\nG1 X0 Y109.798 E0.331\nG1 X2.209 Y109.776 E0.690\nG1 X4.418 Y109.709 E0.691\nG1 X6.624 Y109.598 E0.690\nG1 X8.828 Y109.442 E0.690\nG1 X11.028 Y109.242 E0.690\nG1 X13.224 Y108.998 E0.690\nG1 X15.415 Y108.71 E0.691\nG1 X17.599 Y108.377 E0.690\nG1 X19.776 Y108.001 E0.690\nG1 X21.945 Y107.581 E0.690\nG1 X24.105 Y107.117 E0.690\nG1 X26.255 Y106.61 E0.690\nG1 X28.395 Y106.06 E0.690\nG1 X30.523 Y105.466 E0.690\nG1 X32.639 Y104.83 E0.690\nG1 X34.742 Y104.152 E0.690\nG1 X36.83 Y103.431 E0.690\nG1 X38.904 Y102.668 E0.691\nG1 X40.961 Y101.864 E0.690\nG1 X43.003 Y101.019 E0.691\nG1 X45.026 Y100.132 E0.690\nG1 X47.032 Y99.205 E0.691\nG1 X49.018 Y98.238 E0.690\nG1 X50.985 Y97.231 E0.691\nG1 X52.931 Y96.185 E0.690\nG1 X54.672 Y95.203 E0.625\nG92 E0.0\nG1 E-5 F3000 ; retract 5mm\nG1 X52.931 Y96.185 F1000 ; wipe\nG1 X50.985 Y97.231 F1000 ; wipe\nG1 X49.018 Y98.238 F1000 ; wipe\nG1 X0 Y109.798 F1000\nG1 E4.8 F1500; de-retract\nG92 E0.0 ; reset extrusion distance\nM221 S{if layer_height<0.075}100{else}95{endif}
@ -443,7 +443,7 @@ start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104
inherits = *common_akossel* inherits = *common_akossel*
printer_model = AK printer_model = AK
printer_variant = 0.4 printer_variant = 0.4
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_Anycubic\nPRINTER_MODEL_AK\nPRINTER_HAS_BOWDEN\n printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_Anycubic\nPRINTER_MODEL_AK\nPRINTER_HAS_BOWDEN\n
bed_shape = 89.6575x7.84402,88.6327x15.6283,86.9333x23.2937,84.5723x30.7818,81.5677x38.0356,77.9423x45,73.7237x51.6219,68.944x57.8509,63.6396x63.6396,57.8509x68.944,51.6219x73.7237,45x77.9423,38.0356x81.5677,30.7818x84.5723,23.2937x86.9333,15.6283x88.6327,7.84402x89.6575,5.51091e-15x90,-7.84402x89.6575,-15.6283x88.6327,-23.2937x86.9333,-30.7818x84.5723,-38.0356x81.5677,-45x77.9423,-51.6219x73.7237,-57.8509x68.944,-63.6396x63.6396,-68.944x57.8509,-73.7237x51.6219,-77.9423x45,-81.5677x38.0356,-84.5723x30.7818,-86.9333x23.2937,-88.6327x15.6283,-89.6575x7.84402,-90x1.10218e-14,-89.6575x-7.84402,-88.6327x-15.6283,-86.9333x-23.2937,-84.5723x-30.7818,-81.5677x-38.0356,-77.9423x-45,-73.7237x-51.6219,-68.944x-57.8509,-63.6396x-63.6396,-57.8509x-68.944,-51.6219x-73.7237,-45x-77.9423,-38.0356x-81.5677,-30.7818x-84.5723,-23.2937x-86.9333,-15.6283x-88.6327,-7.84402x-89.6575,-1.65327e-14x-90,7.84402x-89.6575,15.6283x-88.6327,23.2937x-86.9333,30.7818x-84.5723,38.0356x-81.5677,45x-77.9423,51.6219x-73.7237,57.8509x-68.944,63.6396x-63.6396,68.944x-57.8509,73.7237x-51.6219,77.9423x-45,81.5677x-38.0356,84.5723x-30.7818,86.9333x-23.2937,88.6327x-15.6283,89.6575x-7.84402,90x-2.20436e-14 bed_shape = 89.6575x7.84402,88.6327x15.6283,86.9333x23.2937,84.5723x30.7818,81.5677x38.0356,77.9423x45,73.7237x51.6219,68.944x57.8509,63.6396x63.6396,57.8509x68.944,51.6219x73.7237,45x77.9423,38.0356x81.5677,30.7818x84.5723,23.2937x86.9333,15.6283x88.6327,7.84402x89.6575,5.51091e-15x90,-7.84402x89.6575,-15.6283x88.6327,-23.2937x86.9333,-30.7818x84.5723,-38.0356x81.5677,-45x77.9423,-51.6219x73.7237,-57.8509x68.944,-63.6396x63.6396,-68.944x57.8509,-73.7237x51.6219,-77.9423x45,-81.5677x38.0356,-84.5723x30.7818,-86.9333x23.2937,-88.6327x15.6283,-89.6575x7.84402,-90x1.10218e-14,-89.6575x-7.84402,-88.6327x-15.6283,-86.9333x-23.2937,-84.5723x-30.7818,-81.5677x-38.0356,-77.9423x-45,-73.7237x-51.6219,-68.944x-57.8509,-63.6396x-63.6396,-57.8509x-68.944,-51.6219x-73.7237,-45x-77.9423,-38.0356x-81.5677,-30.7818x-84.5723,-23.2937x-86.9333,-15.6283x-88.6327,-7.84402x-89.6575,-1.65327e-14x-90,7.84402x-89.6575,15.6283x-88.6327,23.2937x-86.9333,30.7818x-84.5723,38.0356x-81.5677,45x-77.9423,51.6219x-73.7237,57.8509x-68.944,63.6396x-63.6396,68.944x-57.8509,73.7237x-51.6219,77.9423x-45,81.5677x-38.0356,84.5723x-30.7818,86.9333x-23.2937,88.6327x-15.6283,89.6575x-7.84402,90x-2.20436e-14
start_gcode = G90 ; use absolute coordinates\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 ; home\nG1 X-39.672 Y69.712 Z0.3 F9000\nG92 E0.0\nG1 F1000\nG1 X-38.457 Y70.397 E0.209\nG1 X-37.043 Y71.157 E0.241\nG1 X-35.614 Y71.889 E0.241\nG1 X-34.171 Y72.591 E0.241\nG1 X-32.714 Y73.265 E0.241\nG1 X-31.244 Y73.909 E0.241\nG1 X-29.761 Y74.523 E0.241\nG1 X-28.266 Y75.108 E0.241\nG1 X-26.759 Y75.662 E0.241\nG1 X-25.242 Y76.185 E0.241\nG1 X-23.714 Y76.678 E0.241\nG1 X-22.177 Y77.14 E0.241\nG1 X-20.63 Y77.571 E0.241\nG1 X-19.076 Y77.971 E0.241\nG1 X-17.514 Y78.34 E0.241\nG1 X-15.944 Y78.677 E0.241\nG1 X-14.368 Y78.982 E0.241\nG1 X-12.786 Y79.255 E0.241\nG1 X-11.199 Y79.497 E0.241\nG1 X-9.608 Y79.706 E0.241\nG1 X-8.013 Y79.884 E0.241\nG1 X-6.414 Y80.029 E0.241\nG1 X-4.813 Y80.142 E0.241\nG1 X-3.21 Y80.223 E0.241\nG1 X-1.605 Y80.271 E0.241\nG1 X0 Y80.287 E0.241\nG1 X1.605 Y80.271 E0.502\nG1 X3.21 Y80.223 E0.502\nG1 X4.813 Y80.142 E0.502\nG1 X6.414 Y80.029 E0.502\nG1 X8.013 Y79.884 E0.502\nG1 X9.608 Y79.706 E0.502\nG1 X11.199 Y79.497 E0.501\nG1 X12.786 Y79.255 E0.502\nG1 X14.368 Y78.982 E0.502\nG1 X15.944 Y78.677 E0.502\nG1 X17.514 Y78.34 E0.502\nG1 X19.076 Y77.971 E0.502\nG1 X20.63 Y77.571 E0.501\nG1 X22.177 Y77.14 E0.502\nG1 X23.714 Y76.678 E0.502\nG1 X25.242 Y76.185 E0.502\nG1 X26.759 Y75.662 E0.501\nG1 X28.266 Y75.108 E0.502\nG1 X29.761 Y74.523 E0.502\nG1 X31.244 Y73.909 E0.502\nG1 X32.714 Y73.265 E0.502\nG1 X34.171 Y72.591 E0.502\nG1 X35.614 Y71.889 E0.501\nG1 X37.043 Y71.157 E0.502\nG1 X38.457 Y70.397 E0.502\nG1 X39.672 Y69.712 E0.436\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} start_gcode = G90 ; use absolute coordinates\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 ; home\nG1 X-39.672 Y69.712 Z0.3 F9000\nG92 E0.0\nG1 F1000\nG1 X-38.457 Y70.397 E0.209\nG1 X-37.043 Y71.157 E0.241\nG1 X-35.614 Y71.889 E0.241\nG1 X-34.171 Y72.591 E0.241\nG1 X-32.714 Y73.265 E0.241\nG1 X-31.244 Y73.909 E0.241\nG1 X-29.761 Y74.523 E0.241\nG1 X-28.266 Y75.108 E0.241\nG1 X-26.759 Y75.662 E0.241\nG1 X-25.242 Y76.185 E0.241\nG1 X-23.714 Y76.678 E0.241\nG1 X-22.177 Y77.14 E0.241\nG1 X-20.63 Y77.571 E0.241\nG1 X-19.076 Y77.971 E0.241\nG1 X-17.514 Y78.34 E0.241\nG1 X-15.944 Y78.677 E0.241\nG1 X-14.368 Y78.982 E0.241\nG1 X-12.786 Y79.255 E0.241\nG1 X-11.199 Y79.497 E0.241\nG1 X-9.608 Y79.706 E0.241\nG1 X-8.013 Y79.884 E0.241\nG1 X-6.414 Y80.029 E0.241\nG1 X-4.813 Y80.142 E0.241\nG1 X-3.21 Y80.223 E0.241\nG1 X-1.605 Y80.271 E0.241\nG1 X0 Y80.287 E0.241\nG1 X1.605 Y80.271 E0.502\nG1 X3.21 Y80.223 E0.502\nG1 X4.813 Y80.142 E0.502\nG1 X6.414 Y80.029 E0.502\nG1 X8.013 Y79.884 E0.502\nG1 X9.608 Y79.706 E0.502\nG1 X11.199 Y79.497 E0.501\nG1 X12.786 Y79.255 E0.502\nG1 X14.368 Y78.982 E0.502\nG1 X15.944 Y78.677 E0.502\nG1 X17.514 Y78.34 E0.502\nG1 X19.076 Y77.971 E0.502\nG1 X20.63 Y77.571 E0.501\nG1 X22.177 Y77.14 E0.502\nG1 X23.714 Y76.678 E0.502\nG1 X25.242 Y76.185 E0.502\nG1 X26.759 Y75.662 E0.501\nG1 X28.266 Y75.108 E0.502\nG1 X29.761 Y74.523 E0.502\nG1 X31.244 Y73.909 E0.502\nG1 X32.714 Y73.265 E0.502\nG1 X34.171 Y72.591 E0.502\nG1 X35.614 Y71.889 E0.501\nG1 X37.043 Y71.157 E0.502\nG1 X38.457 Y70.397 E0.502\nG1 X39.672 Y69.712 E0.436\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
@ -784,7 +784,7 @@ printer_model = MEGA0
printer_variant = 0.4 printer_variant = 0.4
max_layer_height = 0.3 max_layer_height = 0.3
min_layer_height = 0.1 min_layer_height = 0.1
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_MEGA0 printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_MEGA0
bed_shape = 0x0,220x0,220x220,0x220 bed_shape = 0x0,220x0,220x220,0x220
max_print_height = 250 max_print_height = 250
machine_max_acceleration_e = 5000 machine_max_acceleration_e = 5000
@ -822,54 +822,61 @@ end_gcode = M117 Cooling down...\nM104 S0 ; turn off extruder\nM107 ; Fan off\nM
[print:*common_mega*] [print:*common_mega*]
bottom_solid_min_thickness = 0.5 bottom_solid_min_thickness = 0.5
bridge_acceleration = 1800 bridge_acceleration = 1000
bridge_flow_ratio = 0.8 bridge_flow_ratio = 0.95
bridge_speed = 30 bridge_speed = 30
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ANYCUBIC.*/ and printer_notes=~/.*PRINTER_MODEL_I3_MEGA.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ANYCUBIC.*/ and printer_notes=~/.*PRINTER_MODEL_I3_MEGA.*/ and nozzle_diameter[0]==0.4
default_acceleration = 1800 default_acceleration = 1000
ensure_vertical_shell_thickness = 1 ensure_vertical_shell_thickness = 1
external_perimeter_extrusion_width = 0.6 external_perimeter_extrusion_width = 0.45
external_perimeter_speed = 40 external_perimeter_speed = 25
extruder_clearance_height = 35 extruder_clearance_height = 35
extruder_clearance_radius = 60 extruder_clearance_radius = 60
extrusion_width = 0.45 extrusion_width = 0.45
fill_density = 15% fill_density = 15%
fill_pattern = gyroid fill_pattern = gyroid
first_layer_acceleration = 1800 first_layer_acceleration = 1000
first_layer_extrusion_width = 0.42 first_layer_extrusion_width = 0.42
first_layer_height = 0.2 first_layer_height = 0.2
first_layer_speed = 20
gap_fill_speed = 40 gap_fill_speed = 40
gcode_comments = 1 gcode_comments = 1
infill_acceleration = 1800 infill_acceleration = 1000
infill_anchor = 2.5
infill_anchor_max = 12
infill_extrusion_width = 0.45 infill_extrusion_width = 0.45
infill_speed = 60 max_print_speed = 200
min_skirt_length = 4
only_retract_when_crossing_perimeters = 0 only_retract_when_crossing_perimeters = 0
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
perimeter_acceleration = 1800 perimeter_acceleration = 800
perimeter_extrusion_width = 0.45 perimeter_extrusion_width = 0.45
perimeter_speed = 45
perimeters = 2 perimeters = 2
seam_position = nearest seam_position = nearest
skirts = 0 skirt_distance = 2
slice_closing_radius = 0.05 skirt_height = 3
small_perimeter_speed = 30 skirts = 1
small_perimeter_speed = 25
solid_infill_below_area = 0 solid_infill_below_area = 0
solid_infill_speed = 60 solid_infill_extrusion_width = 0.45
solid_infill_speed = 80
support_material_buildplate_only = 1 support_material_buildplate_only = 1
support_material_contact_distance = 0.1 support_material_contact_distance = 0.1
support_material_extrusion_width = 0.35 support_material_extrusion_width = 0.35
support_material_interface_layers = 2 support_material_interface_layers = 2
support_material_interface_spacing = 0.2 support_material_interface_spacing = 0.2
support_material_spacing = 2 support_material_spacing = 2
support_material_speed = 50
support_material_threshold = 55 support_material_threshold = 55
support_material_with_sheath = 0
thin_walls = 0 thin_walls = 0
top_infill_extrusion_width = 0.4 top_infill_extrusion_width = 0.4
top_solid_infill_speed = 40 top_solid_infill_speed = 40
top_solid_layers = 5
top_solid_min_thickness = 0.6 top_solid_min_thickness = 0.6
travel_speed = 180 travel_speed = 180
[print:*supported_mega*] [print:*supported_mega*]
raft_layers = 2
support_material = 1 support_material = 1
# XXXXXXXXXXXXXXXXXXXX # XXXXXXXXXXXXXXXXXXXX
@ -911,7 +918,19 @@ inherits = *0.20mm_mega*;*supported_mega*
[print:*0.30mm_mega*] [print:*0.30mm_mega*]
inherits = *common_mega* inherits = *common_mega*
bottom_solid_layers = 4 bottom_solid_layers = 4
bridge_flow_ratio = 0.95 external_perimeter_extrusion_width = 0.6
external_perimeter_speed = 35
extrusion_width = 0.5
fill_pattern = grid
infill_extrusion_width = 0.5
infill_speed = 85
layer_height = 0.3
perimeter_extrusion_width = 0.5
perimeter_speed = 50
small_perimeter_speed = 30
solid_infill_extrusion_width = 0.5
support_material_extrusion_width = 0.38
support_material_speed = 45
top_solid_layers = 4 top_solid_layers = 4
[print:0.30mm DRAFT @MEGA] [print:0.30mm DRAFT @MEGA]
@ -929,7 +948,6 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ANYCUBIC.*/ and
end_filament_gcode = "; Filament-specific end gcode" end_filament_gcode = "; Filament-specific end gcode"
fan_always_on = 1 fan_always_on = 1
fan_below_layer_time = 100 fan_below_layer_time = 100
filament_colour = #FF8000
filament_vendor = Generic filament_vendor = Generic
min_print_speed = 15 min_print_speed = 15
slowdown_below_layer_time = 20 slowdown_below_layer_time = 20
@ -941,13 +959,13 @@ slowdown_below_layer_time = 20
cooling = 0 cooling = 0
fan_always_on = 0 fan_always_on = 0
fan_below_layer_time = 20 fan_below_layer_time = 20
filament_colour = #FFF2EC filament_colour = #3A80CA
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.04 filament_density = 1.04
filament_max_volumetric_speed = 11 filament_max_volumetric_speed = 11
filament_ramming_parameters = "120 100 5.70968 6.03226 7 8.25806 9 9.19355 9.3871 9.77419 10.129 10.3226 10.4516 10.5161| 0.05 5.69677 0.45 6.15484 0.95 8.76774 1.45 9.20323 1.95 9.95806 2.45 10.3871 2.95 10.5677 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" filament_ramming_parameters = "120 100 5.70968 6.03226 7 8.25806 9 9.19355 9.3871 9.77419 10.129 10.3226 10.4516 10.5161| 0.05 5.69677 0.45 6.15484 0.95 8.76774 1.45 9.20323 1.95 9.95806 2.45 10.3871 2.95 10.5677 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_type = ABS filament_type = ABS
first_layer_bed_temperature = 100 first_layer_bed_temperature = 105
first_layer_temperature = 255 first_layer_temperature = 255
max_fan_speed = 30 max_fan_speed = 30
min_fan_speed = 20 min_fan_speed = 20
@ -955,14 +973,14 @@ slowdown_below_layer_time = 20
[filament:Generic ABS @MEGA] [filament:Generic ABS @MEGA]
inherits = *ABS_mega* inherits = *ABS_mega*
[filament:*FLEX_mega*] [filament:*FLEX_mega*]
inherits = *common_mega* inherits = *common_mega*
bed_temperature = 50 bed_temperature = 50
bridge_fan_speed = 80 bridge_fan_speed = 80
cooling = 0 cooling = 0
extrusion_multiplier = 1.15 extrusion_multiplier = 1.15
fan_always_on = 0 fan_always_on = 0
filament_colour = #008000 filament_colour = #008000
filament_cost = 82.00 filament_cost = 82.00
filament_density = 1.22 filament_density = 1.22
@ -970,7 +988,7 @@ filament_deretract_speed = 25
filament_max_volumetric_speed = 1.2 filament_max_volumetric_speed = 1.2
filament_retract_length = 0.8 filament_retract_length = 0.8
filament_type = FLEX filament_type = FLEX
first_layer_bed_temperature = 50 first_layer_bed_temperature = 55
first_layer_temperature = 240 first_layer_temperature = 240
max_fan_speed = 90 max_fan_speed = 90
min_fan_speed = 70 min_fan_speed = 70
@ -979,16 +997,41 @@ temperature = 240
[filament:Generic FLEX @MEGA] [filament:Generic FLEX @MEGA]
inherits = *FLEX_mega* inherits = *FLEX_mega*
[filament:SainSmart TPU @MEGA]
inherits = *FLEX_mega*
filament_vendor = SainSmart
bed_temperature = 50
bridge_fan_speed = 100
cooling = 1
disable_fan_first_layers = 4
filament_cost = 39.99
filament_density = 1.21
filament_deretract_speed = 15
filament_max_volumetric_speed = 1.8
filament_notes = "SainSmart TPU gains popularity among 3D Printing community for its balance of rigidity and flexibility. In addition, with a 95A Shore Hardness and improved bed adhesion, it is easier to print even with a stock elementary 3D Printer like the Creality Ender 3. SainSmart TPU will not disappoint if you are looking for flexible filament. From drone parts, phone cases, to small toys, all can be printed with ease.\n\nhttps://www.sainsmart.com/collections/tpu-filament/products/all-colors-tpu-flexible-filament-1-75mm-0-8kg-1-76lb"
filament_retract_before_travel = 5
filament_retract_length = 4
filament_retract_speed = 40
filament_unloading_speed = 90
first_layer_bed_temperature = 55
first_layer_temperature = 235
full_fan_speed_layer = 6
max_fan_speed = 80
min_fan_speed = 80
slowdown_below_layer_time = 10
temperature = 235
[filament:*PETG_mega*] [filament:*PETG_mega*]
inherits = *common_mega* inherits = *common_mega*
bed_temperature = 90 bed_temperature = 90
bridge_fan_speed = 50 bridge_fan_speed = 50
fan_below_layer_time = 20 fan_below_layer_time = 20
filament_colour = #FF8000
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.27 filament_density = 1.27
filament_max_volumetric_speed = 8 filament_max_volumetric_speed = 8
filament_type = PETG filament_type = PETG
first_layer_bed_temperature = 85 first_layer_bed_temperature = 90
first_layer_temperature = 230 first_layer_temperature = 230
max_fan_speed = 50 max_fan_speed = 50
min_fan_speed = 30 min_fan_speed = 30
@ -997,14 +1040,63 @@ temperature = 240
[filament:Generic PETG @MEGA] [filament:Generic PETG @MEGA]
inherits = *PETG_mega* inherits = *PETG_mega*
[filament:ColorFabb XT-CF20 @MEGA]
inherits = *PETG_mega*
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_notes=~/.*PRINTER_VENDOR_ANYCUBIC.*/ and printer_notes=~/.*PRINTER_MODEL_I3_MEGA.*/
extrusion_multiplier = 1.05
filament_colour = #804040
filament_cost = 66.60
filament_density = 1.35
filament_deretract_speed = 25
filament_max_volumetric_speed = 2
filament_notes = "Based on colorFabb_XT, XT-CF20 is a carbon fiber composite material. Loaded with no less than 20% specially sourced carbon fibers we have developed a very stiff and tough 3D printing filament made for functional parts. It is truly a professional printers go-to material, especially for users looking for high melt strength, high melt viscosity and good dimensional accuracy and stability.\n\nhttps://colorfabb.com/xt-cf20"
filament_retract_before_travel = 1
filament_retract_length = 1.4
filament_retract_speed = 40
filament_spool_weight = 236
filament_vendor = ColorFabb
first_layer_temperature = 260
full_fan_speed_layer = 5
slowdown_below_layer_time = 15
temperature = 260
[filament:ERYONE PETG @MEGA]
inherits = *PETG_mega*
filament_vendor = ERYONE
filament_cost = 20.99
filament_notes = "https://eryone.com/petg/show/10.html"
[filament:FormFutura HDglass @MEGA]
inherits = *PETG_mega*
filament_vendor = FormFutura
filament_cost = 46.65
filament_notes = "HDglass is a high performance PETG type of 3D printer with unsurpassed 3D printing properties and improved mechanical strength, flexibility, toughness and heat resistance.\n\nhttps://www.formfutura.com/shop/product/hdglass-2812"
[filament:FormFutura ReForm rPET @MEGA]
inherits = *PETG_mega*
filament_vendor = FormFutura
filament_cost = 26.65
filament_notes = "ReForm rPET is a recycled PETG type of 3D printer filament that is made from post-industrial waste streams of a nearby located plastic bottle manufacturer.\n\nhttps://www.formfutura.com/shop/product/reform-rpet-2836"
filament_spool_weight = 176
[filament:Janbex transparent PETG @MEGA]
inherits = *PETG_mega*
filament_vendor = Janbex
filament_cost = 31.99
filament_spool_weight = 222
first_layer_temperature = 215
min_fan_speed = 100
temperature = 210
[filament:*PLA_mega*] [filament:*PLA_mega*]
inherits = *common_mega* inherits = *common_mega*
bed_temperature = 60 bed_temperature = 60
disable_fan_first_layers = 1 disable_fan_first_layers = 1
filament_colour = #FF3232
filament_cost = 25.40 filament_cost = 25.40
filament_density = 1.24 filament_density = 1.24
filament_max_volumetric_speed = 10 filament_max_volumetric_speed = 10
first_layer_bed_temperature = 60 first_layer_bed_temperature = 65
first_layer_temperature = 215 first_layer_temperature = 215
min_fan_speed = 100 min_fan_speed = 100
temperature = 210 temperature = 210
@ -1012,73 +1104,97 @@ temperature = 210
[filament:Generic PLA @MEGA] [filament:Generic PLA @MEGA]
inherits = *PLA_mega* inherits = *PLA_mega*
[filament:*3Dmensionals PLA_mega*] [filament:3Dmensionals PLA @MEGA]
inherits = *PLA_mega* inherits = *PLA_mega*
filament_vendor = 3Dmensionals filament_vendor = 3Dmensionals
filament_cost = 23.35 filament_cost = 22.90
filament_notes = "Das 3DFilaments - PLA von 3Dmensionals ist ein sehr leicht zu druckendes 3D-Drucker Filament. Dabei handelt es sich um ein etwas härteres PLA mit einer exzellenten thermischen Stabilität. Das Filament zeichnet sich vor allem durch verzugfreies 3D-Drucken aus und weist minimale bis keine Verformung nach dem Abkühlen auf. Daher ist es besonders gut für den Druck größerer Objekte geeignet. Zudem bietet 3DFilaments - PLA über die gesamte Fadenläge eine hervorragende Durchmesser- und Rundheitstoleranz.\n\nhttps://www.3dmensionals.de/3dfilaments?number=PSU3DM001V"
[filament:3Dmensionals PLA @MEGA] [filament:3D Warhorse PLA @MEGA]
inherits = *3Dmensionals PLA_mega* inherits = *PLA_mega*
filament_vendor = 3D Warhorse
filament_cost = 19.99
[filament:3Dmensionals PLA blue @MEGA] [filament:AMOLEN wood PLA]
inherits = *3Dmensionals PLA_mega* inherits = *PLA_mega*
filament_colour = #4155FB filament_vendor = AMOLEN
compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_notes=~/.*PRINTER_VENDOR_ANYCUBIC.*/ and printer_notes=~/.*PRINTER_MODEL_I3_MEGA.*/
extrusion_multiplier = 1.1
filament_colour = #DFC287
filament_cost = 33.99
filament_density = 1.23
filament_max_volumetric_speed = 9
filament_notes = "https://amolen.com/collections/wood/products/amolen-pla-filament-1-75mm-wood-color-3d-printer-filament-1kg2-2lb"
[filament:3Dmensionals PLA silver @MEGA] [filament:FormFutura EasyFil PLA @MEGA]
inherits = *3Dmensionals PLA_mega* inherits = *PLA_mega*
filament_colour = #B9B5B4 filament_vendor = FormFutura
filament_cost = 39.93
filament_notes = "EasyFil PLA is an easy to print PLA type of 3D printer filament that is available in a wide variety of colors. Its improved flowing behavior make 3D printed layers flow more into each other.\n\nhttps://www.formfutura.com/shop/product/easyfil-pla-2801"
[filament:3Dmensionals PLA white @MEGA] [filament:FormFutura ReForm rPLA @MEGA]
inherits = *3Dmensionals PLA_mega* inherits = *PLA_mega*
filament_colour = #FEFEFD filament_vendor = FormFutura
filament_cost = 26.65
filament_notes = "ReForm is a sustainable initiative within Formfutura to efficiently manage residual extrusion waste streams and re-use them into high-end upcycled filaments. The ideology behind ReForm is to a make 3D printing more sustainable without having to make compromises on material properties and yet keep it affordable.\n\nhttps://www.formfutura.com/shop/product/reform-rpla-2838"
[filament:*Verbatim PLA_mega*] [filament:GIANTARM PLA @MEGA]
inherits = *PLA_mega*
filament_vendor = GIANTARM
filament_cost = 24.99
[filament:Prusament PLA @MEGA]
inherits = *PLA_mega*
filament_vendor = Prusa Polymers
filament_cost = 30.24
filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa"
filament_spool_weight = 201
temperature = 215
[filament:Verbatim PLA @MEGA]
inherits = *PLA_mega* inherits = *PLA_mega*
filament_vendor = Verbatim filament_vendor = Verbatim
filament_cost = 23.88 filament_cost = 23.88
[filament:Verbatim PLA @MEGA]
inherits = *Verbatim PLA_mega*
[filament:Verbatim PLA black @MEGA]
inherits = *Verbatim PLA_mega*
filament_colour = #333333
[printer:*common_mega*] [printer:*common_mega*]
printer_technology = FFF printer_technology = FFF
bed_shape = 0x0,210x0,210x210,0x210 bed_shape = 0x0,210x0,210x210,0x210
before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z] before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]
default_filament_profile = Generic PLA @MEGA default_filament_profile = Generic PLA @MEGA
default_print_profile = 0.15mm QUALITY @MEGA default_print_profile = 0.15mm QUALITY @MEGA
deretract_speed = 50 deretract_speed = 40
end_gcode = G4 ; wait\nG92 E0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors end_gcode = G1 E-1.0 F2100 ; retract\nG92 E0.0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} E-34.0 F720 ; move print head up & retract filament\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y105 F3000 ; park print head\nM84 ; disable motors
extruder_colour = #808080 extruder_colour = #808080
gcode_flavor = marlin gcode_flavor = marlin
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
max_layer_height = 0.36 max_layer_height = 0.36
max_print_height = 205 max_print_height = 205
remaining_times = 1
retract_before_travel = 1.5
retract_before_wipe = 60% retract_before_wipe = 60%
retract_layer_change = 1 retract_layer_change = 1
retract_length = 6 retract_length = 3.2
retract_lift = 0.075 retract_lift = 0.2
retract_lift_below = 204 retract_lift_below = 204
retract_speed = 70
silent_mode = 0 silent_mode = 0
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y0 Z1 F100 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0\nG1 Z0.2 F360\nG1 X60 E9 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0 start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y1.0 Z0.3 F1000 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0.0\n; initial load\nG1 X205.0 E19 F1000\nG1 Y1.6\nG1 X5.0 E19 F1000\nG92 E0.0\n; intro line\nG1 Y2.0 Z0.2 F1000\nG1 X65.0 E9.0 F1000\nG1 X105.0 E12.5 F1000\nG92 E0.0
thumbnails = 16x16,220x124
use_relative_e_distances = 1 use_relative_e_distances = 1
wipe = 1 wipe = 1
machine_max_acceleration_e = 5000 machine_max_acceleration_e = 10000
machine_max_acceleration_extruding = 1250 machine_max_acceleration_extruding = 1250
machine_max_acceleration_retracting = 1250 machine_max_acceleration_retracting = 1250
machine_max_acceleration_x = 1000 machine_max_acceleration_x = 3000
machine_max_acceleration_y = 1000 machine_max_acceleration_y = 2000
machine_max_acceleration_z = 200 machine_max_acceleration_z = 60
machine_max_feedrate_e = 60 machine_max_feedrate_e = 60
machine_max_feedrate_x = 200 machine_max_feedrate_x = 500
machine_max_feedrate_y = 200 machine_max_feedrate_y = 500
machine_max_feedrate_z = 6 machine_max_feedrate_z = 6
machine_max_jerk_e = 5 machine_max_jerk_e = 5
machine_max_jerk_x = 8 machine_max_jerk_x = 10
machine_max_jerk_y = 8 machine_max_jerk_y = 10
machine_max_jerk_z = 0.4 machine_max_jerk_z = 0.4
[printer:Anycubic i3 Mega] [printer:Anycubic i3 Mega]
@ -1092,6 +1208,7 @@ inherits = *common_mega*
printer_model = I3MEGAS printer_model = I3MEGAS
printer_variant = 0.4 printer_variant = 0.4
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_I3_MEGA_S\nPRINTER_HAS_BOWDEN printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_I3_MEGA_S\nPRINTER_HAS_BOWDEN
machine_max_feedrate_e = 30
machine_max_feedrate_z = 8 machine_max_feedrate_z = 8
@ -1743,7 +1860,7 @@ machine_max_jerk_z = 5
machine_min_extruding_rate = 0 machine_min_extruding_rate = 0
machine_min_travel_rate = 0 machine_min_travel_rate = 0
printer_settings_id = printer_settings_id =
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PREDATOR\nPRINTER_HAS_BOWDEN\n printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PREDATOR\nPRINTER_HAS_BOWDEN\n
default_filament_profile = Generic PLA @PREDATOR default_filament_profile = Generic PLA @PREDATOR
[printer:Anycubic Predator 0.4 nozzle] [printer:Anycubic Predator 0.4 nozzle]

View file

@ -0,0 +1,561 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210.5mm"
height="210.5mm"
viewBox="0 0 210.5 210.5"
version="1.1"
id="svg886"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="ai3m.svg">
<defs
id="defs880" />
<sodipodi:namedview
id="base"
pagecolor="#7670ff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="466.73093"
inkscape:cy="380.93745"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:lockguides="false"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata883">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 2"
transform="translate(0,-86.5)"
style="display:inline"
sodipodi:insensitive="true">
<path
id="path869"
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.04216461;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
d="m 167.32704,288.75984 v -3.83255 h 1.45715 v 7.07119 l -6.06232,-5.36476 v -1.94915 z m -9.42165,-2.52278 1.54352,2.96287 h -0.60107 l -0.76147,1.4617 h 2.12401 l 0.6004,1.1525 h 1.65752 l -3.73414,-7.16792 z m 44.62362,4.25408 v 1.50745 c 0.98166,-0.11796 1.8734,-0.62934 2.47099,-1.41702 l -1.14996,-0.94952 c -0.31636,0.44259 -0.78818,0.74942 -1.32103,0.85909 z m -40.17486,-27.16603 11.53897,-20.77294 18.70747,18.05526 -17.35981,21.92818 z m -5.69662,28.48902 1.6624,-3.19108 -0.82877,-1.59087 L 155,291.81413 Z m 22.56867,-7.05936 c 0.97028,0.11659 1.8532,0.61761 2.4509,1.39077 l -1.16155,0.93258 c -0.31614,-0.41985 -0.77457,-0.70996 -1.28935,-0.81591 z m 3.41542,0.17252 h 1.46761 v 4.19716 c 0,0.59031 0.3725,1.11637 0.92932,1.3124 v 1.50898 c -1.38222,-0.22635 -2.39693,-1.42074 -2.39693,-2.82138 z m 8.44048,6.88684 v -6.88684 h -1.43359 v 6.88684 z m 1.73019,-4.18512 c 0.35459,0 0.64204,-0.28746 0.64204,-0.64204 0,-0.35459 -0.28745,-0.64204 -0.64204,-0.64204 h -0.8869 v -1.41764 h 0.8869 c 1.13753,0 2.05968,0.92215 2.05968,2.05968 0,1.13753 -0.92215,2.05968 -2.05968,2.05968 h -0.8869 l -0.004,-1.41764 z m -30.09042,4.18512 h 1.49929 v -2.7357 l -1.49929,-1.32677 z m 33.23702,0 h 1.43359 v -6.88684 h -1.43359 z m -6.61958,-43.20436 -15.24232,-6.09042 18.63476,17.98509 11.51531,1.11097 z m -18.15413,43.20436 v -3.62964 l 2.39506,-3.2572 h 1.87519 l -2.78508,3.78762 v 3.09922 z m 0.39556,-5.55407 -0.92997,1.26472 -1.81565,-2.59749 h 1.814 z m 21.17859,-25.58701 11.46398,1.10603 -13.7034,15.36187 -15.13578,5.47973 z m 7.21757,28.12689 c 0.17089,0.85467 0.83896,1.52274 1.69364,1.69363 v 1.50644 c -1.6794,-0.19619 -3.0039,-1.52068 -3.20008,-3.20007 -0.23374,-2.00112 1.19897,-3.81283 3.20008,-4.04658 v 1.50643 c -1.16914,0.23375 -1.92739,1.37102 -1.69364,2.54015 z m -5.04526,0.93674 c 0,-0.30809 -0.0685,-0.61233 -0.20062,-0.89067 -0.35104,0.36232 -0.79749,0.61783 -1.28769,0.73699 0.0131,0.0502 0.0197,0.10183 0.0197,0.15368 0,0.33625 -0.2726,0.60885 -0.60885,0.60885 h -0.92882 v 1.4686 l 0.92882,-10e-6 c 1.14733,0 2.07744,-0.9301 2.07744,-2.07744 z m -14.38439,-0.10463 1.14995,0.94952 c -0.5976,0.78768 -1.48934,1.29906 -2.471,1.41702 v -1.50745 c 0.53286,-0.10967 1.00468,-0.4165 1.32105,-0.85909 z m -2.17951,0.86152 c -0.85468,-0.17089 -1.52275,-0.83896 -1.69364,-1.69363 -0.23376,-1.16913 0.52451,-2.3064 1.69364,-2.54015 v -1.50643 c -2.00111,0.23375 -3.43383,2.04546 -3.20007,4.04658 0.19617,1.67939 1.52067,3.00388 3.20007,3.20007 z m 9.99181,-1.36912 v -4.19716 h -1.46761 v 4.19716 c 0,0.62159 -0.41231,1.16778 -1.01011,1.3381 v 1.49533 c 1.41883,-0.19091 2.47772,-1.40182 2.47772,-2.83343 z m 14.16946,-2.86224 v -1.50744 c 0.97027,0.11659 1.85319,0.61761 2.45089,1.39077 l -1.16154,0.93258 c -0.31614,-0.41985 -0.77458,-0.70996 -1.28935,-0.81591 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccsccccscccccccccccccccccccccccccccccccccccccccccccsccsccccscccccccccccccccccccccccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 1"
transform="translate(0,-86.5)"
style="display:none">
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.68079633,77.180801 H 219.81921"
id="path1596"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,77.180801 V 296.31918"
id="path1594"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,296.31918 H 0.68079633"
id="path1592"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.68079633,296.31918 V 77.180801"
id="rect1544"
inkscape:connector-curvature="0" />
<path
id="path5301"
d="M 9.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 19.681,296.31918 V 77.180801"
id="path5303" />
<path
id="path5305"
d="M 29.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 39.681,296.31918 V 77.180801"
id="path5307" />
<path
id="path5309"
d="M 49.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 59.681,296.31918 V 77.180801"
id="path5311" />
<path
id="path5313"
d="M 69.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 79.681,296.31918 V 77.180801"
id="path5315" />
<path
id="path5317"
d="M 89.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 99.681,296.31918 V 77.180801"
id="path5319" />
<path
id="path5321"
d="M 109.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 119.681,296.31918 V 77.180801"
id="path5323" />
<path
id="path5325"
d="M 129.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 139.681,296.31918 V 77.180801"
id="path5327" />
<path
id="path5329"
d="M 149.681,296.31918 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 159.681,296.31918 V 77.180801"
id="path5331" />
<path
id="path5333"
d="m 169.681,296.31918 v -3.0895 m 0,-10.12031 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 179.681,296.31918 v -2.82492 m 0,-10.18645 v -6.87917 m 0,-28.50885 V 77.180801"
id="path5335"
sodipodi:nodetypes="cccccc" />
<path
id="path5337"
d="m 189.681,296.31918 v -2.95721 m 0,-50.37005 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 199.681,296.31918 v -3.15564 m 0,-9.72344 v -4.69636 m 0,-30.8901 V 77.180801"
id="path5339"
sodipodi:nodetypes="cccccc" />
<path
id="path5341"
d="m 209.681,296.31918 v -2.89106 m 0,-10.25261 v -14.61822 m 0,-12.76615 V 77.180801"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
id="path5343"
d="m 219.81921,287.319 h -3.39004 m -51.52761,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 219.81921,277.319 h -18.00827 m -21.36511,0 H 0.68079633"
id="path5345"
sodipodi:nodetypes="cccc" />
<path
id="path5347"
d="m 219.81921,267.319 h -8.94629 m -36.90938,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 219.81921,257.319 h -8.74786 m -36.90937,0 H 0.68079633"
id="path5349"
sodipodi:nodetypes="cccc" />
<path
id="path5351"
d="m 219.81921,247.319 h -21.44786 m -18.12395,0 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,237.319 H 0.68079633"
id="path5353" />
<path
id="path5355"
d="M 219.81921,227.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,217.319 H 0.68079633"
id="path5357" />
<path
id="path5359"
d="M 219.81921,207.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,197.319 H 0.68079633"
id="path5361" />
<path
id="path5363"
d="M 219.81921,187.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,177.319 H 0.68079633"
id="path5365" />
<path
id="path5367"
d="M 219.81921,167.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,157.319 H 0.68079633"
id="path5369" />
<path
id="path5371"
d="M 219.81921,147.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,137.319 H 0.68079633"
id="path5373" />
<path
id="path5375"
d="M 219.81921,127.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,117.319 H 0.68079633"
id="path5377" />
<path
id="path5379"
d="M 219.81921,107.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 219.81921,97.319 H 0.68079633"
id="path5381" />
<path
id="path5383"
d="M 219.81921,87.319 H 0.68079633"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.40000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 3"
style="display:inline">
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,205.25001 h 152.88208 m 53.78799,0 h 3.32996"
id="path973"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 145.25002,210.25 V 0.25000128"
id="path971"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 205.25001,210.25 v -2.79018 m 0,-10.89792 V 0.25000128"
id="path969"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,15.250006 H 210.25003"
id="path967"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,155.25001 H 210.25003"
id="path965"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 55.249995,210.25 V 0.25000128"
id="path963"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,175.25 h 160.97366 m 43.87229,0 h 5.15408"
id="path961"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 125.25003,210.25 V 0.25000128"
id="path959"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,35.250001 H 210.25003"
id="path957"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 195.25003,210.25 v -3.02405 m 0,-10.68744 0,-10.52373 m 0,-19.79114 0,-165.97363872"
id="path955"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 35.25,210.25 V 0.25000128"
id="path953"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.2500251,210.25 V 0.25000128"
id="path951"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,25.249986 H 210.25003"
id="path949"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,105.25 H 210.25003"
id="path947"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 105.25004,210.25 V 0.25000128"
id="path945"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,55.249996 H 210.25003"
id="path943"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 185.25001,210.25 v -3.02405 m 0,-10.73421 v -2.8531 m 0,-34.30738 V 0.25000128"
id="path941"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 175.25,210.25 v -3.11759 m 0,-51.39092 V 0.25000128"
id="path939"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 15.250005,210.25 V 0.25000128"
id="path937"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,125.25 H 210.25003"
id="path935"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 85.250005,210.25 V 0.25000128"
id="path933"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,75.249991 H 210.25003"
id="path931"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 155.25,210.25 v -3.02405 m 0,-7.39 V 0.25000128"
id="path929"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,0.25000128 V 210.25"
id="path927"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.40000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,5.2499912 H 210.25003"
id="path925"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,145.24999 H 210.25003"
id="path923"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 65.25001,210.25 V 0.25000128"
id="path921"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,95.250021 H 210.25003"
id="path919"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 165.25002,210.25 v -2.79018 m 0,-10.4302 v -13.18975 m 0,-14.82678 V 0.25000128"
id="path917"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 135.25001,210.25 V 0.25000128"
id="path915"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 210.25003,0.25000128 H 0.25"
id="path913"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,165.25002 h 166.9605 m 26.98754,0 h 16.05199"
id="path911"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 45.250015,210.25 V 0.25000128"
id="path909"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 115.25001,210.25 V 0.25000128"
id="path907"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,45.249981 H 210.25003"
id="path905"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 210.25003,210.25 V 0.25000128"
id="path903"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 25.25002,210.25 V 0.25000128"
id="path901"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,185.25002 h 166.25892 m 30.26159,0 h 13.47952"
id="path899"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,115.25002 H 210.25003"
id="path897"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 95.25002,210.25 V 0.25000128"
id="path895"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,65.250011 H 210.25003"
id="path893"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,210.25 H 210.25003"
id="path891"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,85.250006 H 210.25003"
id="path889"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.25,195.25 h 172.9694 m 6.46576,0 h 30.56487"
id="path887"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.25,135.25001 H 210.25003"
id="path885"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 75.250025,210.25 V 0.25000128"
id="path875"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

View file

@ -21,7 +21,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = ender3.svg bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER3BLTOUCH] [printer_model:ENDER3BLTOUCH]
name = Creality Ender-3 BLTouch name = Creality Ender-3 BLTouch
@ -30,7 +30,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = ender3.svg bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER3V2] [printer_model:ENDER3V2]
name = Creality Ender-3 V2 name = Creality Ender-3 V2
@ -39,7 +39,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender3v2_bed.stl bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER3MAX] [printer_model:ENDER3MAX]
name = Creality Ender-3 Max name = Creality Ender-3 Max
@ -48,7 +48,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = cr10v2_bed.stl bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg bed_texture = cr10spro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER4] [printer_model:ENDER4]
name = Creality Ender-4 name = Creality Ender-4
@ -57,7 +57,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender3v2_bed.stl bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER5] [printer_model:ENDER5]
name = Creality Ender-5 name = Creality Ender-5
@ -66,7 +66,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = ender3.svg bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER5PLUS] [printer_model:ENDER5PLUS]
name = Creality Ender-5 Plus name = Creality Ender-5 Plus
@ -75,7 +75,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender5plus_bed.stl bed_model = ender5plus_bed.stl
bed_texture = ender5plus.svg bed_texture = ender5plus.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER6] [printer_model:ENDER6]
name = Creality Ender-6 name = Creality Ender-6
@ -84,7 +84,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender6_bed.stl bed_model = ender6_bed.stl
bed_texture = ender6.svg bed_texture = ender6.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:ENDER2] [printer_model:ENDER2]
name = Creality Ender-2 name = Creality Ender-2
@ -93,7 +93,7 @@ technology = FFF
family = ENDER family = ENDER
bed_model = ender2_bed.stl bed_model = ender2_bed.stl
bed_texture = ender2.svg bed_texture = ender2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR5PRO] [printer_model:CR5PRO]
name = Creality CR-5 Pro name = Creality CR-5 Pro
@ -102,7 +102,7 @@ technology = FFF
family = CR family = CR
bed_model = cr5pro_bed.stl bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg bed_texture = cr5pro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR5PROH] [printer_model:CR5PROH]
name = Creality CR-5 Pro H name = Creality CR-5 Pro H
@ -111,7 +111,7 @@ technology = FFF
family = CR family = CR
bed_model = cr5pro_bed.stl bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg bed_texture = cr5pro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR6SE] [printer_model:CR6SE]
name = Creality CR-6 SE name = Creality CR-6 SE
@ -120,7 +120,7 @@ technology = FFF
family = CR family = CR
bed_model = cr6se_bed.stl bed_model = cr6se_bed.stl
bed_texture = cr6se.svg bed_texture = cr6se.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR6MAX] [printer_model:CR6MAX]
name = Creality CR-6 Max name = Creality CR-6 Max
@ -129,7 +129,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10s4_bed.stl bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg bed_texture = cr10s4.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10MINI] [printer_model:CR10MINI]
name = Creality CR-10 Mini name = Creality CR-10 Mini
@ -138,7 +138,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10mini_bed.stl bed_model = cr10mini_bed.stl
bed_texture = cr10mini.svg bed_texture = cr10mini.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10MAX] [printer_model:CR10MAX]
name = Creality CR-10 Max name = Creality CR-10 Max
@ -147,7 +147,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10max_bed.stl bed_model = cr10max_bed.stl
bed_texture = cr10max.svg bed_texture = cr10max.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10] [printer_model:CR10]
name = Creality CR-10 name = Creality CR-10
@ -156,7 +156,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10_bed.stl bed_model = cr10_bed.stl
bed_texture = cr10.svg bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10V2] [printer_model:CR10V2]
name = Creality CR-10 V2 name = Creality CR-10 V2
@ -165,7 +165,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10v2_bed.stl bed_model = cr10v2_bed.stl
bed_texture = cr10.svg bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10V3] [printer_model:CR10V3]
name = Creality CR-10 V3 name = Creality CR-10 V3
@ -174,7 +174,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10v2_bed.stl bed_model = cr10v2_bed.stl
bed_texture = cr10.svg bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10S] [printer_model:CR10S]
name = Creality CR-10 S name = Creality CR-10 S
@ -183,7 +183,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10_bed.stl bed_model = cr10_bed.stl
bed_texture = cr10.svg bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10SPRO] [printer_model:CR10SPRO]
name = Creality CR-10 S Pro name = Creality CR-10 S Pro
@ -192,7 +192,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10v2_bed.stl bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg bed_texture = cr10spro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10SPROV2] [printer_model:CR10SPROV2]
name = Creality CR-10 S Pro V2 name = Creality CR-10 S Pro V2
@ -201,7 +201,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10v2_bed.stl bed_model = cr10v2_bed.stl
bed_texture = cr10.svg bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10S4] [printer_model:CR10S4]
name = Creality CR-10 S4 name = Creality CR-10 S4
@ -210,7 +210,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10s4_bed.stl bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg bed_texture = cr10s4.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR10S5] [printer_model:CR10S5]
name = Creality CR-10 S5 name = Creality CR-10 S5
@ -219,7 +219,7 @@ technology = FFF
family = CR family = CR
bed_model = cr10s5_bed.stl bed_model = cr10s5_bed.stl
bed_texture = cr10s5.svg bed_texture = cr10s5.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR20] [printer_model:CR20]
name = Creality CR-20 name = Creality CR-20
@ -228,7 +228,7 @@ technology = FFF
family = CR family = CR
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = cr20.svg bed_texture = cr20.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR20PRO] [printer_model:CR20PRO]
name = Creality CR-20 Pro name = Creality CR-20 Pro
@ -237,7 +237,7 @@ technology = FFF
family = CR family = CR
bed_model = ender3_bed.stl bed_model = ender3_bed.stl
bed_texture = cr20.svg bed_texture = cr20.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR200B] [printer_model:CR200B]
name = Creality CR-200B name = Creality CR-200B
@ -246,7 +246,7 @@ technology = FFF
family = CR family = CR
bed_model = cr200b_bed.stl bed_model = cr200b_bed.stl
bed_texture = cr200b.svg bed_texture = cr200b.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
[printer_model:CR8] [printer_model:CR8]
name = Creality CR-8 name = Creality CR-8
@ -255,7 +255,7 @@ technology = FFF
family = CR family = CR
bed_model = cr8_bed.stl bed_model = cr8_bed.stl
bed_texture = cr8.svg bed_texture = cr8.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
#[printer_model:CRX] #[printer_model:CRX]
#name = Creality CR-X #name = Creality CR-X
@ -264,7 +264,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X #family = CR-X
#bed_model = cr10v2_bed.stl #bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg #bed_texture = cr10spro.svg
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
#[printer_model:CRXPRO] #[printer_model:CRXPRO]
#name = Creality CR-X Pro #name = Creality CR-X Pro
@ -273,7 +273,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X #family = CR-X
#bed_model = cr10v2_bed.stl #bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg #bed_texture = cr10spro.svg
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@ -326,7 +326,7 @@ notes =
overhangs = 0 overhangs = 0
only_retract_when_crossing_perimeters = 0 only_retract_when_crossing_perimeters = 0
ooze_prevention = 0 ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode output_filename_format = {input_filename_base}_{print_time}_{layer_height}mm_{temperature[0]}C_{filament_type[0]}_{printer_model}.gcode
perimeters = 2 perimeters = 2
perimeter_extruder = 1 perimeter_extruder = 1
perimeter_extrusion_width = 0.45 perimeter_extrusion_width = 0.45
@ -639,6 +639,18 @@ filament_density = 1.24
filament_colour = #FF0000 filament_colour = #FF0000
filament_spool_weight = 256 filament_spool_weight = 256
[filament:Devil Design PLA Matt @CREALITY]
inherits = *PLA*
filament_vendor = Devil Design
temperature = 205
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 20.00
filament_density = 1.38
filament_colour = #FF0000
filament_spool_weight = 256
[filament:Devil Design PLA Galaxy @CREALITY] [filament:Devil Design PLA Galaxy @CREALITY]
inherits = *PLA* inherits = *PLA*
renamed_from = "Devil Design PLA (Galaxy) @CREALITY" renamed_from = "Devil Design PLA (Galaxy) @CREALITY"

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

View file

@ -2,8 +2,9 @@ project(clipper)
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
add_library(clipper STATIC add_library(clipper STATIC
clipper.cpp # We are using ClipperLib compiled as part of the libslic3r project using Slic3r::Point as its base type.
clipper.hpp # clipper.cpp
# clipper.hpp
clipper_z.cpp clipper_z.cpp
clipper_z.hpp clipper_z.hpp
) )

File diff suppressed because it is too large Load diff

View file

@ -37,10 +37,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <functional> #include <functional>
#include <Eigen/Geometry>
#define CLIPPER_VERSION "6.2.6" #define CLIPPER_VERSION "6.2.6"
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz //#define CLIPPERLIB_USE_XYZ
//use_lines: Enables line clipping. Adds a very minor cost to performance. //use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines #define use_lines
@ -57,11 +59,15 @@
#include <functional> #include <functional>
#include <queue> #include <queue>
#ifdef use_xyz #ifdef CLIPPERLIB_NAMESPACE_PREFIX
namespace ClipperLib_Z { namespace CLIPPERLIB_NAMESPACE_PREFIX {
#else /* use_xyz */ #endif // CLIPPERLIB_NAMESPACE_PREFIX
namespace ClipperLib {
#endif /* use_xyz */ #ifdef CLIPPERLIB_USE_XYZ
namespace ClipperLib_Z {
#else
namespace ClipperLib {
#endif
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip }; enum PolyType { ptSubject, ptClip };
@ -88,29 +94,24 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#endif // CLIPPERLIB_INT32 #endif // CLIPPERLIB_INT32
struct IntPoint { #ifdef CLIPPERLIB_INTPOINT_TYPE
cInt X; using IntPoint = CLIPPERLIB_INTPOINT_TYPE;
cInt Y; #else // CLIPPERLIB_INTPOINT_TYPE
#ifdef use_xyz using IntPoint = Eigen::Matrix<cInt,
cInt Z; #ifdef CLIPPERLIB_USE_XYZ
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 3
#else #else // CLIPPERLIB_USE_XYZ
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 2
#endif #endif // CLIPPERLIB_USE_XYZ
, 1, Eigen::DontAlign>;
#endif // CLIPPERLIB_INTPOINT_TYPE
using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
{
return a.X == b.X && a.Y == b.Y;
}
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
{
return a.X != b.X || a.Y != b.Y;
}
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
typedef std::vector< IntPoint > Path; typedef std::vector<IntPoint> Path;
typedef std::vector< Path > Paths; typedef std::vector<Path> Paths;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
@ -119,16 +120,9 @@ std::ostream& operator <<(std::ostream &s, const IntPoint &p);
std::ostream& operator <<(std::ostream &s, const Path &p); std::ostream& operator <<(std::ostream &s, const Path &p);
std::ostream& operator <<(std::ostream &s, const Paths &p); std::ostream& operator <<(std::ostream &s, const Paths &p);
struct DoublePoint
{
double X;
double Y;
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback; typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback;
#endif #endif
@ -197,9 +191,7 @@ double Area(const Path &poly);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path); int PointInPolygon(const IntPoint &pt, const Path &path);
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415);
@ -269,11 +261,11 @@ enum EdgeSide { esLeft = 1, esRight = 2};
}; };
// Point of an output polygon. // Point of an output polygon.
// 36B on 64bit system without use_xyz. // 36B on 64bit system without CLIPPERLIB_USE_XYZ.
struct OutPt { struct OutPt {
// 4B // 4B
int Idx; int Idx;
// 16B without use_xyz / 24B with use_xyz // 16B without CLIPPERLIB_USE_XYZ / 24B with CLIPPERLIB_USE_XYZ
IntPoint Pt; IntPoint Pt;
// 4B on 32bit system, 8B on 64bit system // 4B on 32bit system, 8B on 64bit system
OutPt *Next; OutPt *Next;
@ -306,7 +298,58 @@ public:
m_HasOpenPaths(false) {} m_HasOpenPaths(false) {}
~ClipperBase() { Clear(); } ~ClipperBase() { Clear(); }
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
template<typename PathsProvider>
bool AddPaths(PathsProvider &&paths_provider, PolyType PolyTyp, bool Closed)
{
size_t num_paths = paths_provider.size();
if (num_paths == 0)
return false;
if (num_paths == 1)
return AddPath(*paths_provider.begin(), PolyTyp, Closed);
std::vector<int> num_edges(num_paths, 0);
int num_edges_total = 0;
size_t i = 0;
for (const Path &pg : paths_provider) {
// Remove duplicate end point from a closed input path.
// Remove duplicate points from the end of the input path.
int highI = (int)pg.size() -1;
if (Closed)
while (highI > 0 && (pg[highI] == pg[0]))
--highI;
while (highI > 0 && (pg[highI] == pg[highI -1]))
--highI;
if ((Closed && highI < 2) || (!Closed && highI < 1))
highI = -1;
num_edges[i ++] = highI + 1;
num_edges_total += highI + 1;
}
if (num_edges_total == 0)
return false;
// Allocate a new edge array.
std::vector<TEdge> edges(num_edges_total);
// Fill in the edge array.
bool result = false;
TEdge *p_edge = edges.data();
i = 0;
for (const Path &pg : paths_provider) {
if (num_edges[i]) {
bool res = AddPathInternal(pg, num_edges[i] - 1, PolyTyp, Closed, p_edge);
if (res) {
p_edge += num_edges[i];
result = true;
}
}
++ i;
}
if (result)
// At least some edges were generated. Remember the edge array.
m_edges.emplace_back(std::move(edges));
return result;
}
void Clear(); void Clear();
IntRect GetBounds(); IntRect GetBounds();
// By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping. // By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping.
@ -368,7 +411,7 @@ public:
bool StrictlySimple() const {return m_StrictSimple;}; bool StrictlySimple() const {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;}; void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0) //set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; }
#endif #endif
protected: protected:
@ -401,7 +444,7 @@ private:
// Does the result go to a PolyTree or Paths? // Does the result go to a PolyTree or Paths?
bool m_UsingPolyTree; bool m_UsingPolyTree;
bool m_StrictSimple; bool m_StrictSimple;
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
ZFillCallback m_ZFill; //custom callback ZFillCallback m_ZFill; //custom callback
#endif #endif
void SetWindingCount(TEdge& edge) const; void SetWindingCount(TEdge& edge) const;
@ -454,7 +497,7 @@ private:
void DoSimplePolygons(); void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif #endif
}; };
@ -467,7 +510,11 @@ public:
MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {} MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {}
~ClipperOffset() { Clear(); } ~ClipperOffset() { Clear(); }
void AddPath(const Path& path, JoinType joinType, EndType endType); void AddPath(const Path& path, JoinType joinType, EndType endType);
void AddPaths(const Paths& paths, JoinType joinType, EndType endType); template<typename PathsProvider>
void AddPaths(PathsProvider &&paths, JoinType joinType, EndType endType) {
for (const Path &path : paths)
AddPath(path, joinType, endType);
}
void Execute(Paths& solution, double delta); void Execute(Paths& solution, double delta);
void Execute(PolyTree& solution, double delta); void Execute(PolyTree& solution, double delta);
void Clear(); void Clear();
@ -504,8 +551,20 @@ class clipperException : public std::exception
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<typename PathsProvider>
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) {
Clipper c;
c.StrictlySimple(true);
c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true);
Paths out;
c.Execute(ctUnion, out, fillType, fillType);
return out;
}
} //ClipperLib namespace } //ClipperLib namespace
#ifdef CLIPPERLIB_NAMESPACE_PREFIX
} // namespace CLIPPERLIB_NAMESPACE_PREFIX
#endif // CLIPPERLIB_NAMESPACE_PREFIX
#endif //clipper_hpp #endif //clipper_hpp

View file

@ -1,7 +1,7 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support. // Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
// Enable the Z coordinate support. // Enable the Z coordinate support.
#define use_xyz #define CLIPPERLIB_USE_XYZ
// and let it compile // and let it compile
#include "clipper.cpp" #include "clipper.cpp"

View file

@ -2,17 +2,17 @@
#ifndef clipper_z_hpp #ifndef clipper_z_hpp
#ifdef clipper_hpp #ifdef clipper_hpp
#error "You should include the clipper_z.hpp first" #error "You should include clipper_z.hpp before clipper.hpp"
#endif #endif
#define clipper_z_hpp #define clipper_z_hpp
// Enable the Z coordinate support. // Enable the Z coordinate support.
#define use_xyz #define CLIPPERLIB_USE_XYZ
#include "clipper.hpp" #include "clipper.hpp"
#undef clipper_hpp #undef clipper_hpp
#undef use_xyz #undef CLIPPERLIB_USE_XYZ
#endif // clipper_z_hpp #endif // clipper_z_hpp

View file

@ -12,11 +12,8 @@ set(LIBNEST2D_SRCFILES
include/libnest2d/placers/bottomleftplacer.hpp include/libnest2d/placers/bottomleftplacer.hpp
include/libnest2d/placers/nfpplacer.hpp include/libnest2d/placers/nfpplacer.hpp
include/libnest2d/selections/selection_boilerplate.hpp include/libnest2d/selections/selection_boilerplate.hpp
#include/libnest2d/selections/filler.hpp
include/libnest2d/selections/firstfit.hpp include/libnest2d/selections/firstfit.hpp
#include/libnest2d/selections/djd_heuristic.hpp include/libnest2d/backends/libslic3r/geometries.hpp
include/libnest2d/backends/clipper/geometries.hpp
include/libnest2d/backends/clipper/clipper_polygon.hpp
include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp
include/libnest2d/optimizers/nlopt/simplex.hpp include/libnest2d/optimizers/nlopt/simplex.hpp
include/libnest2d/optimizers/nlopt/subplex.hpp include/libnest2d/optimizers/nlopt/subplex.hpp
@ -27,5 +24,5 @@ set(LIBNEST2D_SRCFILES
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost) target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r)
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_clipper) target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)

View file

@ -1,72 +0,0 @@
#ifndef CLIPPER_POLYGON_HPP
#define CLIPPER_POLYGON_HPP
#include <clipper.hpp>
namespace ClipperLib {
struct Polygon {
Path Contour;
Paths Holes;
inline Polygon() = default;
inline explicit Polygon(const Path& cont): Contour(cont) {}
// inline explicit Polygon(const Paths& holes):
// Holes(holes) {}
inline Polygon(const Path& cont, const Paths& holes):
Contour(cont), Holes(holes) {}
inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {}
// inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {}
inline Polygon(Path&& cont, Paths&& holes):
Contour(std::move(cont)), Holes(std::move(holes)) {}
};
inline IntPoint& operator +=(IntPoint& p, const IntPoint& pa ) {
// This could be done with SIMD
p.X += pa.X;
p.Y += pa.Y;
return p;
}
inline IntPoint operator+(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret += p2;
return ret;
}
inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) {
p.X -= pa.X;
p.Y -= pa.Y;
return p;
}
inline IntPoint operator -(const IntPoint& p ) {
IntPoint ret = p;
ret.X = -ret.X;
ret.Y = -ret.Y;
return ret;
}
inline IntPoint operator-(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret -= p2;
return ret;
}
inline IntPoint& operator *=(IntPoint& p, const IntPoint& pa ) {
p.X *= pa.X;
p.Y *= pa.Y;
return p;
}
inline IntPoint operator*(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret *= p2;
return ret;
}
}
#endif // CLIPPER_POLYGON_HPP

View file

@ -1,356 +0,0 @@
#ifndef CLIPPER_BACKEND_HPP
#define CLIPPER_BACKEND_HPP
#include <sstream>
#include <unordered_map>
#include <cassert>
#include <vector>
#include <iostream>
#include <libnest2d/geometry_traits.hpp>
#include <libnest2d/geometry_traits_nfp.hpp>
#include "clipper_polygon.hpp"
namespace libnest2d {
// Aliases for convinience
using PointImpl = ClipperLib::IntPoint;
using PathImpl = ClipperLib::Path;
using HoleStore = ClipperLib::Paths;
using PolygonImpl = ClipperLib::Polygon;
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
template<> struct ShapeTag<PathImpl> { using Type = PathTag; };
template<> struct ShapeTag<PointImpl> { using Type = PointTag; };
// Type of coordinate units used by Clipper. Enough to specialize for point,
// the rest of the types will work (Path, Polygon)
template<> struct CoordType<PointImpl> {
using Type = ClipperLib::cInt;
static const constexpr ClipperLib::cInt MM_IN_COORDS = 1000000;
};
// Enough to specialize for path, it will work for multishape and Polygon
template<> struct PointType<PathImpl> { using Type = PointImpl; };
// This is crucial. CountourType refers to itself by default, so we don't have
// to secialize for clipper Path. ContourType<PathImpl>::Type is PathImpl.
template<> struct ContourType<PolygonImpl> { using Type = PathImpl; };
// The holes are contained in Clipper::Paths
template<> struct HolesContainer<PolygonImpl> { using Type = ClipperLib::Paths; };
namespace pointlike {
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline ClipperLib::cInt x(const PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline ClipperLib::cInt y(const PointImpl& p)
{
return p.Y;
}
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline ClipperLib::cInt& x(PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline ClipperLib::cInt& y(PointImpl& p)
{
return p.Y;
}
}
// Using the libnest2d default area implementation
#define DISABLE_BOOST_AREA
namespace shapelike {
template<>
inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance, const PolygonTag&)
{
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
using ClipperLib::jtSquare;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
Paths result;
try {
ClipperOffset offs;
offs.AddPath(sh.Contour, jtSquare, etClosedPolygon);
offs.AddPaths(sh.Holes, jtSquare, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
} catch (ClipperLib::clipperException &) {
throw GeometryException(GeomErr::OFFSET);
}
// Offsetting reverts the orientation and also removes the last vertex
// so boost will not have a closed polygon.
// we plan to replace contours
sh.Holes.clear();
bool found_the_contour = false;
for(auto& r : result) {
if(ClipperLib::Orientation(r)) {
// We don't like if the offsetting generates more than one contour
// but throwing would be an overkill. Instead, we should warn the
// caller about the inability to create correct geometries
if(!found_the_contour) {
sh.Contour = std::move(r);
ClipperLib::ReversePath(sh.Contour);
auto front_p = sh.Contour.front();
sh.Contour.emplace_back(std::move(front_p));
found_the_contour = true;
} else {
dout() << "Warning: offsetting result is invalid!";
/* TODO warning */
}
} else {
// TODO If there are multiple contours we can't be sure which hole
// belongs to the first contour. (But in this case the situation is
// bad enough to let it go...)
sh.Holes.emplace_back(std::move(r));
ClipperLib::ReversePath(sh.Holes.back());
auto front_p = sh.Holes.back().front();
sh.Holes.back().emplace_back(std::move(front_p));
}
}
}
template<>
inline void offset(PathImpl& sh, TCoord<PointImpl> distance, const PathTag&)
{
PolygonImpl p(std::move(sh));
offset(p, distance, PolygonTag());
sh = p.Contour;
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline std::string toString(const PolygonImpl& sh)
{
std::stringstream ss;
ss << "Contour {\n";
for(auto p : sh.Contour) {
ss << "\t" << p.X << " " << p.Y << "\n";
}
ss << "}\n";
for(auto& h : sh.Holes) {
ss << "Holes {\n";
for(auto p : h) {
ss << "\t{\n";
ss << "\t\t" << p.X << " " << p.Y << "\n";
ss << "\t}\n";
}
ss << "}\n";
}
return ss.str();
}
template<>
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
{
PolygonImpl p;
p.Contour = path;
p.Holes = holes;
return p;
}
template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
PolygonImpl p;
p.Contour.swap(path);
p.Holes.swap(holes);
return p;
}
template<>
inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
{
return sh.Holes;
}
template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
{
return sh.Holes;
}
template<>
inline TContour<PolygonImpl>& hole(PolygonImpl& sh, unsigned long idx)
{
return sh.Holes[idx];
}
template<>
inline const TContour<PolygonImpl>& hole(const PolygonImpl& sh,
unsigned long idx)
{
return sh.Holes[idx];
}
template<> inline size_t holeCount(const PolygonImpl& sh)
{
return sh.Holes.size();
}
template<> inline PathImpl& contour(PolygonImpl& sh)
{
return sh.Contour;
}
template<>
inline const PathImpl& contour(const PolygonImpl& sh)
{
return sh.Contour;
}
#define DISABLE_BOOST_TRANSLATE
template<>
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; }
}
#define DISABLE_BOOST_ROTATE
template<>
inline void rotate(PolygonImpl& sh, const Radians& rads)
{
using Coord = TCoord<PointImpl>;
auto cosa = rads.cos();
auto sina = rads.sin();
for(auto& p : sh.Contour) {
p = {
static_cast<Coord>(p.X * cosa - p.Y * sina),
static_cast<Coord>(p.X * sina + p.Y * cosa)
};
}
for(auto& hole : sh.Holes) for(auto& p : hole) {
p = {
static_cast<Coord>(p.X * cosa - p.Y * sina),
static_cast<Coord>(p.X * sina + p.Y * cosa)
};
}
}
} // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE
inline TMultiShape<PolygonImpl> clipper_execute(
ClipperLib::Clipper& clipper,
ClipperLib::ClipType clipType,
ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd,
ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd)
{
TMultiShape<PolygonImpl> retv;
ClipperLib::PolyTree result;
clipper.Execute(clipType, result, subjFillType, clipFillType);
retv.reserve(static_cast<size_t>(result.Total()));
std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
PolygonImpl poly;
poly.Contour.swap(pptr->Contour);
assert(!pptr->IsHole());
if(!poly.Contour.empty() ) {
auto front_p = poly.Contour.front();
auto &back_p = poly.Contour.back();
if(front_p.X != back_p.X || front_p.Y != back_p.X)
poly.Contour.emplace_back(front_p);
}
for(auto h : pptr->Childs) { processHole(h, poly); }
retv.push_back(poly);
};
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
{
poly.Holes.emplace_back(std::move(pptr->Contour));
assert(pptr->IsHole());
if(!poly.Contour.empty() ) {
auto front_p = poly.Contour.front();
auto &back_p = poly.Contour.back();
if(front_p.X != back_p.X || front_p.Y != back_p.X)
poly.Contour.emplace_back(front_p);
}
for(auto c : pptr->Childs) processPoly(c);
};
auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
{
for(auto ch : node->Childs) processPoly(ch);
};
traverse(&result);
return retv;
}
namespace nfp {
template<> inline TMultiShape<PolygonImpl>
merge(const TMultiShape<PolygonImpl>& shapes)
{
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
bool closed = true;
bool valid = true;
for(auto& path : shapes) {
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
for(auto& h : path.Holes)
valid &= clipper.AddPath(h, ClipperLib::ptSubject, closed);
}
if(!valid) throw GeometryException(GeomErr::MERGE);
return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative);
}
}
}
#define DISABLE_BOOST_CONVEX_HULL
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
// All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // CLIPPER_BACKEND_HPP

View file

@ -0,0 +1,283 @@
#ifndef CLIPPER_BACKEND_HPP
#define CLIPPER_BACKEND_HPP
#include <sstream>
#include <unordered_map>
#include <cassert>
#include <vector>
#include <iostream>
#include <libnest2d/geometry_traits.hpp>
#include <libnest2d/geometry_traits_nfp.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/ClipperUtils.hpp>
namespace Slic3r {
template<class T, class En = void> struct IsVec_ : public std::false_type {};
template<class T> struct IsVec_< Vec<2, T> >: public std::true_type {};
template<class T>
static constexpr const bool IsVec = IsVec_<libnest2d::remove_cvref_t<T>>::value;
template<class T, class O> using VecOnly = std::enable_if_t<IsVec<T>, O>;
inline Point operator+(const Point& p1, const Point& p2) {
Point ret = p1;
ret += p2;
return ret;
}
inline Point operator -(const Point& p ) {
Point ret = p;
ret.x() = -ret.x();
ret.y() = -ret.y();
return ret;
}
inline Point operator-(const Point& p1, const Point& p2) {
Point ret = p1;
ret -= p2;
return ret;
}
inline Point& operator *=(Point& p, const Point& pa ) {
p.x() *= pa.x();
p.y() *= pa.y();
return p;
}
inline Point operator*(const Point& p1, const Point& p2) {
Point ret = p1;
ret *= p2;
return ret;
}
} // namespace Slic3r
namespace libnest2d {
template<class T> using Vec = Slic3r::Vec<2, T>;
// Aliases for convinience
using PointImpl = Slic3r::Point;
using PathImpl = Slic3r::Polygon;
using HoleStore = Slic3r::Polygons;
using PolygonImpl = Slic3r::ExPolygon;
template<> struct ShapeTag<Slic3r::Vec2crd> { using Type = PointTag; };
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
template<> struct ShapeTag<std::vector<Slic3r::Vec2crd>> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::ExPolygons> { using Type = MultiPolygonTag; };
// Type of coordinate units used by Clipper. Enough to specialize for point,
// the rest of the types will work (Path, Polygon)
template<> struct CoordType<Slic3r::Point> {
using Type = coord_t;
static const constexpr coord_t MM_IN_COORDS = 1000000;
};
template<> struct CoordType<Slic3r::Vec2crd> {
using Type = coord_t;
static const constexpr coord_t MM_IN_COORDS = 1000000;
};
// Enough to specialize for path, it will work for multishape and Polygon
template<> struct PointType<std::vector<Slic3r::Vec2crd>> { using Type = Slic3r::Vec2crd; };
template<> struct PointType<Slic3r::Polygon> { using Type = Slic3r::Point; };
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
// This is crucial. CountourType refers to itself by default, so we don't have
// to secialize for clipper Path. ContourType<PathImpl>::Type is PathImpl.
template<> struct ContourType<Slic3r::ExPolygon> { using Type = Slic3r::Polygon; };
// The holes are contained in Clipper::Paths
template<> struct HolesContainer<Slic3r::ExPolygon> { using Type = Slic3r::Polygons; };
template<>
struct OrientationType<Slic3r::Polygon> {
static const constexpr Orientation Value = Orientation::COUNTER_CLOCKWISE;
};
template<>
struct OrientationType<Slic3r::Points> {
static const constexpr Orientation Value = Orientation::COUNTER_CLOCKWISE;
};
template<>
struct ClosureType<Slic3r::Polygon> {
static const constexpr Closure Value = Closure::OPEN;
};
template<>
struct ClosureType<Slic3r::Points> {
static const constexpr Closure Value = Closure::OPEN;
};
template<> struct MultiShape<Slic3r::ExPolygon> { using Type = Slic3r::ExPolygons; };
template<> struct ContourType<Slic3r::ExPolygons> { using Type = Slic3r::Polygon; };
// Using the libnest2d default area implementation
#define DISABLE_BOOST_AREA
namespace shapelike {
template<>
inline void offset(Slic3r::ExPolygon& sh, coord_t distance, const PolygonTag&)
{
#define DISABLE_BOOST_OFFSET
auto res = Slic3r::offset_ex(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
template<>
inline void offset(Slic3r::Polygon& sh, coord_t distance, const PathTag&)
{
auto res = Slic3r::offset(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline std::string toString(const Slic3r::ExPolygon& sh)
{
std::stringstream ss;
ss << "Contour {\n";
for(auto &p : sh.contour.points) {
ss << "\t" << p.x() << " " << p.y() << "\n";
}
ss << "}\n";
for(auto& h : sh.holes) {
ss << "Holes {\n";
for(auto p : h.points) {
ss << "\t{\n";
ss << "\t\t" << p.x() << " " << p.y() << "\n";
ss << "\t}\n";
}
ss << "}\n";
}
return ss.str();
}
template<>
inline Slic3r::ExPolygon create(const Slic3r::Polygon& path, const Slic3r::Polygons& holes)
{
Slic3r::ExPolygon p;
p.contour = path;
p.holes = holes;
return p;
}
template<> inline Slic3r::ExPolygon create(Slic3r::Polygon&& path, Slic3r::Polygons&& holes) {
Slic3r::ExPolygon p;
p.contour.points.swap(path.points);
p.holes.swap(holes);
return p;
}
template<>
inline const THolesContainer<PolygonImpl>& holes(const Slic3r::ExPolygon& sh)
{
return sh.holes;
}
template<> inline THolesContainer<PolygonImpl>& holes(Slic3r::ExPolygon& sh)
{
return sh.holes;
}
template<>
inline Slic3r::Polygon& hole(Slic3r::ExPolygon& sh, unsigned long idx)
{
return sh.holes[idx];
}
template<>
inline const Slic3r::Polygon& hole(const Slic3r::ExPolygon& sh, unsigned long idx)
{
return sh.holes[idx];
}
template<> inline size_t holeCount(const Slic3r::ExPolygon& sh)
{
return sh.holes.size();
}
template<> inline Slic3r::Polygon& contour(Slic3r::ExPolygon& sh)
{
return sh.contour;
}
template<>
inline const Slic3r::Polygon& contour(const Slic3r::ExPolygon& sh)
{
return sh.contour;
}
template<>
inline void reserve(Slic3r::Polygon& p, size_t vertex_capacity, const PathTag&)
{
p.points.reserve(vertex_capacity);
}
template<>
inline void addVertex(Slic3r::Polygon& sh, const PathTag&, const Slic3r::Point &p)
{
sh.points.emplace_back(p);
}
#define DISABLE_BOOST_TRANSLATE
template<>
inline void translate(Slic3r::ExPolygon& sh, const Slic3r::Point& offs)
{
sh.translate(offs);
}
#define DISABLE_BOOST_ROTATE
template<>
inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads)
{
sh.rotate(rads);
}
} // namespace shapelike
namespace nfp {
#define DISABLE_BOOST_NFP_MERGE
template<>
inline TMultiShape<PolygonImpl> merge(const TMultiShape<PolygonImpl>& shapes)
{
return Slic3r::union_ex(shapes);
}
} // namespace nfp
} // namespace libnest2d
#define DISABLE_BOOST_CONVEX_HULL
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
// All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // CLIPPER_BACKEND_HPP

View file

@ -128,22 +128,32 @@ template<class S> struct ContourType<DefaultMultiShape<S>> {
using Type = typename ContourType<S>::Type; using Type = typename ContourType<S>::Type;
}; };
enum class Orientation { enum class Orientation { CLOCKWISE, COUNTER_CLOCKWISE };
CLOCKWISE,
COUNTER_CLOCKWISE
};
template<class S> template<class S>
struct OrientationType { struct OrientationType {
// Default Polygon orientation that the library expects // Default Polygon orientation that the library expects
static const Orientation Value = Orientation::CLOCKWISE; static const constexpr Orientation Value = Orientation::CLOCKWISE;
}; };
template<class T> inline /*constexpr*/ bool is_clockwise() { template<class T> inline constexpr bool is_clockwise() {
return OrientationType<TContour<T>>::Value == Orientation::CLOCKWISE; return OrientationType<TContour<T>>::Value == Orientation::CLOCKWISE;
} }
template<class T>
inline const constexpr Orientation OrientationTypeV =
OrientationType<TContour<T>>::Value;
enum class Closure { OPEN, CLOSED };
template<class S> struct ClosureType {
static const constexpr Closure Value = Closure::CLOSED;
};
template<class T>
inline const constexpr Closure ClosureTypeV =
ClosureType<TContour<T>>::Value;
/** /**
* \brief A point pair base class for other point pairs (segment, box, ...). * \brief A point pair base class for other point pairs (segment, box, ...).
@ -587,9 +597,9 @@ inline void reserve(RawPath& p, size_t vertex_capacity, const PathTag&)
} }
template<class S, class...Args> template<class S, class...Args>
inline void addVertex(S& sh, const PathTag&, Args...args) inline void addVertex(S& sh, const PathTag&, const TPoint<S> &p)
{ {
sh.emplace_back(std::forward<Args>(args)...); sh.emplace_back(p);
} }
template<class S, class Fn> template<class S, class Fn>
@ -841,9 +851,9 @@ template<class P> auto rbegin(P& p) -> decltype(_backward(end(p)))
return _backward(end(p)); return _backward(end(p));
} }
template<class P> auto rcbegin(const P& p) -> decltype(_backward(end(p))) template<class P> auto rcbegin(const P& p) -> decltype(_backward(cend(p)))
{ {
return _backward(end(p)); return _backward(cend(p));
} }
template<class P> auto rend(P& p) -> decltype(_backward(begin(p))) template<class P> auto rend(P& p) -> decltype(_backward(begin(p)))
@ -873,16 +883,16 @@ inline void reserve(T& sh, size_t vertex_capacity) {
reserve(sh, vertex_capacity, Tag<T>()); reserve(sh, vertex_capacity, Tag<T>());
} }
template<class S, class...Args> template<class S>
inline void addVertex(S& sh, const PolygonTag&, Args...args) inline void addVertex(S& sh, const PolygonTag&, const TPoint<S> &p)
{ {
addVertex(contour(sh), PathTag(), std::forward<Args>(args)...); addVertex(contour(sh), PathTag(), p);
} }
template<class S, class...Args> // Tag dispatcher template<class S> // Tag dispatcher
inline void addVertex(S& sh, Args...args) inline void addVertex(S& sh, const TPoint<S> &p)
{ {
addVertex(sh, Tag<S>(), std::forward<Args>(args)...); addVertex(sh, Tag<S>(), p);
} }
template<class S> template<class S>

View file

@ -28,7 +28,7 @@ inline void buildPolygon(const EdgeList& edgelist,
auto& rsh = sl::contour(rpoly); auto& rsh = sl::contour(rpoly);
sl::reserve(rsh, 2*edgelist.size()); sl::reserve(rsh, 2 * edgelist.size());
// Add the two vertices from the first edge into the final polygon. // Add the two vertices from the first edge into the final polygon.
sl::addVertex(rsh, edgelist.front().first()); sl::addVertex(rsh, edgelist.front().first());
@ -57,7 +57,6 @@ inline void buildPolygon(const EdgeList& edgelist,
tmp = std::next(tmp); tmp = std::next(tmp);
} }
} }
template<class Container, class Iterator = typename Container::iterator> template<class Container, class Iterator = typename Container::iterator>
@ -214,17 +213,24 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
// Reserve the needed memory // Reserve the needed memory
edgelist.reserve(cap); edgelist.reserve(cap);
sl::reserve(rsh, static_cast<unsigned long>(cap)); sl::reserve(rsh, static_cast<unsigned long>(cap));
auto add_edge = [&edgelist](const Vertex &v1, const Vertex &v2) {
Edge e{v1, v2};
if (e.sqlength() > 0)
edgelist.emplace_back(e);
};
{ // place all edges from sh into edgelist { // place all edges from sh into edgelist
auto first = sl::cbegin(sh); auto first = sl::cbegin(sh);
auto next = std::next(first); auto next = std::next(first);
while(next != sl::cend(sh)) { while(next != sl::cend(sh)) {
if (pl::magnsq(*next - *first) > 0) add_edge(*(first), *(next));
edgelist.emplace_back(*(first), *(next));
++first; ++next; ++first; ++next;
} }
if constexpr (ClosureTypeV<RawShape> == Closure::OPEN)
add_edge(*sl::rcbegin(sh), *sl::cbegin(sh));
} }
{ // place all edges from other into edgelist { // place all edges from other into edgelist
@ -232,17 +238,19 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
auto next = std::next(first); auto next = std::next(first);
while(next != sl::cend(other)) { while(next != sl::cend(other)) {
if (pl::magnsq(*next - *first) > 0) add_edge(*(next), *(first));
edgelist.emplace_back(*(next), *(first));
++first; ++next; ++first; ++next;
} }
if constexpr (ClosureTypeV<RawShape> == Closure::OPEN)
add_edge(*sl::cbegin(other), *sl::rcbegin(other));
} }
std::sort(edgelist.begin(), edgelist.end(), std::sort(edgelist.begin(), edgelist.end(),
[](const Edge& e1, const Edge& e2) [](const Edge& e1, const Edge& e2)
{ {
Vertex ax(1, 0); // Unit vector for the X axis const Vertex ax(1, 0); // Unit vector for the X axis
// get cectors from the edges // get cectors from the edges
Vertex p1 = e1.second() - e1.first(); Vertex p1 = e1.second() - e1.first();
@ -288,12 +296,18 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
// If Ratio is an actual rational type, there is no precision loss // If Ratio is an actual rational type, there is no precision loss
auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0]; auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0];
auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1]; auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1];
return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2; if constexpr (is_clockwise<RawShape>())
return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2;
else
return q[0] < 2 ? pcos1 > pcos2 : pcos1 < pcos2;
} }
// If in different quadrants, compare the quadrant indices only. // If in different quadrants, compare the quadrant indices only.
return q[0] > q[1]; if constexpr (is_clockwise<RawShape>())
return q[0] > q[1];
else
return q[0] < q[1];
}); });
__nfp::buildPolygon(edgelist, rsh, top_nfp); __nfp::buildPolygon(edgelist, rsh, top_nfp);

View file

@ -7,6 +7,10 @@
#include <libnest2d/backends/clipper/geometries.hpp> #include <libnest2d/backends/clipper/geometries.hpp>
#endif #endif
#ifdef LIBNEST2D_GEOMETRIES_libslic3r
#include <libnest2d/backends/libslic3r/geometries.hpp>
#endif
#ifdef LIBNEST2D_OPTIMIZER_nlopt #ifdef LIBNEST2D_OPTIMIZER_nlopt
// We include the stock optimizers for local and global optimization // We include the stock optimizers for local and global optimization
#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer #include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer

View file

@ -96,7 +96,7 @@ public:
* @return The orientation type identifier for the _Item type. * @return The orientation type identifier for the _Item type.
*/ */
static BP2D_CONSTEXPR Orientation orientation() { static BP2D_CONSTEXPR Orientation orientation() {
return OrientationType<RawShape>::Value; return OrientationType<TContour<RawShape>>::Value;
} }
/** /**
@ -446,44 +446,32 @@ private:
} }
}; };
template<class Sh> Sh create_rect(TCoord<Sh> width, TCoord<Sh> height)
{
auto sh = sl::create<Sh>(
{{0, 0}, {0, height}, {width, height}, {width, 0}});
if constexpr (ClosureTypeV<Sh> == Closure::CLOSED)
sl::addVertex(sh, {0, 0});
if constexpr (OrientationTypeV<Sh> == Orientation::COUNTER_CLOCKWISE)
std::reverse(sl::begin(sh), sl::end(sh));
return sh;
}
/** /**
* \brief Subclass of _Item for regular rectangle items. * \brief Subclass of _Item for regular rectangle items.
*/ */
template<class RawShape> template<class Sh>
class _Rectangle: public _Item<RawShape> { class _Rectangle: public _Item<Sh> {
using _Item<RawShape>::vertex; using _Item<Sh>::vertex;
using TO = Orientation; using TO = Orientation;
public: public:
using Unit = TCoord<TPoint<RawShape>>; using Unit = TCoord<Sh>;
template<TO o = OrientationType<RawShape>::Value> inline _Rectangle(Unit w, Unit h): _Item<Sh>{create_rect<Sh>(w, h)} {}
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != CLOCKWISE
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{0, height},
{width, height},
{width, 0},
{0, 0}
} ))
{
}
template<TO o = OrientationType<RawShape>::Value>
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != COUNTER_CLOCKWISE
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{width, 0},
{width, height},
{0, height},
{0, 0}
} ))
{
}
inline Unit width() const BP2D_NOEXCEPT { inline Unit width() const BP2D_NOEXCEPT {
return getX(vertex(2)); return getX(vertex(2));

View file

@ -365,44 +365,50 @@ protected:
// the additional vertices for maintaning min object distance // the additional vertices for maintaning min object distance
sl::reserve(rsh, finish-start+4); sl::reserve(rsh, finish-start+4);
/*auto addOthers = [&rsh, finish, start, &item](){ auto addOthers_ = [&rsh, finish, start, &item](){
for(size_t i = start+1; i < finish; i++) for(size_t i = start+1; i < finish; i++)
sl::addVertex(rsh, item.vertex(i)); sl::addVertex(rsh, item.vertex(i));
};*/ };
auto reverseAddOthers = [&rsh, finish, start, &item](){ auto reverseAddOthers_ = [&rsh, finish, start, &item](){
for(auto i = finish-1; i > start; i--) for(auto i = finish-1; i > start; i--)
sl::addVertex(rsh, item.vertex( sl::addVertex(rsh, item.vertex(static_cast<unsigned long>(i)));
static_cast<unsigned long>(i))); };
auto addOthers = [&addOthers_, &reverseAddOthers_]() {
if constexpr (!is_clockwise<RawShape>())
addOthers_();
else
reverseAddOthers_();
}; };
// Final polygon construction... // Final polygon construction...
static_assert(OrientationType<RawShape>::Value ==
Orientation::CLOCKWISE,
"Counter clockwise toWallPoly() Unimplemented!");
// Clockwise polygon construction // Clockwise polygon construction
sl::addVertex(rsh, topleft_vertex); sl::addVertex(rsh, topleft_vertex);
if(dir == Dir::LEFT) reverseAddOthers(); if(dir == Dir::LEFT) addOthers();
else { else {
sl::addVertex(rsh, getX(topleft_vertex), 0); sl::addVertex(rsh, {getX(topleft_vertex), 0});
sl::addVertex(rsh, getX(bottomleft_vertex), 0); sl::addVertex(rsh, {getX(bottomleft_vertex), 0});
} }
sl::addVertex(rsh, bottomleft_vertex); sl::addVertex(rsh, bottomleft_vertex);
if(dir == Dir::LEFT) { if(dir == Dir::LEFT) {
sl::addVertex(rsh, 0, getY(bottomleft_vertex)); sl::addVertex(rsh, {0, getY(bottomleft_vertex)});
sl::addVertex(rsh, 0, getY(topleft_vertex)); sl::addVertex(rsh, {0, getY(topleft_vertex)});
} }
else reverseAddOthers(); else addOthers();
// Close the polygon // Close the polygon
sl::addVertex(rsh, topleft_vertex); if constexpr (ClosureTypeV<RawShape> == Closure::CLOSED)
sl::addVertex(rsh, topleft_vertex);
if constexpr (!is_clockwise<RawShape>())
std::reverse(rsh.begin(), rsh.end());
return ret; return ret;
} }

View file

@ -250,8 +250,8 @@ template<class RawShape> class EdgeCache {
Vertex ret = edge.first(); Vertex ret = edge.first();
// Get the point on the edge which lies in ed distance from the start // Get the point on the edge which lies in ed distance from the start
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))), ret += Vertex(static_cast<Coord>(std::round(ed*std::cos(angle))),
static_cast<Coord>(std::round(ed*std::sin(angle))) }; static_cast<Coord>(std::round(ed*std::sin(angle))));
return ret; return ret;
} }
@ -724,8 +724,7 @@ private:
auto rawobjfunc = [_objfunc, iv, startpos] auto rawobjfunc = [_objfunc, iv, startpos]
(Vertex v, Item& itm) (Vertex v, Item& itm)
{ {
auto d = v - iv; auto d = (v - iv) + startpos;
d += startpos;
itm.translation(d); itm.translation(d);
return _objfunc(itm); return _objfunc(itm);
}; };
@ -742,8 +741,7 @@ private:
&item, &bin, &iv, &startpos] (const Optimum& o) &item, &bin, &iv, &startpos] (const Optimum& o)
{ {
auto v = getNfpPoint(o); auto v = getNfpPoint(o);
auto d = v - iv; auto d = (v - iv) + startpos;
d += startpos;
item.translation(d); item.translation(d);
merged_pile.emplace_back(item.transformedShape()); merged_pile.emplace_back(item.transformedShape());
@ -877,8 +875,7 @@ private:
} }
if( best_score < global_score ) { if( best_score < global_score ) {
auto d = getNfpPoint(optimum) - iv; auto d = (getNfpPoint(optimum) - iv) + startpos;
d += startpos;
final_tr = d; final_tr = d;
final_rot = initial_rot + rot; final_rot = initial_rot + rot;
can_pack = true; can_pack = true;

View file

@ -19,7 +19,7 @@
#pragma warning(pop) #pragma warning(pop)
#endif #endif
// this should be removed to not confuse the compiler // this should be removed to not confuse the compiler
// #include <libnest2d.h> // #include "../libnest2d.hpp"
namespace bp2d { namespace bp2d {
@ -30,6 +30,10 @@ using libnest2d::PolygonImpl;
using libnest2d::PathImpl; using libnest2d::PathImpl;
using libnest2d::Orientation; using libnest2d::Orientation;
using libnest2d::OrientationType; using libnest2d::OrientationType;
using libnest2d::OrientationTypeV;
using libnest2d::ClosureType;
using libnest2d::Closure;
using libnest2d::ClosureTypeV;
using libnest2d::getX; using libnest2d::getX;
using libnest2d::getY; using libnest2d::getY;
using libnest2d::setX; using libnest2d::setX;
@ -213,8 +217,15 @@ struct ToBoostOrienation<bp2d::Orientation::COUNTER_CLOCKWISE> {
static const order_selector Value = counterclockwise; static const order_selector Value = counterclockwise;
}; };
static const bp2d::Orientation RealOrientation = template<bp2d::Closure> struct ToBoostClosure {};
bp2d::OrientationType<bp2d::PolygonImpl>::Value;
template<> struct ToBoostClosure<bp2d::Closure::OPEN> {
static const constexpr closure_selector Value = closure_selector::open;
};
template<> struct ToBoostClosure<bp2d::Closure::CLOSED> {
static const constexpr closure_selector Value = closure_selector::closed;
};
// Ring implementation ///////////////////////////////////////////////////////// // Ring implementation /////////////////////////////////////////////////////////
@ -225,12 +236,13 @@ template<> struct tag<bp2d::PathImpl> {
template<> struct point_order<bp2d::PathImpl> { template<> struct point_order<bp2d::PathImpl> {
static const order_selector value = static const order_selector value =
ToBoostOrienation<RealOrientation>::Value; ToBoostOrienation<bp2d::OrientationTypeV<bp2d::PathImpl>>::Value;
}; };
// All our Paths should be closed for the bin packing application // All our Paths should be closed for the bin packing application
template<> struct closure<bp2d::PathImpl> { template<> struct closure<bp2d::PathImpl> {
static const closure_selector value = closed; static const constexpr closure_selector value =
ToBoostClosure< bp2d::ClosureTypeV<bp2d::PathImpl> >::Value;
}; };
// Polygon implementation ////////////////////////////////////////////////////// // Polygon implementation //////////////////////////////////////////////////////

View file

@ -3,7 +3,7 @@
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include <libnest2d/backends/clipper/geometries.hpp> #include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp> #include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <libnest2d/placers/nfpplacer.hpp> #include <libnest2d/placers/nfpplacer.hpp>
#include <libnest2d/selections/firstfit.hpp> #include <libnest2d/selections/firstfit.hpp>
@ -54,23 +54,22 @@ namespace Slic3r {
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs> template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled( inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
const ClipperLib::IntPoint &v) noexcept const Slic3r::ClipperLib::IntPoint &v) noexcept
{ {
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X), return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.x()),
unscaled<Tout>(v.Y)}; unscaled<Tout>(v.y())};
} }
namespace arrangement { namespace arrangement {
using namespace libnest2d; using namespace libnest2d;
namespace clppr = ClipperLib;
// Get the libnest2d types for clipper backend // Get the libnest2d types for clipper backend
using Item = _Item<clppr::Polygon>; using Item = _Item<ExPolygon>;
using Box = _Box<clppr::IntPoint>; using Box = _Box<Point>;
using Circle = _Circle<clppr::IntPoint>; using Circle = _Circle<Point>;
using Segment = _Segment<clppr::IntPoint>; using Segment = _Segment<Point>;
using MultiPolygon = TMultiShape<clppr::Polygon>; using MultiPolygon = ExPolygons;
// Summon the spatial indexing facilities from boost // Summon the spatial indexing facilities from boost
namespace bgi = boost::geometry::index; namespace bgi = boost::geometry::index;
@ -127,8 +126,8 @@ template<class TBin>
class AutoArranger { class AutoArranger {
public: public:
// Useful type shortcuts... // Useful type shortcuts...
using Placer = typename placers::_NofitPolyPlacer<clppr::Polygon, TBin>; using Placer = typename placers::_NofitPolyPlacer<ExPolygon, TBin>;
using Selector = selections::_FirstFitSelection<clppr::Polygon>; using Selector = selections::_FirstFitSelection<ExPolygon>;
using Packer = _Nester<Placer, Selector>; using Packer = _Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig; using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>; using Distance = TCoord<PointImpl>;
@ -168,7 +167,7 @@ protected:
// as it possibly can be but at the same time, it has to provide // as it possibly can be but at the same time, it has to provide
// reasonable results. // reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/> std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const Item &item, const clppr::IntPoint &bincenter) objfunc(const Item &item, const Point &bincenter)
{ {
const double bin_area = m_bin_area; const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree; const SpatIndex& spatindex = m_rtree;
@ -220,12 +219,12 @@ protected:
switch (compute_case) { switch (compute_case) {
case BIG_ITEM: { case BIG_ITEM: {
const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner const Point& minc = ibb.minCorner(); // bottom left corner
const clppr::IntPoint& maxc = ibb.maxCorner(); // top right corner const Point& maxc = ibb.maxCorner(); // top right corner
// top left and bottom right corners // top left and bottom right corners
clppr::IntPoint top_left{getX(minc), getY(maxc)}; Point top_left{getX(minc), getY(maxc)};
clppr::IntPoint bottom_right{getX(maxc), getY(minc)}; Point bottom_right{getX(maxc), getY(minc)};
// Now the distance of the gravity center will be calculated to the // Now the distance of the gravity center will be calculated to the
// five anchor points and the smallest will be chosen. // five anchor points and the smallest will be chosen.
@ -452,7 +451,7 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
// Specialization for a generalized polygon. // Specialization for a generalized polygon.
// Warning: this is unfinished business. It may or may not work. // Warning: this is unfinished business. It may or may not work.
template<> template<>
std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn() std::function<double(const Item &)> AutoArranger<ExPolygon>::get_objfn()
{ {
auto bincenter = sl::boundingBox(m_bin).center(); auto bincenter = sl::boundingBox(m_bin).center();
return [this, bincenter](const Item &item) { return [this, bincenter](const Item &item) {
@ -521,7 +520,7 @@ void _arrange(
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};} inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); } inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); } inline ExPolygon to_nestbin(const Polygon &p) { return ExPolygon{p}; }
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); } inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); } inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
@ -568,19 +567,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
const Vec2crd &offs = arrpoly.translation; const Vec2crd &offs = arrpoly.translation;
double rotation = arrpoly.rotation; double rotation = arrpoly.rotation;
if (p.is_counter_clockwise()) p.reverse();
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
// This fixes: // This fixes:
// https://github.com/prusa3d/PrusaSlicer/issues/2209 // https://github.com/prusa3d/PrusaSlicer/issues/2209
if (clpath.Contour.size() < 3) if (p.points.size() < 3)
return; return;
auto firstp = clpath.Contour.front(); outp.emplace_back(std::move(p));
clpath.Contour.emplace_back(firstp);
outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation); outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()}); outp.back().translation({offs.x(), offs.y()});
outp.back().binId(arrpoly.bed_idx); outp.back().binId(arrpoly.bed_idx);
@ -624,7 +616,7 @@ void arrange(ArrangePolygons & arrangables,
const BedT & bed, const BedT & bed,
const ArrangeParams & params) const ArrangeParams & params)
{ {
namespace clppr = ClipperLib; namespace clppr = Slic3r::ClipperLib;
std::vector<Item> items, fixeditems; std::vector<Item> items, fixeditems;
items.reserve(arrangables.size()); items.reserve(arrangables.size());
@ -643,8 +635,8 @@ void arrange(ArrangePolygons & arrangables,
_arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn); _arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
for(size_t i = 0; i < items.size(); ++i) { for(size_t i = 0; i < items.size(); ++i) {
clppr::IntPoint tr = items[i].translation(); Point tr = items[i].translation();
arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)}; arrangables[i].translation = {coord_t(tr.x()), coord_t(tr.y())};
arrangables[i].rotation = items[i].rotation(); arrangables[i].rotation = items[i].rotation();
arrangables[i].bed_idx = items[i].binId(); arrangables[i].bed_idx = items[i].binId();
} }

View file

@ -58,7 +58,7 @@ void BridgeDetector::initialize()
// detect anchors as intersection between our bridge expolygon and the lower slices // detect anchors as intersection between our bridge expolygon and the lower slices
// safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
this->_anchor_regions = intersection_ex(grown, to_polygons(this->lower_slices), true); this->_anchor_regions = intersection_ex(grown, union_safety_offset(this->lower_slices));
/* /*
if (0) { if (0) {
@ -227,29 +227,33 @@ void ExPolygon::get_trapezoids(ExPolygon clone, Polygons* polygons, double angle
// This algorithm may return more trapezoids than necessary // This algorithm may return more trapezoids than necessary
// (i.e. it may break a single trapezoid in several because // (i.e. it may break a single trapezoid in several because
// other parts of the object have x coordinates in the middle) // other parts of the object have x coordinates in the middle)
static void get_trapezoids2(const ExPolygon &expoly, Polygons* polygons) static void get_trapezoids2(const ExPolygon& expoly, Polygons* polygons)
{ {
Polygons src_polygons = to_polygons(expoly); Polygons src_polygons = to_polygons(expoly);
// get all points of this ExPolygon // get all points of this ExPolygon
const Points pp = to_points(src_polygons); const Points pp = to_points(src_polygons);
// build our bounding box // build our bounding box
BoundingBox bb(pp); BoundingBox bb(pp);
// get all x coordinates // get all x coordinates
std::vector<coord_t> xx; std::vector<coord_t> xx;
xx.reserve(pp.size()); xx.reserve(pp.size());
for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p)
xx.push_back(p->x()); xx.push_back(p->x());
std::sort(xx.begin(), xx.end()); std::sort(xx.begin(), xx.end());
// find trapezoids by looping from first to next-to-last coordinate // find trapezoids by looping from first to next-to-last coordinate
Polygons rectangle;
rectangle.emplace_back(Polygon());
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) { for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
coord_t next_x = *(x + 1); coord_t next_x = *(x + 1);
if (*x != next_x) if (*x != next_x) {
// intersect with rectangle // intersect with rectangle
// append results to return value // append results to return value
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, src_polygons)); rectangle.front() = { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } };
polygons_append(*polygons, intersection(rectangle, src_polygons));
}
} }
} }
@ -302,7 +306,7 @@ Polygons BridgeDetector::coverage(double angle) const
covered = union_(covered); covered = union_(covered);
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result. // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle)); polygons_rotate(covered, -(PI/2.0 - angle));
covered = intersection(covered, to_polygons(this->expolygons)); covered = intersection(this->expolygons, covered);
#if 0 #if 0
{ {
my @lines = map @{$_->lines}, @$trapezoids; my @lines = map @{$_->lines}, @$trapezoids;

View file

@ -78,7 +78,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
// Assign the maximum Z from four points. This values is valid index of the island // Assign the maximum Z from four points. This values is valid index of the island
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
}); });
// Add islands // Add islands
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true); clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
@ -90,9 +90,9 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
ConstPrintObjectPtrs top_level_objects_with_brim; ConstPrintObjectPtrs top_level_objects_with_brim;
for (int i = 0; i < islands_polytree.ChildCount(); ++i) { for (int i = 0; i < islands_polytree.ChildCount(); ++i) {
for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) { for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) {
if (point.Z != 0 && processed_objects_idx.find(island_to_object[point.Z - 1]->id().id) == processed_objects_idx.end()) { if (point.z() != 0 && processed_objects_idx.find(island_to_object[point.z() - 1]->id().id) == processed_objects_idx.end()) {
top_level_objects_with_brim.emplace_back(island_to_object[point.Z - 1]); top_level_objects_with_brim.emplace_back(island_to_object[point.z() - 1]);
processed_objects_idx.insert(island_to_object[point.Z - 1]->id().id); processed_objects_idx.insert(island_to_object[point.z() - 1]->id().id);
} }
} }
} }
@ -139,7 +139,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
Polygons no_brim_area_object; Polygons no_brim_area_object;
for (const ExPolygon &ex_poly : object->layers().front()->lslices) { for (const ExPolygon &ex_poly : object->layers().front()->lslices) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim) if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, offset(ex_poly.holes, -no_brim_offset)); append(no_brim_area_object, offset(ex_poly.holes, -no_brim_offset));
@ -156,7 +156,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
} }
} }
return diff_ex(to_polygons(std::move(brim_area)), no_brim_area); return diff_ex(brim_area, no_brim_area);
} }
static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset)
@ -183,14 +183,14 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs
if (top_outer_brim) if (top_outer_brim)
no_brim_area_object.emplace_back(ex_poly); no_brim_area_object.emplace_back(ex_poly);
else else
append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
} }
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset))); append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ex_poly.contour, no_brim_offset)); append(no_brim_area_object, to_expolygons(offset(ex_poly.contour, no_brim_offset)));
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset)); append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
@ -317,7 +317,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare); islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare);
} }
loops = union_pt_chained_outside_in(loops, false); loops = union_pt_chained_outside_in(loops);
std::reverse(loops.begin(), loops.end()); std::reverse(loops.begin(), loops.end());
extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()),
float(flow.width()), float(print.skirt_first_layer_height())); float(flow.width()), float(print.skirt_first_layer_height()));
@ -342,7 +342,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
poly.douglas_peucker(SCALED_RESOLUTION); poly.douglas_peucker(SCALED_RESOLUTION);
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
} }
loops = union_pt_chained_outside_in(loops, false); loops = union_pt_chained_outside_in(loops);
std::vector<Polylines> loops_pl_by_levels; std::vector<Polylines> loops_pl_by_levels;
{ {
@ -456,7 +456,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) { clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment // Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
// hat the Z coordinate not set to the contour coordinate. // hat the Z coordinate not set to the contour coordinate.
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
}); });
// add polygons // add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false); clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
@ -474,8 +474,8 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
for (const ClipperLib_Z::Path &path : loops_trimmed) { for (const ClipperLib_Z::Path &path : loops_trimmed) {
size_t input_idx = 0; size_t input_idx = 0;
for (const ClipperLib_Z::IntPoint &pt : path) for (const ClipperLib_Z::IntPoint &pt : path)
if (pt.Z > 0) { if (pt.z() > 0) {
input_idx = (size_t)pt.Z; input_idx = (size_t)pt.z();
break; break;
} }
assert(input_idx != 0); assert(input_idx != 0);
@ -492,14 +492,14 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
size_t j = i + 1; size_t j = i + 1;
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ; for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ;
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first; const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) {
auto *loop = new ExtrusionLoop(); auto *loop = new ExtrusionLoop();
brim.entities.emplace_back(loop); brim.entities.emplace_back(loop);
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points; Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size()); points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path) for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
i = j; i = j;
} else { } else {
//FIXME The path chaining here may not be optimal. //FIXME The path chaining here may not be optimal.
@ -511,7 +511,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points; Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size()); points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path) for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
} }
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt); chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size()); brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size());

View file

@ -23,6 +23,8 @@ add_library(libslic3r STATIC
BridgeDetector.hpp BridgeDetector.hpp
Brim.cpp Brim.cpp
Brim.hpp Brim.hpp
clipper.cpp
clipper.hpp
ClipperUtils.cpp ClipperUtils.cpp
ClipperUtils.hpp ClipperUtils.hpp
Config.cpp Config.cpp
@ -161,6 +163,7 @@ add_library(libslic3r STATIC
AppConfig.hpp AppConfig.hpp
Print.cpp Print.cpp
Print.hpp Print.hpp
PrintApply.cpp
PrintBase.cpp PrintBase.cpp
PrintBase.hpp PrintBase.hpp
PrintConfig.cpp PrintConfig.cpp

File diff suppressed because it is too large Load diff

View file

@ -8,234 +8,361 @@
#include "Surface.hpp" #include "Surface.hpp"
// import these wherever we're included // import these wherever we're included
using ClipperLib::jtMiter; using Slic3r::ClipperLib::jtMiter;
using ClipperLib::jtRound; using Slic3r::ClipperLib::jtRound;
using ClipperLib::jtSquare; using Slic3r::ClipperLib::jtSquare;
static constexpr const float ClipperSafetyOffset = 10.f;
enum class ApplySafetyOffset {
No,
Yes
};
#define CLIPPERUTILS_UNSAFE_OFFSET #define CLIPPERUTILS_UNSAFE_OFFSET
// #define CLIPPERUTILS_OFFSET_SCALE
#ifdef CLIPPERUTILS_OFFSET_SCALE
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
// which is optionally executed by other functions (union, intersection, diff).
// This scaling (cca 130t) is applied over the usual SCALING_FACTOR.
// By the way, is the scalling for offset needed at all?
// The reason to apply this scaling may be to match the resolution of the double mantissa.
#define CLIPPER_OFFSET_POWER_OF_2 17
// 2^17=131072
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE)
#endif // CLIPPERUTILS_OFFSET_SCALE
namespace Slic3r { namespace Slic3r {
//----------------------------------------------------------- namespace ClipperUtils {
// legacy code from Clipper documentation class PathsProviderIteratorBase {
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons); public:
Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree); using value_type = Points;
//----------------------------------------------------------- using difference_type = std::ptrdiff_t;
using pointer = const Points*;
using reference = const Points&;
using iterator_category = std::input_iterator_tag;
};
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); class EmptyPathsProvider {
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); public:
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const ExPolygons &input); struct iterator : public PathsProviderIteratorBase {
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); public:
Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input); const Points& operator*() { assert(false); return s_empty_points; }
Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input); // all iterators point to end.
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input); constexpr bool operator==(const iterator &rhs) const { return true; }
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input); constexpr bool operator!=(const iterator &rhs) const { return false; }
Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); const Points& operator++(int) { assert(false); return s_empty_points; }
const iterator& operator++() { assert(false); return *this; }
};
constexpr EmptyPathsProvider() {}
static constexpr iterator cend() throw() { return iterator{}; }
static constexpr iterator end() throw() { return cend(); }
static constexpr iterator cbegin() throw() { return cend(); }
static constexpr iterator begin() throw() { return cend(); }
static constexpr size_t size() throw() { return 0; }
static Points s_empty_points;
};
class SinglePathProvider {
public:
SinglePathProvider(const Points &points) : m_points(points) {}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(const Points &points) : m_ptr(&points) {}
const Points& operator*() const { return *m_ptr; }
bool operator==(const iterator &rhs) const { return m_ptr == rhs.m_ptr; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
const Points& operator++(int) { auto out = m_ptr; m_ptr = &s_end; return *out; }
iterator& operator++() { m_ptr = &s_end; return *this; }
private:
const Points *m_ptr;
};
iterator cbegin() const { return iterator(m_points); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(s_end); }
iterator end() const { return this->cend(); }
size_t size() const { return 1; }
private:
const Points &m_points;
static Points s_end;
};
template<typename MultiPointType>
class MultiPointsProvider {
public:
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(typename std::vector<MultiPointType>::const_iterator it) : m_it(it) {}
const Points& operator*() const { return m_it->points; }
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
const Points& operator++(int) { return (m_it ++)->points; }
iterator& operator++() { ++ m_it; return *this; }
private:
typename std::vector<MultiPointType>::const_iterator m_it;
};
iterator cbegin() const { return iterator(m_multipoints.begin()); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(m_multipoints.end()); }
iterator end() const { return this->cend(); }
size_t size() const { return m_multipoints.size(); }
private:
const std::vector<MultiPointType> &m_multipoints;
};
using PolygonsProvider = MultiPointsProvider<Polygon>;
using PolylinesProvider = MultiPointsProvider<Polyline>;
struct ExPolygonProvider {
ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(const ExPolygon &expoly, int idx) : m_expoly(expoly), m_idx(idx) {}
const Points& operator*() const { return (m_idx == 0) ? m_expoly.contour.points : m_expoly.holes[m_idx - 1].points; }
bool operator==(const iterator &rhs) const { assert(m_expoly == rhs.m_expoly); return m_idx == rhs.m_idx; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
const Points& operator++(int) { const Points &out = **this; ++ m_idx; return out; }
iterator& operator++() { ++ m_idx; return *this; }
private:
const ExPolygon &m_expoly;
int m_idx;
};
iterator cbegin() const { return iterator(m_expoly, 0); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(m_expoly, m_expoly.holes.size() + 1); }
iterator end() const { return this->cend(); }
size_t size() const { return m_expoly.holes.size() + 1; }
private:
const ExPolygon &m_expoly;
};
struct ExPolygonsProvider {
ExPolygonsProvider(const ExPolygons &expolygons) : m_expolygons(expolygons) {
m_size = 0;
for (const ExPolygon &expoly : expolygons)
m_size += expoly.holes.size() + 1;
}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(ExPolygons::const_iterator it) : m_it_expolygon(it), m_idx_contour(0) {}
const Points& operator*() const { return (m_idx_contour == 0) ? m_it_expolygon->contour.points : m_it_expolygon->holes[m_idx_contour - 1].points; }
bool operator==(const iterator &rhs) const { return m_it_expolygon == rhs.m_it_expolygon && m_idx_contour == rhs.m_idx_contour; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
iterator& operator++() {
if (++ m_idx_contour == m_it_expolygon->holes.size() + 1) {
++ m_it_expolygon;
m_idx_contour = 0;
}
return *this;
}
const Points& operator++(int) {
const Points &out = **this;
++ (*this);
return out;
}
private:
ExPolygons::const_iterator m_it_expolygon;
size_t m_idx_contour;
};
iterator cbegin() const { return iterator(m_expolygons.cbegin()); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(m_expolygons.cend()); }
iterator end() const { return this->cend(); }
size_t size() const { return m_size; }
private:
const ExPolygons &m_expolygons;
size_t m_size;
};
struct SurfacesProvider {
SurfacesProvider(const Surfaces &surfaces) : m_surfaces(surfaces) {
m_size = 0;
for (const Surface &surface : surfaces)
m_size += surface.expolygon.holes.size() + 1;
}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(Surfaces::const_iterator it) : m_it_surface(it), m_idx_contour(0) {}
const Points& operator*() const { return (m_idx_contour == 0) ? m_it_surface->expolygon.contour.points : m_it_surface->expolygon.holes[m_idx_contour - 1].points; }
bool operator==(const iterator &rhs) const { return m_it_surface == rhs.m_it_surface && m_idx_contour == rhs.m_idx_contour; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
iterator& operator++() {
if (++ m_idx_contour == m_it_surface->expolygon.holes.size() + 1) {
++ m_it_surface;
m_idx_contour = 0;
}
return *this;
}
const Points& operator++(int) {
const Points &out = **this;
++ (*this);
return out;
}
private:
Surfaces::const_iterator m_it_surface;
size_t m_idx_contour;
};
iterator cbegin() const { return iterator(m_surfaces.cbegin()); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(m_surfaces.cend()); }
iterator end() const { return this->cend(); }
size_t size() const { return m_size; }
private:
const Surfaces &m_surfaces;
size_t m_size;
};
struct SurfacesPtrProvider {
SurfacesPtrProvider(const SurfacesPtr &surfaces) : m_surfaces(surfaces) {
m_size = 0;
for (const Surface *surface : surfaces)
m_size += surface->expolygon.holes.size() + 1;
}
struct iterator : public PathsProviderIteratorBase {
public:
explicit iterator(SurfacesPtr::const_iterator it) : m_it_surface(it), m_idx_contour(0) {}
const Points& operator*() const { return (m_idx_contour == 0) ? (*m_it_surface)->expolygon.contour.points : (*m_it_surface)->expolygon.holes[m_idx_contour - 1].points; }
bool operator==(const iterator &rhs) const { return m_it_surface == rhs.m_it_surface && m_idx_contour == rhs.m_idx_contour; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
iterator& operator++() {
if (++ m_idx_contour == (*m_it_surface)->expolygon.holes.size() + 1) {
++ m_it_surface;
m_idx_contour = 0;
}
return *this;
}
const Points& operator++(int) {
const Points &out = **this;
++ (*this);
return out;
}
private:
SurfacesPtr::const_iterator m_it_surface;
size_t m_idx_contour;
};
iterator cbegin() const { return iterator(m_surfaces.cbegin()); }
iterator begin() const { return this->cbegin(); }
iterator cend() const { return iterator(m_surfaces.cend()); }
iterator end() const { return this->cend(); }
size_t size() const { return m_size; }
private:
const SurfacesPtr &m_surfaces;
size_t m_size;
};
}
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
// offset Polygons // offset Polygons
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#endif // CLIPPERUTILS_UNSAFE_OFFSET
// offset Polylines // offset Polylines
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3);
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3);
inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::Polygons union_safety_offset(const Slic3r::ExPolygons &expolygons);
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::ExPolygons &expolygons);
// offset expolygons and surfaces Slic3r::Polygons offset2(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET #ifdef CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
Slic3r::Polygons union_safety_offset(const Slic3r::Polygons &expolygons);
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::Polygons &polygons);
#endif // CLIPPERUTILS_UNSAFE_OFFSET #endif // CLIPPERUTILS_UNSAFE_OFFSET
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip);
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
#ifdef CLIPPERUTILS_UNSAFE_OFFSET // Safety offset is applied to the clipping polygons only.
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
double miterLimit = 3); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
double miterLimit = 3); Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
double miterLimit = 3); Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
#endif // CLIPPERUTILS_UNSAFE_OFFSET Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1, inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
// diff
inline Slic3r::Polygons
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{ {
return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_); return _clipper_ln(ClipperLib::ctDifference, subject, clip);
} }
inline Slic3r::ExPolygons // Safety offset is applied to the clipping polygons only.
diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
{ {
return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_); return _clipper_ln(ClipperLib::ctIntersection, subject, clip);
} }
inline Slic3r::ExPolygons inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip)
diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
inline Slic3r::Polygons
diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
inline Slic3r::Polylines
diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
inline Slic3r::Polylines
diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
inline Slic3r::Lines
diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
// intersection
inline Slic3r::Polygons
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
inline Slic3r::Polygons
intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{ {
Slic3r::Lines lines; Slic3r::Lines lines;
lines.emplace_back(subject); lines.emplace_back(subject);
return _clipper_ln(ClipperLib::ctIntersection, lines, clip, safety_offset_); return _clipper_ln(ClipperLib::ctIntersection, lines, clip);
} }
// union Slic3r::Polygons union_(const Slic3r::Polygons &subject);
inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset_ = false) Slic3r::Polygons union_(const Slic3r::ExPolygons &subject);
{ Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2);
return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject);
} Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject);
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject);
inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false) ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject);
{ ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject);
return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
}
inline Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false) Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject);
{
return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_offset_ = false);
ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false);
ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false);
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject, bool safety_offset_ = false);
ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes); ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);
@ -283,7 +410,7 @@ void traverse_pt(const ClipperLib::PolyNode *tree, Polygons *out)
if (!tree) return; // terminates recursion if (!tree) return; // terminates recursion
// Push the contour of the current level // Push the contour of the current level
out->emplace_back(ClipperPath_to_Slic3rPolygon(tree->Contour)); out->emplace_back(tree->Contour);
// Do the recursion for all the children. // Do the recursion for all the children.
traverse_pt<ordering>(tree->Childs, out); traverse_pt<ordering>(tree->Childs, out);
@ -302,13 +429,13 @@ void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *out)
} }
ExPolygon level; ExPolygon level;
level.contour = ClipperPath_to_Slic3rPolygon(tree->Contour); level.contour.points = tree->Contour;
foreach_node<ordering>(tree->Childs, foreach_node<ordering>(tree->Childs,
[out, &level] (const ClipperLib::PolyNode *node) { [out, &level] (const ClipperLib::PolyNode *node) {
// Holes are collected here. // Holes are collected here.
level.holes.emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); level.holes.emplace_back(node->Contour);
// By doing a recursion, a new level expoly is created with the contour // By doing a recursion, a new level expoly is created with the contour
// and holes of the lower level. Doing this for all the childs. // and holes of the lower level. Doing this for all the childs.
@ -331,8 +458,6 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
void safety_offset(ClipperLib::Paths* paths);
Polygons top_level_islands(const Slic3r::Polygons &polygons); Polygons top_level_islands(const Slic3r::Polygons &polygons);
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit); ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit);

View file

@ -471,8 +471,8 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
{ {
t_config_option_key opt_key = opt_key_src; t_config_option_key opt_key = opt_key_src;
std::string value = value_src; std::string value = value_src;
// Both opt_key and value may be modified by _handle_legacy(). // Both opt_key and value may be modified by handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by _handle_legacy(). // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
this->handle_legacy(opt_key, value); this->handle_legacy(opt_key, value);
if (opt_key.empty()) if (opt_key.empty())
// Ignore the option. // Ignore the option.

View file

@ -18,11 +18,53 @@
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/format/format_fwd.hpp> #include <boost/format/format_fwd.hpp>
#include <boost/functional/hash.hpp>
#include <boost/property_tree/ptree_fwd.hpp> #include <boost/property_tree/ptree_fwd.hpp>
#include <cereal/access.hpp> #include <cereal/access.hpp>
#include <cereal/types/base_class.hpp> #include <cereal/types/base_class.hpp>
namespace Slic3r {
struct FloatOrPercent
{
double value;
bool percent;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; }
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); }
}
namespace std {
template<> struct hash<Slic3r::FloatOrPercent> {
std::size_t operator()(const Slic3r::FloatOrPercent& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.value);
return v.percent ? seed ^ 0x9e3779b9 : seed;
}
};
template<> struct hash<Slic3r::Vec2d> {
std::size_t operator()(const Slic3r::Vec2d& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.x());
boost::hash_combine(seed, std::hash<double>{}(v.y()));
return seed;
}
};
template<> struct hash<Slic3r::Vec3d> {
std::size_t operator()(const Slic3r::Vec3d& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.x());
boost::hash_combine(seed, std::hash<double>{}(v.y()));
boost::hash_combine(seed, std::hash<double>{}(v.z()));
return seed;
}
};
}
namespace Slic3r { namespace Slic3r {
// Name of the configuration option. // Name of the configuration option.
@ -137,6 +179,7 @@ public:
virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); } virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); }
virtual bool operator==(const ConfigOption &rhs) const = 0; virtual bool operator==(const ConfigOption &rhs) const = 0;
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
virtual size_t hash() const throw() = 0;
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; } bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
bool is_vector() const { return ! this->is_scalar(); } bool is_vector() const { return ! this->is_scalar(); }
// If this option is nullable, then it may have its value or values set to nil. // If this option is nullable, then it may have its value or values set to nil.
@ -185,8 +228,10 @@ public:
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value; return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
} }
bool operator==(const T &rhs) const { return this->value == rhs; } bool operator==(const T &rhs) const throw() { return this->value == rhs; }
bool operator!=(const T &rhs) const { return this->value != rhs; } bool operator!=(const T &rhs) const throw() { return this->value != rhs; }
size_t hash() const throw() override { return std::hash<T>{}(this->value); }
private: private:
friend class cereal::access; friend class cereal::access;
@ -339,8 +384,16 @@ public:
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values; return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
} }
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; } bool operator==(const std::vector<T> &rhs) const throw() { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; } bool operator!=(const std::vector<T> &rhs) const throw() { return this->values != rhs; }
size_t hash() const throw() override {
std::hash<T> hasher;
size_t seed = 0;
for (const auto &v : this->values)
boost::hash_combine(seed, hasher(v));
return seed;
}
// Is this option overridden by another option? // Is this option overridden by another option?
// An option overrides another option if it is not nil and not equal. // An option overrides another option if it is not nil and not equal.
@ -413,7 +466,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
double getFloat() const override { return this->value; } double getFloat() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -454,7 +507,7 @@ public:
static ConfigOptionType static_type() { return coFloats; } static ConfigOptionType static_type() { return coFloats; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); }
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
@ -566,7 +619,7 @@ public:
int getInt() const override { return this->value; } int getInt() const override { return this->value; }
void setInt(int val) override { this->value = val; } void setInt(int val) override { this->value = val; }
ConfigOption* clone() const override { return new ConfigOptionInt(*this); } ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -606,7 +659,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); }
ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionIntsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->values == rhs.values; }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -689,7 +742,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionString(*this); } ConfigOption* clone() const override { return new ConfigOptionString(*this); }
ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionString &rhs) const throw() { return this->value == rhs.value; }
bool empty() const { return this->value.empty(); } bool empty() const { return this->value.empty(); }
std::string serialize() const override std::string serialize() const override
@ -722,7 +775,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; }
bool is_nil(size_t) const override { return false; } bool is_nil(size_t) const override { return false; }
std::string serialize() const override std::string serialize() const override
@ -757,7 +810,8 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPercent &rhs) const throw() { return this->value == rhs.value; }
double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; } double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
std::string serialize() const override std::string serialize() const override
@ -796,8 +850,8 @@ public:
static ConfigOptionType static_type() { return coPercents; } static ConfigOptionType static_type() { return coPercents; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); }
ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercentsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionPercentsTempl &rhs) const throw() { return this->values == rhs.values; }
std::string serialize() const override std::string serialize() const override
{ {
@ -856,8 +910,12 @@ public:
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs)); assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs); return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
} }
bool operator==(const ConfigOptionFloatOrPercent &rhs) const bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw()
{ return this->value == rhs.value && this->percent == rhs.percent; } { return this->value == rhs.value && this->percent == rhs.percent; }
size_t hash() const throw() override {
size_t seed = std::hash<double>{}(this->value);
return this->percent ? seed ^ 0x9e3779b9 : seed;
}
double get_abs_value(double ratio_over) const double get_abs_value(double ratio_over) const
{ return this->percent ? (ratio_over * this->value / 100) : this->value; } { return this->percent ? (ratio_over * this->value / 100) : this->value; }
@ -891,27 +949,6 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); } template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
}; };
struct FloatOrPercent
{
double value;
bool percent;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
{
return l.value == r.value && l.percent == r.percent;
}
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
{
return !(l == r);
}
template<bool NULLABLE> template<bool NULLABLE>
class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent> class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
{ {
@ -925,13 +962,14 @@ public:
static ConfigOptionType static_type() { return coFloatsOrPercents; } static ConfigOptionType static_type() { return coFloatsOrPercents; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)); assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values); return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
} }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -1038,7 +1076,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPoint &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1074,7 +1112,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; }
bool is_nil(size_t) const override { return false; } bool is_nil(size_t) const override { return false; }
std::string serialize() const override std::string serialize() const override
@ -1146,7 +1184,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); } ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPoint3 &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1183,7 +1221,7 @@ public:
bool getBool() const override { return this->value; } bool getBool() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1217,7 +1255,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); }
ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBoolsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionBoolsTempl &rhs) const throw() { return this->values == rhs.values; }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -1311,7 +1349,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); } ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionEnum<T> &rhs) const throw() { return this->value == rhs.value; }
int getInt() const override { return (int)this->value; } int getInt() const override { return (int)this->value; }
void setInt(int val) override { this->value = T(val); } void setInt(int val) override { this->value = T(val); }
@ -1352,22 +1390,7 @@ public:
} }
// Map from an enum name to an enum integer value. // Map from an enum name to an enum integer value.
static const t_config_enum_names& get_enum_names() static const t_config_enum_names& get_enum_names();
{
static t_config_enum_names names;
if (names.empty()) {
// Initialize the map.
const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
int cnt = 0;
for (const auto& kvp : enum_keys_map)
cnt = std::max(cnt, kvp.second);
cnt += 1;
names.assign(cnt, "");
for (const auto& kvp : enum_keys_map)
names[kvp.second] = kvp.first;
}
return names;
}
// Map from an enum name to an enum integer value. // Map from an enum name to an enum integer value.
static const t_config_enum_values& get_enum_values(); static const t_config_enum_values& get_enum_values();
@ -1397,7 +1420,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); } ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionEnumGeneric &rhs) const throw() { return this->value == rhs.value; }
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {

View file

@ -546,7 +546,7 @@ bool EdgeGrid::Grid::inside(const Point &pt_src)
return false; return false;
coord_t ix = p(0) / m_resolution; coord_t ix = p(0) / m_resolution;
coord_t iy = p(1) / m_resolution; coord_t iy = p(1) / m_resolution;
if (ix >= this->m_cols || iy >= this->m_rows) if (ix >= m_cols || iy >= m_rows)
return false; return false;
size_t i_closest = (size_t)-1; size_t i_closest = (size_t)-1;

View file

@ -83,8 +83,8 @@ inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.
inline size_t number_polygons(const ExPolygons &expolys) inline size_t number_polygons(const ExPolygons &expolys)
{ {
size_t n_polygons = 0; size_t n_polygons = 0;
for (ExPolygons::const_iterator it = expolys.begin(); it != expolys.end(); ++ it) for (const ExPolygon &ex : expolys)
n_polygons += it->holes.size() + 1; n_polygons += ex.holes.size() + 1;
return n_polygons; return n_polygons;
} }
@ -360,6 +360,8 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
extern bool remove_sticks(ExPolygon &poly); extern bool remove_sticks(ExPolygon &poly);
extern void keep_largest_contour_only(ExPolygons &polygons); extern void keep_largest_contour_only(ExPolygons &polygons);
inline double area(const ExPolygon &poly) { return poly.area(); }
inline double area(const ExPolygons &polys) inline double area(const ExPolygons &polys)
{ {
double s = 0.; double s = 0.;

View file

@ -10,10 +10,6 @@
namespace Slic3r { namespace Slic3r {
// Borrowed from C++20
template<class T>
using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
// Override for valid execution policies // Override for valid execution policies
template<class EP> struct IsExecutionPolicy_ : public std::false_type {}; template<class EP> struct IsExecutionPolicy_ : public std::false_type {};

View file

@ -14,12 +14,12 @@ namespace Slic3r {
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
{ {
this->_inflate_collection(intersection_pl((Polylines)polyline, to_polygons(collection.expolygons)), retval); this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection.expolygons), retval);
} }
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
{ {
this->_inflate_collection(diff_pl((Polylines)this->polyline, to_polygons(collection.expolygons)), retval); this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection.expolygons), retval);
} }
void ExtrusionPath::clip_end(double distance) void ExtrusionPath::clip_end(double distance)
@ -318,7 +318,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
case erIroning : return L("Ironing"); case erIroning : return L("Ironing");
case erBridgeInfill : return L("Bridge infill"); case erBridgeInfill : return L("Bridge infill");
case erGapFill : return L("Gap fill"); case erGapFill : return L("Gap fill");
case erSkirt : return L("Skirt"); case erSkirt : return L("Skirt/Brim");
case erSupportMaterial : return L("Support material"); case erSupportMaterial : return L("Support material");
case erSupportMaterialInterface : return L("Support material interface"); case erSupportMaterialInterface : return L("Support material interface");
case erWipeTower : return L("Wipe tower"); case erWipeTower : return L("Wipe tower");
@ -349,7 +349,7 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
return erBridgeInfill; return erBridgeInfill;
else if (role == L("Gap fill")) else if (role == L("Gap fill"))
return erGapFill; return erGapFill;
else if (role == L("Skirt")) else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier
return erSkirt; return erSkirt;
else if (role == L("Support material")) else if (role == L("Support material"))
return erSupportMaterial; return erSupportMaterial;

View file

@ -122,10 +122,10 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
if (surface.surface_type == stInternalVoid) if (surface.surface_type == stInternalVoid)
has_internal_voids = true; has_internal_voids = true;
else { else {
const PrintRegionConfig &region_config = layerm.region()->config(); const PrintRegionConfig &region_config = layerm.region().config();
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
bool is_bridge = layer.id() > 0 && surface.is_bridge(); bool is_bridge = layer.id() > 0 && surface.is_bridge();
params.extruder = layerm.region()->extruder(extrusion_role); params.extruder = layerm.region().extruder(extrusion_role);
params.pattern = region_config.fill_pattern.value; params.pattern = region_config.fill_pattern.value;
params.density = float(region_config.fill_density); params.density = float(region_config.fill_density);
@ -160,11 +160,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.anchor_length = 1000.f; params.anchor_length = 1000.f;
params.anchor_length_max = 1000.f; params.anchor_length_max = 1000.f;
} else { } else {
// it's internal infill, so we can calculate a generic flow spacing // Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status,
// for all layers, for avoiding the ugly effect of // so that internall infill will be aligned over all layers of the current region.
// misaligned infill on first layer because of different extrusion width and params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
// layer height
params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing();
// Anchor a sparse infill to inner perimeters with the following anchor length: // Anchor a sparse infill to inner perimeters with the following anchor length:
params.anchor_length = float(region_config.infill_anchor); params.anchor_length = float(region_config.infill_anchor);
if (region_config.infill_anchor.percent) if (region_config.infill_anchor.percent)
@ -213,7 +211,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
Polygons polys = to_polygons(std::move(fill.expolygons)); Polygons polys = to_polygons(std::move(fill.expolygons));
// Make a union of polygons, use a safety offset, subtract the preceding polygons. // Make a union of polygons, use a safety offset, subtract the preceding polygons.
// Bridges are processed first (see SurfaceFill::operator<()) // Bridges are processed first (see SurfaceFill::operator<())
fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true); fill.expolygons = all_polygons.empty() ? union_safety_offset_ex(polys) : diff_ex(polys, all_polygons, ApplySafetyOffset::Yes);
append(all_polygons, std::move(polys)); append(all_polygons, std::move(polys));
} else if (&fill != &surface_fills.back()) } else if (&fill != &surface_fills.back())
append(all_polygons, to_polygons(fill.expolygons)); append(all_polygons, to_polygons(fill.expolygons));
@ -254,12 +252,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
// Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2 // Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2
Polygons collapsed = diff( Polygons collapsed = diff(
surfaces_polygons, surfaces_polygons,
offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2), offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2 + ClipperSafetyOffset));
true);
//FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being //FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being
// added if two offsetted void regions merge. // added if two offsetted void regions merge.
// polygons_append(voids, collapsed); // polygons_append(voids, collapsed);
ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true); ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, ApplySafetyOffset::Yes);
// Now find an internal infill SurfaceFill to add these extrusions to. // Now find an internal infill SurfaceFill to add these extrusions to.
SurfaceFill *internal_solid_fill = nullptr; SurfaceFill *internal_solid_fill = nullptr;
unsigned int region_id = 0; unsigned int region_id = 0;
@ -277,11 +274,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
} }
if (internal_solid_fill == nullptr) { if (internal_solid_fill == nullptr) {
// Produce another solid fill. // Produce another solid fill.
params.extruder = layerm.region()->extruder(frSolidInfill); params.extruder = layerm.region().extruder(frSolidInfill);
params.pattern = layerm.region()->config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; params.pattern = layerm.region().config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
params.density = 100.f; params.density = 100.f;
params.extrusion_role = erInternalInfill; params.extrusion_role = erInternalInfill;
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value));
// calculate the actual flow we'll be using for this infill // calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill); params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing(); params.spacing = params.flow.spacing();
@ -504,7 +501,7 @@ void Layer::make_ironing()
for (LayerRegion *layerm : m_regions) for (LayerRegion *layerm : m_regions)
if (! layerm->slices.empty()) { if (! layerm->slices.empty()) {
IroningParams ironing_params; IroningParams ironing_params;
const PrintRegionConfig &config = layerm->region()->config(); const PrintRegionConfig &config = layerm->region().config();
if (config.ironing && if (config.ironing &&
(config.ironing_type == IroningType::AllSolid || (config.ironing_type == IroningType::AllSolid ||
(config.top_solid_layers > 0 && (config.top_solid_layers > 0 &&
@ -559,7 +556,7 @@ void Layer::make_ironing()
Polygons infills; Polygons infills;
for (size_t k = i; k < j; ++ k) { for (size_t k = i; k < j; ++ k) {
const IroningParams &ironing_params = by_extruder[k]; const IroningParams &ironing_params = by_extruder[k];
const PrintRegionConfig &region_config = ironing_params.layerm->region()->config(); const PrintRegionConfig &region_config = ironing_params.layerm->region().config();
bool iron_everything = region_config.ironing_type == IroningType::AllSolid; bool iron_everything = region_config.ironing_type == IroningType::AllSolid;
bool iron_completely = iron_everything; bool iron_completely = iron_everything;
if (iron_everything) { if (iron_everything) {
@ -596,7 +593,7 @@ void Layer::make_ironing()
// For IroningType::AllSolid only: // For IroningType::AllSolid only:
// Add solid infill areas for layers, that contain some non-ironable infil (sparse infill, bridge infill). // Add solid infill areas for layers, that contain some non-ironable infil (sparse infill, bridge infill).
append(infills, to_polygons(std::move(ironing_areas))); append(infills, to_polygons(std::move(ironing_areas)));
ironing_areas = union_ex(infills, true); ironing_areas = union_safety_offset_ex(infills);
} }
} }

View file

@ -162,7 +162,7 @@ void Fill3DHoneycomb::_fill_surface_single(
pl.translate(bb.min); pl.translate(bb.min);
// clip pattern to boundaries, chain the clipped polylines // clip pattern to boundaries, chain the clipped polylines
polylines = intersection_pl(polylines, to_polygons(expolygon)); polylines = intersection_pl(polylines, expolygon);
// connect lines if needed // connect lines if needed
if (params.dont_connect() || polylines.size() <= 1) if (params.dont_connect() || polylines.size() <= 1)

View file

@ -291,13 +291,13 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
double extrusion_width; double extrusion_width;
}; };
std::vector<RegionFillData> region_fill_data; std::vector<RegionFillData> region_fill_data;
region_fill_data.reserve(print_object.print()->regions().size()); region_fill_data.reserve(print_object.num_printing_regions());
bool build_octree = false; bool build_octree = false;
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values; const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end()); double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
for (const PrintRegion *region : print_object.print()->regions()) { for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegionConfig &config = region->config(); const PrintRegionConfig &config = print_object.printing_region(region_id).config();
bool nonempty = config.fill_density > 0; bool nonempty = config.fill_density > 0;
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic; bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic; bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
@ -1368,7 +1368,7 @@ void Filler::_fill_surface_single(
all_polylines.reserve(lines.size()); all_polylines.reserve(lines.size());
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; }); std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
// Crop all polylines // Crop all polylines
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon)); all_polylines = intersection_pl(std::move(all_polylines), expolygon);
#endif #endif
} }

View file

@ -595,7 +595,6 @@ static inline bool line_rounded_thick_segment_collision(
// Very short line vector. Just test whether the center point is inside the offset line. // Very short line vector. Just test whether the center point is inside the offset line.
Vec2d lpt = 0.5 * (line_a + line_b); Vec2d lpt = 0.5 * (line_a + line_b);
if (segment_l > SCALED_EPSILON) { if (segment_l > SCALED_EPSILON) {
struct Linef { Vec2d a, b; };
intersects = line_alg::distance_to_squared(Linef{ segment_a, segment_b }, lpt) < offset2; intersects = line_alg::distance_to_squared(Linef{ segment_a, segment_b }, lpt) < offset2;
} else } else
intersects = (0.5 * (segment_a + segment_b) - lpt).squaredNorm() < offset2; intersects = (0.5 * (segment_a + segment_b) - lpt).squaredNorm() < offset2;
@ -1196,8 +1195,6 @@ static inline void mark_boundary_segments_overlapping_infill(
// Spacing (width) of the infill lines. // Spacing (width) of the infill lines.
const double spacing) const double spacing)
{ {
struct Linef { Vec2d a; Vec2d b; };
for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) { for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
const Points &contour = graph.boundary[cp.contour_idx]; const Points &contour = graph.boundary[cp.contour_idx];
const std::vector<double> &contour_params = graph.boundary_params[cp.contour_idx]; const std::vector<double> &contour_params = graph.boundary_params[cp.contour_idx];
@ -2003,9 +2000,8 @@ static double evaluate_support_arch_cost(const Polyline &pl)
double dmax = 0; double dmax = 0;
// Maximum distance in Y axis out of the (ymin, ymax) band and from the (front, back) line. // Maximum distance in Y axis out of the (ymin, ymax) band and from the (front, back) line.
struct Linef { Vec2d a, b; };
Linef line { front.cast<double>(), back.cast<double>() }; Linef line { front.cast<double>(), back.cast<double>() };
for (const Point pt : pl.points) for (const Point &pt : pl.points)
dmax = std::max<double>(std::max(dmax, line_alg::distance_to(line, Vec2d(pt.cast<double>()))), std::max(pt.y() - ymax, ymin - pt.y())); dmax = std::max<double>(std::max(dmax, line_alg::distance_to(line, Vec2d(pt.cast<double>()))), std::max(pt.y() - ymax, ymin - pt.y()));
return dmax; return dmax;
} }

View file

@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single(
// generate paths from the outermost to the innermost, to avoid // generate paths from the outermost to the innermost, to avoid
// adhesion problems of the first central tiny loops // adhesion problems of the first central tiny loops
loops = union_pt_chained_outside_in(loops, false); loops = union_pt_chained_outside_in(loops);
// split paths using a nearest neighbor search // split paths using a nearest neighbor search
size_t iPathFirst = polylines_out.size(); size_t iPathFirst = polylines_out.size();

View file

@ -180,7 +180,7 @@ void FillGyroid::_fill_surface_single(
for (Polyline &pl : polylines) for (Polyline &pl : polylines)
pl.translate(bb.min); pl.translate(bb.min);
polylines = intersection_pl(polylines, to_polygons(expolygon)); polylines = intersection_pl(polylines, expolygon);
if (! polylines.empty()) { if (! polylines.empty()) {
// Remove very small bits, but be careful to not remove infill lines connecting thin walls! // Remove very small bits, but be careful to not remove infill lines connecting thin walls!

View file

@ -73,7 +73,7 @@ void FillHoneycomb::_fill_surface_single(
} }
} }
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon)); all_polylines = intersection_pl(std::move(all_polylines), expolygon);
if (params.dont_connect() || all_polylines.size() <= 1) if (params.dont_connect() || all_polylines.size() <= 1)
append(polylines_out, chain_polylines(std::move(all_polylines))); append(polylines_out, chain_polylines(std::move(all_polylines)));
else else

View file

@ -58,7 +58,7 @@ void FillLine::_fill_surface_single(
pts.push_back(it->a); pts.push_back(it->a);
pts.push_back(it->b); pts.push_back(it->b);
} }
Polylines polylines = intersection_pl(polylines_src, offset(expolygon, scale_(0.02)), false); Polylines polylines = intersection_pl(polylines_src, offset(expolygon, scale_(0.02)));
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines! // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
const float INFILL_OVERLAP_OVER_SPACING = 0.3f; const float INFILL_OVERLAP_OVER_SPACING = 0.3f;

View file

@ -33,18 +33,16 @@ void FillPlanePath::_fill_surface_single(
coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)), coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines))); coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)));
Polylines polylines;
if (pts.size() >= 2) { if (pts.size() >= 2) {
// Convert points to a polyline, upscale. // Convert points to a polyline, upscale.
polylines.push_back(Polyline()); Polylines polylines(1, Polyline());
Polyline &polyline = polylines.back(); Polyline &polyline = polylines.front();
polyline.points.reserve(pts.size()); polyline.points.reserve(pts.size());
for (const Vec2d &pt : pts) for (const Vec2d &pt : pts)
polyline.points.push_back(Point( polyline.points.emplace_back(
coord_t(floor(pt.x() * distance_between_lines + 0.5)), coord_t(floor(pt.x() * distance_between_lines + 0.5)),
coord_t(floor(pt.y() * distance_between_lines + 0.5)))); coord_t(floor(pt.y() * distance_between_lines + 0.5)));
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines); polylines = intersection_pl(polylines, expolygon);
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
Polylines chained; Polylines chained;
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1) if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
chained = chain_polylines(std::move(polylines)); chained = chain_polylines(std::move(polylines));

View file

@ -89,18 +89,11 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
if (opt->percent) { if (opt->percent) {
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height"; auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
auto opt_layer_height = config.option(opt_key_layer_height); auto opt_layer_height = config.option(opt_key_layer_height);
if (opt_layer_height == nullptr) if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, opt_key_layer_height); throw_on_missing_variable(opt_key, opt_key_layer_height);
double layer_height = opt_layer_height->getFloat(); assert(! first_layer || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent);
if (first_layer && static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent) { return opt->get_abs_value(opt_layer_height->getFloat());
// first_layer_height depends on layer_height.
opt_layer_height = config.option("layer_height");
if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, "layer_height");
layer_height *= 0.01 * opt_layer_height->getFloat();
}
return opt->get_abs_value(layer_height);
} }
if (opt->value == 0.) { if (opt->value == 0.) {
@ -238,13 +231,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{ {
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; const PrintConfig &print_config = object->print()->config();
const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width;
return Flow::new_from_config_width( return Flow::new_from_config_width(
frSupportMaterial, frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config().extrusion_width, (width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value))); (layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value)));
} }
Flow support_material_interface_flow(const PrintObject *object, float layer_height) Flow support_material_interface_flow(const PrintObject *object, float layer_height)

View file

@ -248,7 +248,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
{ {
double incr, val, prev; double incr, val, prev;
bool stop = false; bool stop = false;
tbb::spin_mutex mutex; tbb::spin_mutex mutex = {};
} st {100. / slices.size(), 0., 0.}; } st {100. / slices.size(), 0., 0.};
tbb::parallel_for(size_t(0), arch.images.size(), tbb::parallel_for(size_t(0), arch.images.size(),
@ -371,6 +371,13 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
m["numSlow"] = std::to_string(stats.slow_layers_count); m["numSlow"] = std::to_string(stats.slow_layers_count);
m["numFast"] = std::to_string(stats.fast_layers_count); m["numFast"] = std::to_string(stats.fast_layers_count);
m["printTime"] = std::to_string(stats.estimated_print_time); m["printTime"] = std::to_string(stats.estimated_print_time);
bool hollow_en = false;
auto it = print.objects().begin();
while (!hollow_en && it != print.objects().end())
hollow_en = (*it++)->config().hollowing_enable;
m["hollow"] = hollow_en ? "1" : "0";
m["action"] = "print"; m["action"] = "print";
} }

View file

@ -611,12 +611,47 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
// free functions called by GCode::do_export() // free functions called by GCode::do_export()
namespace DoExport { namespace DoExport {
static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) // static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
// {
// const GCodeProcessor::Result& result = processor.get_result();
// print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
// print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
// get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
// }
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
{ {
const GCodeProcessor::Result& result = processor.get_result(); const GCodeProcessor::Result& result = processor.get_result();
print_statistics.estimated_normal_print_time = get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time); print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ? print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A"; get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
// update filament statictics
double total_extruded_volume = 0.0;
double total_used_filament = 0.0;
double total_weight = 0.0;
double total_cost = 0.0;
for (auto volume : result.print_statistics.volumes_per_extruder) {
total_extruded_volume += volume.second;
size_t extruder_id = volume.first;
auto extruder = std::find_if(extruders.begin(), extruders.end(), [extruder_id](const Extruder& extr) { return extr.id() == extruder_id; });
if (extruder == extruders.end())
continue;
double s = PI * sqr(0.5* extruder->filament_diameter());
double weight = volume.second * extruder->filament_density() * 0.001;
total_used_filament += volume.second/s;
total_weight += weight;
total_cost += weight * extruder->filament_cost() * 0.001;
}
print_statistics.total_extruded_volume = total_extruded_volume;
print_statistics.total_used_filament = total_used_filament;
print_statistics.total_weight = total_weight;
print_statistics.total_cost = total_cost;
print_statistics.filament_stats = result.print_statistics.volumes_per_extruder;
} }
#if ENABLE_VALIDATE_CUSTOM_GCODE #if ENABLE_VALIDATE_CUSTOM_GCODE
@ -754,7 +789,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); }); m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
#if ENABLE_GCODE_WINDOW #if ENABLE_GCODE_WINDOW
if (result != nullptr) { if (result != nullptr) {
*result = std::move(m_processor.extract_result()); *result = std::move(m_processor.extract_result());
@ -796,19 +832,19 @@ namespace DoExport {
// get the minimum cross-section used in the print // get the minimum cross-section used in the print
std::vector<double> mm3_per_mm; std::vector<double> mm3_per_mm;
for (auto object : print.objects()) { for (auto object : print.objects()) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) {
const PrintRegion* region = print.regions()[region_id]; const PrintRegion &region = object->printing_region(region_id);
for (auto layer : object->layers()) { for (auto layer : object->layers()) {
const LayerRegion* layerm = layer->regions()[region_id]; const LayerRegion* layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed") == 0 || if (region.config().get_abs_value("perimeter_speed") == 0 ||
region->config().get_abs_value("small_perimeter_speed") == 0 || region.config().get_abs_value("small_perimeter_speed") == 0 ||
region->config().get_abs_value("external_perimeter_speed") == 0 || region.config().get_abs_value("external_perimeter_speed") == 0 ||
region->config().get_abs_value("bridge_speed") == 0) region.config().get_abs_value("bridge_speed") == 0)
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
if (region->config().get_abs_value("infill_speed") == 0 || if (region.config().get_abs_value("infill_speed") == 0 ||
region->config().get_abs_value("solid_infill_speed") == 0 || region.config().get_abs_value("solid_infill_speed") == 0 ||
region->config().get_abs_value("top_solid_infill_speed") == 0 || region.config().get_abs_value("top_solid_infill_speed") == 0 ||
region->config().get_abs_value("bridge_speed") == 0) region.config().get_abs_value("bridge_speed") == 0)
{ {
// Minimal volumetric flow should not be calculated over ironing extrusions. // Minimal volumetric flow should not be calculated over ironing extrusions.
// Use following lambda instead of the built-it method. // Use following lambda instead of the built-it method.
@ -887,8 +923,7 @@ namespace DoExport {
if (thumbnail_cb != nullptr) if (thumbnail_cb != nullptr)
{ {
const size_t max_row_length = 78; const size_t max_row_length = 78;
ThumbnailsList thumbnails; ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true });
thumbnail_cb(thumbnails, sizes, true, true, true, true);
for (const ThumbnailData& data : thumbnails) for (const ThumbnailData& data : thumbnails)
{ {
if (data.is_valid()) if (data.is_valid())
@ -958,7 +993,6 @@ namespace DoExport {
dst.first += buf; dst.first += buf;
++ dst.second; ++ dst.second;
}; };
print_statistics.filament_stats.insert(std::pair<size_t, float>{extruder.id(), (float)used_filament});
append(out_filament_used_mm, "%.2lf", used_filament); append(out_filament_used_mm, "%.2lf", used_filament);
append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
if (filament_weight > 0.) { if (filament_weight > 0.) {
@ -1111,17 +1145,19 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Write some terse information on the slicing parameters. // Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects().front(); const PrintObject *first_object = print.objects().front();
const double layer_height = first_object->config().layer_height.value; const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height); assert(! print.config().first_layer_height.percent);
for (const PrintRegion* region : print.regions()) { const double first_layer_height = print.config().first_layer_height.value;
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width()); for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width()); const PrintRegion &region = print.get_print_region(region_id);
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width()); _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width()); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width()); _write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
_write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
if (print.has_support_material()) if (print.has_support_material())
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
if (print.config().first_layer_extrusion_width.value > 0) if (print.config().first_layer_extrusion_width.value > 0)
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width()); _write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
_write_format(file, "\n"); _write_format(file, "\n");
} }
print.throw_if_canceled(); print.throw_if_canceled();
@ -1935,7 +1971,7 @@ void GCode::process_layer(
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt()); bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) { if (enable) {
for (const LayerRegion *layer_region : layer.regions()) for (const LayerRegion *layer_region : layer.regions())
if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() || if (size_t(layer_region->region().config().bottom_solid_layers.value) > layer.id() ||
layer_region->perimeters.items_count() > 1u || layer_region->perimeters.items_count() > 1u ||
layer_region->fills.items_count() > 0) { layer_region->fills.items_count() > 0) {
enable = false; enable = false;
@ -2109,7 +2145,9 @@ void GCode::process_layer(
const LayerRegion *layerm = layer.regions()[region_id]; const LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr) if (layerm == nullptr)
continue; continue;
const PrintRegion &region = *print.regions()[region_id]; // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not
// identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion.
const PrintRegion &region = print.get_print_region(layerm->region().print_region_id());
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map. // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
// It is also necessary to save which extrusions are part of MM wiping and which are not. // It is also necessary to save which extrusions are part of MM wiping and which are not.
@ -2167,8 +2205,8 @@ void GCode::process_layer(
// extrusions->first_point fits inside ith slice // extrusions->first_point fits inside ith slice
point_inside_surface(island_idx, extrusions->first_point())) { point_inside_surface(island_idx, extrusions->first_point())) {
if (islands[island_idx].by_region.empty()) if (islands[island_idx].by_region.empty())
islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides); islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides);
break; break;
} }
} }
@ -2570,7 +2608,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
std::string gcode; std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) for (const ObjectByExtruder::Island::Region &region : by_region)
if (! region.perimeters.empty()) { if (! region.perimeters.empty()) {
m_config.apply(print.regions()[&region - &by_region.front()]->config()); m_config.apply(print.get_print_region(&region - &by_region.front()).config());
for (const ExtrusionEntity *ee : region.perimeters) for (const ExtrusionEntity *ee : region.perimeters)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
} }
@ -2591,7 +2629,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
if ((ee->role() == erIroning) == ironing) if ((ee->role() == erIroning) == ironing)
extrusions.emplace_back(ee); extrusions.emplace_back(ee);
if (! extrusions.empty()) { if (! extrusions.empty()) {
m_config.apply(print.regions()[&region - &by_region.front()]->config()); m_config.apply(print.get_print_region(&region - &by_region.front()).config());
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
for (const ExtrusionEntity *fill : extrusions) { for (const ExtrusionEntity *fill : extrusions) {
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill); auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);

View file

@ -326,7 +326,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
const char *line_start = gcode.c_str(); const char *line_start = gcode.c_str();
const char *line_end = line_start; const char *line_end = line_start;
const char extrusion_axis = config.get_extrusion_axis()[0]; const char extrusion_axis = get_extrusion_axis(config)[0];
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
// for a sequence of extrusion moves. // for a sequence of extrusion moves.
size_t active_speed_modifier = size_t(-1); size_t active_speed_modifier = size_t(-1);

View file

@ -186,6 +186,72 @@ void GCodeProcessor::TimeMachine::CustomGCodeTime::reset()
times = std::vector<std::pair<CustomGCode::Type, float>>(); times = std::vector<std::pair<CustomGCode::Type, float>>();
} }
void GCodeProcessor::UsedFilaments::reset()
{
color_change_cache = 0.0f;
volumes_per_color_change = std::vector<double>();
tool_change_cache = 0.0f;
volumes_per_extruder.clear();
role_cache = 0.0f;
filaments_per_role.clear();
}
void GCodeProcessor::UsedFilaments::increase_caches(double extruded_volume)
{
color_change_cache += extruded_volume;
tool_change_cache += extruded_volume;
role_cache += extruded_volume;
}
void GCodeProcessor::UsedFilaments::process_color_change_cache()
{
if (color_change_cache != 0.0f) {
volumes_per_color_change.push_back(color_change_cache);
color_change_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_extruder_cache(GCodeProcessor* processor)
{
size_t active_extruder_id = processor->m_extruder_id;
if (tool_change_cache != 0.0f) {
if (volumes_per_extruder.find(active_extruder_id) != volumes_per_extruder.end())
volumes_per_extruder[active_extruder_id] += tool_change_cache;
else
volumes_per_extruder[active_extruder_id] = tool_change_cache;
tool_change_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor)
{
if (role_cache != 0.0f) {
std::pair<double, double> filament = { 0.0f, 0.0f };
double s = PI * sqr(0.5 * processor->m_filament_diameters[processor->m_extruder_id]);
filament.first = role_cache/s * 0.001;
filament.second = role_cache * processor->m_filament_densities[processor->m_extruder_id] * 0.001;
ExtrusionRole active_role = processor->m_extrusion_role;
if (filaments_per_role.find(active_role) != filaments_per_role.end()) {
filaments_per_role[active_role].first += filament.first;
filaments_per_role[active_role].second += filament.second;
}
else
filaments_per_role[active_role] = filament;
role_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
{
process_color_change_cache();
process_extruder_cache(processor);
process_role_cache(processor);
}
void GCodeProcessor::TimeMachine::reset() void GCodeProcessor::TimeMachine::reset()
{ {
enabled = false; enabled = false;
@ -348,10 +414,10 @@ void GCodeProcessor::TimeProcessor::reset()
machine_limits = MachineEnvelopeConfig(); machine_limits = MachineEnvelopeConfig();
filament_load_times = std::vector<float>(); filament_load_times = std::vector<float>();
filament_unload_times = std::vector<float>(); filament_unload_times = std::vector<float>();
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
machines[i].reset(); machines[i].reset();
} }
machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].enabled = true; machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
} }
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
@ -416,19 +482,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
size_t g1_lines_counter = 0; size_t g1_lines_counter = 0;
// keeps track of last exported pair <percent, remaining time> // keeps track of last exported pair <percent, remaining time>
#if ENABLE_EXTENDED_M73_LINES #if ENABLE_EXTENDED_M73_LINES
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_main; std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) }; last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
} }
// keeps track of last exported remaining time to next printer stop // keeps track of last exported remaining time to next printer stop
std::array<int, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_stop; std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported_stop[i] = time_in_minutes(machines[i].time); last_exported_stop[i] = time_in_minutes(machines[i].time);
} }
#else #else
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported; std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
last_exported[i] = { 0, time_in_minutes(machines[i].time) }; last_exported[i] = { 0, time_in_minutes(machines[i].time) };
} }
#endif // ENABLE_EXTENDED_M73_LINES #endif // ENABLE_EXTENDED_M73_LINES
@ -451,7 +517,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
line = line.substr(1); line = line.substr(1);
if (export_remaining_time_enabled && if (export_remaining_time_enabled &&
(line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) { (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i]; const TimeMachine& machine = machines[i];
if (machine.enabled) { if (machine.enabled) {
#if ENABLE_EXTENDED_M73_LINES #if ENABLE_EXTENDED_M73_LINES
@ -486,7 +552,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) { else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
#else #else
if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) { if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i]; const TimeMachine& machine = machines[i];
if (machine.enabled) { if (machine.enabled) {
ret += format_line_M73(machine.line_m73_mask.c_str(), ret += format_line_M73(machine.line_m73_mask.c_str(),
@ -497,13 +563,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
} }
else if (line == Estimated_Printing_Time_Placeholder_Tag) { else if (line == Estimated_Printing_Time_Placeholder_Tag) {
#endif // ENABLE_VALIDATE_CUSTOM_GCODE #endif // ENABLE_VALIDATE_CUSTOM_GCODE
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i]; const TimeMachine& machine = machines[i];
PrintEstimatedTimeStatistics::ETimeMode mode = static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i); PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) { if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128]; char buf[128];
sprintf(buf, "; estimated printing time (%s mode) = %s\n", sprintf(buf, "; estimated printing time (%s mode) = %s\n",
(mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.time).c_str()); get_time_dhms(machine.time).c_str());
ret += buf; ret += buf;
} }
@ -545,7 +611,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
unsigned int exported_lines_count = 0; unsigned int exported_lines_count = 0;
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (export_remaining_time_enabled) { if (export_remaining_time_enabled) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = machines[i]; const TimeMachine& machine = machines[i];
if (machine.enabled) { if (machine.enabled) {
// export pair <percent, remaining time> // export pair <percent, remaining time>
@ -789,13 +855,13 @@ GCodeProcessor::GCodeProcessor()
{ {
reset(); reset();
#if ENABLE_EXTENDED_M73_LINES #if ENABLE_EXTENDED_M73_LINES
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
#else #else
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n"; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
#endif // ENABLE_EXTENDED_M73_LINES #endif // ENABLE_EXTENDED_M73_LINES
} }
@ -826,6 +892,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]); m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
} }
m_filament_densities.resize(config.filament_density.values.size());
for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
m_filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
}
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) { if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config); m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
if (m_flavor == gcfMarlinLegacy) { if (m_flavor == gcfMarlinLegacy) {
@ -846,7 +917,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_time_processor.filament_unload_times[i] = static_cast<float>(config.filament_unload_time.values[i]); m_time_processor.filament_unload_times[i] = static_cast<float>(config.filament_unload_time.values[i]);
} }
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
@ -857,6 +928,12 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_time_processor.export_remaining_time_enabled = config.remaining_times.value; m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
m_use_volumetric_e = config.use_volumetric_e; m_use_volumetric_e = config.use_volumetric_e;
#if ENABLE_START_GCODE_VISUALIZATION
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value);
#endif // ENABLE_START_GCODE_VISUALIZATION
} }
void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -890,6 +967,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
} }
} }
const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density");
if (filament_densities != nullptr) {
for (double dens : filament_densities->values) {
m_filament_densities.push_back(static_cast<float>(dens));
}
}
m_result.extruders_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size(); m_result.extruders_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset"); const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset");
@ -1020,7 +1104,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values; m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
} }
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i); float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration; m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
@ -1035,11 +1119,17 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e"); const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
if (use_volumetric_e != nullptr) if (use_volumetric_e != nullptr)
m_use_volumetric_e = use_volumetric_e->value; m_use_volumetric_e = use_volumetric_e->value;
#if ENABLE_START_GCODE_VISUALIZATION
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value);
#endif // ENABLE_START_GCODE_VISUALIZATION
} }
void GCodeProcessor::enable_stealth_time_estimator(bool enabled) void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
{ {
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled = enabled; m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled = enabled;
} }
void GCodeProcessor::reset() void GCodeProcessor::reset()
@ -1060,6 +1150,9 @@ void GCodeProcessor::reset()
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_line_id = 0; m_line_id = 0;
#if ENABLE_SEAMS_VISUALIZATION
m_last_line_id = 0;
#endif // ENABLE_SEAMS_VISUALIZATION
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_feedrate = 0.0f; m_feedrate = 0.0f;
m_width = 0.0f; m_width = 0.0f;
@ -1081,7 +1174,12 @@ void GCodeProcessor::reset()
} }
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f); m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
m_filament_densities = std::vector<float>(Min_Extruder_Count, 1.245f);
m_extruded_last_z = 0.0f; m_extruded_last_z = 0.0f;
#if ENABLE_START_GCODE_VISUALIZATION
m_first_layer_height = 0.0f;
m_processing_start_custom_gcode = false;
#endif // ENABLE_START_GCODE_VISUALIZATION
m_g1_line_id = 0; m_g1_line_id = 0;
m_layer_id = 0; m_layer_id = 0;
m_cp_color.reset(); m_cp_color.reset();
@ -1090,6 +1188,7 @@ void GCodeProcessor::reset()
m_producers_enabled = false; m_producers_enabled = false;
m_time_processor.reset(); m_time_processor.reset();
m_used_filaments.reset();
m_result.reset(); m_result.reset();
m_result.id = ++s_result_id; m_result.id = ++s_result_id;
@ -1167,7 +1266,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
} }
// process the time blocks // process the time blocks
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i]; TimeMachine& machine = m_time_processor.machines[i];
TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time; TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time;
machine.calculate_time(); machine.calculate_time();
@ -1175,6 +1274,8 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache }); gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache });
} }
m_used_filaments.process_caches(this);
update_estimated_times_stats(); update_estimated_times_stats();
// post-process to add M73 lines into the gcode // post-process to add M73 lines into the gcode
@ -1197,20 +1298,20 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
} }
float GCodeProcessor::get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f; return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
} }
std::string GCodeProcessor::get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A"); return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
} }
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const
{ {
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> ret; std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
const TimeMachine& machine = m_time_processor.machines[static_cast<size_t>(mode)]; const TimeMachine& machine = m_time_processor.machines[static_cast<size_t>(mode)];
float total_time = 0.0f; float total_time = 0.0f;
for (const auto& [type, time] : machine.gcode_time.times) { for (const auto& [type, time] : machine.gcode_time.times) {
@ -1222,10 +1323,10 @@ std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcesso
return ret; return ret;
} }
std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
std::vector<std::pair<EMoveType, float>> ret; std::vector<std::pair<EMoveType, float>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].moves_time.size(); ++i) { for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].moves_time.size(); ++i) {
float time = m_time_processor.machines[static_cast<size_t>(mode)].moves_time[i]; float time = m_time_processor.machines[static_cast<size_t>(mode)].moves_time[i];
if (time > 0.0f) if (time > 0.0f)
@ -1235,10 +1336,10 @@ std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEst
return ret; return ret;
} }
std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
std::vector<std::pair<ExtrusionRole, float>> ret; std::vector<std::pair<ExtrusionRole, float>> ret;
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) { if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].roles_time.size(); ++i) { for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].roles_time.size(); ++i) {
float time = m_time_processor.machines[static_cast<size_t>(mode)].roles_time[i]; float time = m_time_processor.machines[static_cast<size_t>(mode)].roles_time[i];
if (time > 0.0f) if (time > 0.0f)
@ -1248,9 +1349,9 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
return ret; return ret;
} }
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
m_time_processor.machines[static_cast<size_t>(mode)].layers_time : m_time_processor.machines[static_cast<size_t>(mode)].layers_time :
std::vector<float>(); std::vector<float>();
} }
@ -1442,7 +1543,15 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_VALIDATE_CUSTOM_GCODE #if ENABLE_VALIDATE_CUSTOM_GCODE
// extrusion role tag // extrusion role tag
if (boost::starts_with(comment, reserved_tag(ETags::Role))) { if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
m_used_filaments.process_role_cache(this);
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())); m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
#if ENABLE_SEAMS_VISUALIZATION
if (m_extrusion_role == erExternalPerimeter)
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
#if ENABLE_START_GCODE_VISUALIZATION
m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0);
#endif // ENABLE_START_GCODE_VISUALIZATION
return; return;
} }
@ -1520,7 +1629,8 @@ void GCodeProcessor::process_tags(const std::string_view comment)
extruder_id = static_cast<unsigned char>(eid); extruder_id = static_cast<unsigned char>(eid);
} }
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview if (extruder_id < m_extruder_colors.size())
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
++m_cp_color.counter; ++m_cp_color.counter;
if (m_cp_color.counter == UCHAR_MAX) if (m_cp_color.counter == UCHAR_MAX)
m_cp_color.counter = 0; m_cp_color.counter = 0;
@ -1531,6 +1641,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
} }
process_custom_gcode_time(CustomGCode::ColorChange); process_custom_gcode_time(CustomGCode::ColorChange);
process_filaments(CustomGCode::ColorChange);
return; return;
} }
@ -1660,6 +1771,10 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
} }
#if ENABLE_SEAMS_VISUALIZATION
if (m_extrusion_role == erExternalPerimeter)
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
return true; return true;
} }
@ -1724,6 +1839,9 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
pos = cmt.find(" outer perimeter"); pos = cmt.find(" outer perimeter");
if (pos == 0) { if (pos == 0) {
m_extrusion_role = erExternalPerimeter; m_extrusion_role = erExternalPerimeter;
#if ENABLE_SEAMS_VISUALIZATION
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
return true; return true;
} }
@ -1878,6 +1996,11 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment)
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
} }
#if ENABLE_SEAMS_VISUALIZATION
if (m_extrusion_role == erExternalPerimeter)
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
return true; return true;
} }
@ -1916,6 +2039,11 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
m_extrusion_role = erNone; m_extrusion_role = erNone;
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type;
} }
#if ENABLE_SEAMS_VISUALIZATION
if (m_extrusion_role == erExternalPerimeter)
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
return true; return true;
} }
@ -1984,6 +2112,9 @@ bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment)
pos = comment.find(" 'Perimeter Path'"); pos = comment.find(" 'Perimeter Path'");
if (pos == 0) { if (pos == 0) {
m_extrusion_role = erExternalPerimeter; m_extrusion_role = erExternalPerimeter;
#if ENABLE_SEAMS_VISUALIZATION
m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION
return true; return true;
} }
@ -2148,6 +2279,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz; float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
// save extruded volume to the cache
m_used_filaments.increase_caches(volume_extruded_filament);
// volume extruded filament / tool displacement = area toolpath cross section // volume extruded filament / tool displacement = area toolpath cross section
m_mm3_per_mm = area_toolpath_cross_section; m_mm3_per_mm = area_toolpath_cross_section;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_GCODE_VIEWER_DATA_CHECKING
@ -2187,7 +2321,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
} }
#if ENABLE_START_GCODE_VISUALIZATION
if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
#else
if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f)) if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f))
#endif // ENABLE_START_GCODE_VISUALIZATION
type = EMoveType::Travel; type = EMoveType::Travel;
// time estimate section // time estimate section
@ -2204,7 +2342,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
assert(distance != 0.0f); assert(distance != 0.0f);
float inv_distance = 1.0f / distance; float inv_distance = 1.0f / distance;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i]; TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled) if (!machine.enabled)
continue; continue;
@ -2214,8 +2352,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
std::vector<TimeBlock>& blocks = machine.blocks; std::vector<TimeBlock>& blocks = machine.blocks;
curr.feedrate = (delta_pos[E] == 0.0f) ? curr.feedrate = (delta_pos[E] == 0.0f) ?
minimum_travel_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate) : minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
minimum_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate); minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
TimeBlock block; TimeBlock block;
block.move_type = type; block.move_type = type;
@ -2233,7 +2371,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]); curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
if (curr.abs_axis_feedrate[a] != 0.0f) { if (curr.abs_axis_feedrate[a] != 0.0f) {
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (axis_max_feedrate != 0.0f) if (axis_max_feedrate != 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]); min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
} }
@ -2250,13 +2388,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// calculates block acceleration // calculates block acceleration
float acceleration = float acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) : (type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
(is_extrusion_only_move(delta_pos) ? (is_extrusion_only_move(delta_pos) ?
get_retract_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) : get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i))); get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
for (unsigned char a = X; a <= E; ++a) { for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration; acceleration = axis_max_acceleration;
} }
@ -2267,7 +2405,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
curr.safe_feedrate = block.feedrate_profile.cruise; curr.safe_feedrate = block.feedrate_profile.cruise;
for (unsigned char a = X; a <= E; ++a) { for (unsigned char a = X; a <= E; ++a) {
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (curr.abs_axis_feedrate[a] > axis_max_jerk) if (curr.abs_axis_feedrate[a] > axis_max_jerk)
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk); curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
} }
@ -2303,19 +2441,19 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction. // Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
float jerk = float jerk =
(v_exit > v_entry) ? (v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ? ((v_entry > 0.0f || v_exit < 0.0f) ?
// coasting // coasting
(v_exit - v_entry) : (v_exit - v_entry) :
// axis reversal // axis reversal
std::max(v_exit, -v_entry)) : std::max(v_exit, -v_entry)) :
// v_exit <= v_entry // v_exit <= v_entry
(((v_entry < 0.0f) || (v_exit > 0.0f)) ? ((v_entry < 0.0f || v_exit > 0.0f) ?
// coasting // coasting
(v_entry - v_exit) : (v_entry - v_exit) :
// axis reversal // axis reversal
std::max(-v_exit, v_entry)); std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) { if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk; v_factor *= axis_max_jerk / jerk;
limited = true; limited = true;
@ -2330,7 +2468,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float vmax_junction_threshold = vmax_junction * 0.99f; float vmax_junction_threshold = vmax_junction * 0.99f;
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start. // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold)) if (prev.safe_feedrate > vmax_junction_threshold && curr.safe_feedrate > vmax_junction_threshold)
vmax_junction = curr.safe_feedrate; vmax_junction = curr.safe_feedrate;
} }
@ -2354,6 +2492,32 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
machine.calculate_time(TimeProcessor::Planner::queue_size); machine.calculate_time(TimeProcessor::Planner::queue_size);
} }
#if ENABLE_SEAMS_VISUALIZATION
if (m_seams_detector.is_active()) {
// check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex())
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
// check for seam ending vertex and store the resulting move
else if ((type != EMoveType::Extrude || m_extrusion_role != erExternalPerimeter) && m_seams_detector.has_first_vertex()) {
auto set_end_position = [this](const Vec3f& pos) {
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
};
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id];
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) {
set_end_position(0.5f * (new_pos + *first_vertex));
store_move_vertex(EMoveType::Seam);
set_end_position(curr_pos);
}
m_seams_detector.activate(false);
}
}
#endif // ENABLE_SEAMS_VISUALIZATION
// store move // store move
store_move_vertex(type); store_move_vertex(type);
} }
@ -2574,8 +2738,8 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f; float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) { m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor); set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
@ -2602,8 +2766,8 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
// http://smoothieware.org/supported-g-codes // http://smoothieware.org/supported-g-codes
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC; float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) { m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor); set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
@ -2623,27 +2787,27 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
{ {
float value; float value;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) { m_time_processor.machine_envelope_processing_enabled) {
if (line.has_value('S', value)) { if (line.has_value('S', value)) {
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware
// It is also generated by PrusaSlicer to control acceleration per extrusion type // It is also generated by PrusaSlicer to control acceleration per extrusion type
// (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used. // (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used.
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value); set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value); set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('T', value)) if (line.has_value('T', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
} }
else { else {
// New acceleration format, compatible with the upstream Marlin. // New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value)) if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value); set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('R', value)) if (line.has_value('R', value))
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value); set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
if (line.has_value('T', value)) if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format. // Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value); set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
} }
} }
} }
@ -2651,8 +2815,8 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
{ {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal || if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
m_time_processor.machine_envelope_processing_enabled) { m_time_processor.machine_envelope_processing_enabled) {
if (line.has_x()) { if (line.has_x()) {
float max_jerk = line.x(); float max_jerk = line.x();
@ -2685,7 +2849,7 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line)
float value_t; float value_t;
if (line.has_value('S', value_s) && !line.has_value('T', value_t)) { if (line.has_value('S', value_s) && !line.has_value('T', value_t)) {
value_s *= 0.01f; value_s *= 0.01f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
m_time_processor.machines[i].extrude_factor_override_percentage = value_s; m_time_processor.machines[i].extrude_factor_override_percentage = value_s;
} }
} }
@ -2736,7 +2900,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line)
{ {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
if (line.has_x()) if (line.has_x())
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC); set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC);
@ -2787,6 +2951,7 @@ void GCodeProcessor::process_T(const std::string_view command)
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode.";
else { else {
unsigned char old_extruder_id = m_extruder_id; unsigned char old_extruder_id = m_extruder_id;
process_filaments(CustomGCode::ToolChange);
m_extruder_id = id; m_extruder_id = id;
m_cp_color.current = m_extruder_colors[id]; m_cp_color.current = m_extruder_colors[id];
// Specific to the MK3 MMU2: // Specific to the MK3 MMU2:
@ -2807,15 +2972,29 @@ void GCodeProcessor::process_T(const std::string_view command)
void GCodeProcessor::store_move_vertex(EMoveType type) void GCodeProcessor::store_move_vertex(EMoveType type)
{ {
#if ENABLE_SEAMS_VISUALIZATION
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
#endif // ENABLE_SEAMS_VISUALIZATION
MoveVertex vertex = { MoveVertex vertex = {
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
#if ENABLE_SEAMS_VISUALIZATION
m_last_line_id,
#else
(type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id, (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id,
#endif // ENABLE_SEAMS_VISUALIZATION
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
type, type,
m_extrusion_role, m_extrusion_role,
m_extruder_id, m_extruder_id,
m_cp_color.current, m_cp_color.current,
#if ENABLE_START_GCODE_VISUALIZATION
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
#else
Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id], Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
#endif // ENABLE_START_GCODE_VISUALIZATION
m_end_position[E] - m_start_position[E], m_end_position[E] - m_start_position[E],
m_feedrate, m_feedrate,
m_width, m_width,
@ -2830,7 +3009,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
#if ENABLE_EXTENDED_M73_LINES #if ENABLE_EXTENDED_M73_LINES
// stores stop time placeholders for later use // stores stop time placeholders for later use
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i]; TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled) if (!machine.enabled)
continue; continue;
@ -2841,7 +3020,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
#endif // ENABLE_EXTENDED_M73_LINES #endif // ENABLE_EXTENDED_M73_LINES
} }
float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const float GCodeProcessor::minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
{ {
if (m_time_processor.machine_limits.machine_min_extruding_rate.empty()) if (m_time_processor.machine_limits.machine_min_extruding_rate.empty())
return feedrate; return feedrate;
@ -2849,7 +3028,7 @@ float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode m
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast<size_t>(mode))); return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast<size_t>(mode)));
} }
float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
{ {
if (m_time_processor.machine_limits.machine_min_travel_rate.empty()) if (m_time_processor.machine_limits.machine_min_travel_rate.empty())
return feedrate; return feedrate;
@ -2857,7 +3036,7 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETim
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode))); return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode)));
} }
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{ {
switch (axis) switch (axis)
{ {
@ -2869,7 +3048,7 @@ float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeM
} }
} }
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{ {
switch (axis) switch (axis)
{ {
@ -2881,7 +3060,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ET
} }
} }
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{ {
switch (axis) switch (axis)
{ {
@ -2893,18 +3072,18 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode
} }
} }
float GCodeProcessor::get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode)); return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
} }
float GCodeProcessor::get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{ {
size_t id = static_cast<size_t>(mode); size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION; return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION;
} }
void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value) void GCodeProcessor::set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{ {
size_t id = static_cast<size_t>(mode); size_t id = static_cast<size_t>(mode);
if (id < m_time_processor.machines.size()) { if (id < m_time_processor.machines.size()) {
@ -2914,13 +3093,13 @@ void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mo
} }
} }
float GCodeProcessor::get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const float GCodeProcessor::get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{ {
size_t id = static_cast<size_t>(mode); size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION; return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
} }
void GCodeProcessor::set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value) void GCodeProcessor::set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{ {
size_t id = static_cast<size_t>(mode); size_t id = static_cast<size_t>(mode);
if (id < m_time_processor.machines.size()) { if (id < m_time_processor.machines.size()) {
@ -2948,7 +3127,7 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id)
void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code) void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
{ {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
TimeMachine& machine = m_time_processor.machines[i]; TimeMachine& machine = m_time_processor.machines[i];
if (!machine.enabled) if (!machine.enabled)
continue; continue;
@ -2965,17 +3144,26 @@ void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
} }
} }
void GCodeProcessor::process_filaments(CustomGCode::Type code)
{
if (code == CustomGCode::ColorChange)
m_used_filaments.process_color_change_cache();
if (code == CustomGCode::ToolChange)
m_used_filaments.process_extruder_cache(this);
}
void GCodeProcessor::simulate_st_synchronize(float additional_time) void GCodeProcessor::simulate_st_synchronize(float additional_time)
{ {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
m_time_processor.machines[i].simulate_st_synchronize(additional_time); m_time_processor.machines[i].simulate_st_synchronize(additional_time);
} }
} }
void GCodeProcessor::update_estimated_times_stats() void GCodeProcessor::update_estimated_times_stats()
{ {
auto update_mode = [this](PrintEstimatedTimeStatistics::ETimeMode mode) { auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast<size_t>(mode)]; PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
data.time = get_time(mode); data.time = get_time(mode);
data.custom_gcode_times = get_custom_gcode_times(mode, true); data.custom_gcode_times = get_custom_gcode_times(mode, true);
data.moves_times = get_moves_time(mode); data.moves_times = get_moves_time(mode);
@ -2983,11 +3171,15 @@ void GCodeProcessor::update_estimated_times_stats()
data.layers_times = get_layers_time(mode); data.layers_times = get_layers_time(mode);
}; };
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal); update_mode(PrintEstimatedStatistics::ETimeMode::Normal);
if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled) if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled)
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Stealth); update_mode(PrintEstimatedStatistics::ETimeMode::Stealth);
else else
m_result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].reset(); m_result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].reset();
m_result.print_statistics.volumes_per_color_change = m_used_filaments.volumes_per_color_change;
m_result.print_statistics.volumes_per_extruder = m_used_filaments.volumes_per_extruder;
m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role;
} }
} /* namespace Slic3r */ } /* namespace Slic3r */

View file

@ -12,6 +12,9 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <string_view> #include <string_view>
#if ENABLE_SEAMS_VISUALIZATION
#include <optional>
#endif // ENABLE_SEAMS_VISUALIZATION
namespace Slic3r { namespace Slic3r {
@ -20,6 +23,9 @@ namespace Slic3r {
Noop, Noop,
Retract, Retract,
Unretract, Unretract,
#if ENABLE_SEAMS_VISUALIZATION
Seam,
#endif // ENABLE_SEAMS_VISUALIZATION
Tool_change, Tool_change,
Color_change, Color_change,
Pause_Print, Pause_Print,
@ -30,7 +36,7 @@ namespace Slic3r {
Count Count
}; };
struct PrintEstimatedTimeStatistics struct PrintEstimatedStatistics
{ {
enum class ETimeMode : unsigned char enum class ETimeMode : unsigned char
{ {
@ -56,14 +62,21 @@ namespace Slic3r {
} }
}; };
std::vector<double> volumes_per_color_change;
std::map<size_t, double> volumes_per_extruder;
std::map<ExtrusionRole, std::pair<double, double>> used_filaments_per_role;
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes; std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
PrintEstimatedTimeStatistics() { reset(); } PrintEstimatedStatistics() { reset(); }
void reset() { void reset() {
for (auto m : modes) { for (auto m : modes) {
m.reset(); m.reset();
} }
volumes_per_color_change.clear();
volumes_per_extruder.clear();
used_filaments_per_role.clear();
} }
}; };
@ -308,7 +321,7 @@ namespace Slic3r {
// Additional load / unload times for a filament exchange sequence. // Additional load / unload times for a filament exchange sequence.
std::vector<float> filament_load_times; std::vector<float> filament_load_times;
std::vector<float> filament_unload_times; std::vector<float> filament_unload_times;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines; std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
void reset(); void reset();
@ -321,6 +334,30 @@ namespace Slic3r {
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
}; };
struct UsedFilaments // filaments per ColorChange
{
double color_change_cache;
std::vector<double> volumes_per_color_change;
double tool_change_cache;
std::map<size_t, double> volumes_per_extruder;
double role_cache;
// ExtrusionRole : <used_filament_m, used_filament_g>
std::map<ExtrusionRole, std::pair<double, double>> filaments_per_role;
void reset();
void increase_caches(double extruded_volume);
void process_color_change_cache();
void process_extruder_cache(GCodeProcessor* processor);
void process_role_cache(GCodeProcessor* processor);
void process_caches(GCodeProcessor* processor);
friend class GCodeProcessor;
};
public: public:
#if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
struct MoveVertex struct MoveVertex
@ -366,12 +403,11 @@ namespace Slic3r {
SettingsIds settings_ids; SettingsIds settings_ids;
size_t extruders_count; size_t extruders_count;
std::vector<std::string> extruder_colors; std::vector<std::string> extruder_colors;
PrintEstimatedTimeStatistics time_statistics; PrintEstimatedStatistics print_statistics;
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 }; int64_t time{ 0 };
void reset() void reset() {
{
time = 0; time = 0;
moves = std::vector<MoveVertex>(); moves = std::vector<MoveVertex>();
bed_shape = Pointfs(); bed_shape = Pointfs();
@ -380,8 +416,7 @@ namespace Slic3r {
settings_ids.reset(); settings_ids.reset();
} }
#else #else
void reset() void reset() {
{
moves = std::vector<MoveVertex>(); moves = std::vector<MoveVertex>();
bed_shape = Pointfs(); bed_shape = Pointfs();
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
@ -391,6 +426,29 @@ namespace Slic3r {
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
}; };
#if ENABLE_SEAMS_VISUALIZATION
class SeamsDetector
{
bool m_active{ false };
std::optional<Vec3f> m_first_vertex;
public:
void activate(bool active) {
if (m_active != active) {
m_active = active;
if (m_active)
m_first_vertex.reset();
}
}
std::optional<Vec3f> get_first_vertex() const { return m_first_vertex; }
void set_first_vertex(const Vec3f& vertex) { m_first_vertex = vertex; }
bool is_active() const { return m_active; }
bool has_first_vertex() const { return m_first_vertex.has_value(); }
};
#endif // ENABLE_SEAMS_VISUALIZATION
#if ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_GCODE_VIEWER_DATA_CHECKING
struct DataChecker struct DataChecker
{ {
@ -476,6 +534,9 @@ namespace Slic3r {
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
unsigned int m_line_id; unsigned int m_line_id;
#if ENABLE_SEAMS_VISUALIZATION
unsigned int m_last_line_id;
#endif // ENABLE_SEAMS_VISUALIZATION
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
float m_feedrate; // mm/s float m_feedrate; // mm/s
float m_width; // mm float m_width; // mm
@ -489,11 +550,19 @@ namespace Slic3r {
ExtruderColors m_extruder_colors; ExtruderColors m_extruder_colors;
ExtruderTemps m_extruder_temps; ExtruderTemps m_extruder_temps;
std::vector<float> m_filament_diameters; std::vector<float> m_filament_diameters;
std::vector<float> m_filament_densities;
float m_extruded_last_z; float m_extruded_last_z;
#if ENABLE_START_GCODE_VISUALIZATION
float m_first_layer_height; // mm
bool m_processing_start_custom_gcode;
#endif // ENABLE_START_GCODE_VISUALIZATION
unsigned int m_g1_line_id; unsigned int m_g1_line_id;
unsigned int m_layer_id; unsigned int m_layer_id;
CpColor m_cp_color; CpColor m_cp_color;
bool m_use_volumetric_e; bool m_use_volumetric_e;
#if ENABLE_SEAMS_VISUALIZATION
SeamsDetector m_seams_detector;
#endif // ENABLE_SEAMS_VISUALIZATION
enum class EProducer enum class EProducer
{ {
@ -513,6 +582,7 @@ namespace Slic3r {
bool m_producers_enabled; bool m_producers_enabled;
TimeProcessor m_time_processor; TimeProcessor m_time_processor;
UsedFilaments m_used_filaments;
Result m_result; Result m_result;
static unsigned int s_result_id; static unsigned int s_result_id;
@ -529,7 +599,7 @@ namespace Slic3r {
void apply_config(const PrintConfig& config); void apply_config(const PrintConfig& config);
void enable_stealth_time_estimator(bool enabled); void enable_stealth_time_estimator(bool enabled);
bool is_stealth_time_estimator_enabled() const { bool is_stealth_time_estimator_enabled() const {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled; return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
} }
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; } void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
void enable_producers(bool enabled) { m_producers_enabled = enabled; } void enable_producers(bool enabled) { m_producers_enabled = enabled; }
@ -542,13 +612,13 @@ namespace Slic3r {
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr); void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const; std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const; std::vector<float> get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const;
private: private:
void apply_config(const DynamicPrintConfig& config); void apply_config(const DynamicPrintConfig& config);
@ -664,20 +734,21 @@ namespace Slic3r {
void store_move_vertex(EMoveType type); void store_move_vertex(EMoveType type);
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const; float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const; float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value); void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const; float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value); void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_filament_load_time(size_t extruder_id); float get_filament_load_time(size_t extruder_id);
float get_filament_unload_time(size_t extruder_id); float get_filament_unload_time(size_t extruder_id);
void process_custom_gcode_time(CustomGCode::Type code); void process_custom_gcode_time(CustomGCode::Type code);
void process_filaments(CustomGCode::Type code);
// Simulates firmware st_synchronize() call // Simulates firmware st_synchronize() call
void simulate_st_synchronize(float additional_time = 0.0f); void simulate_st_synchronize(float additional_time = 0.0f);

View file

@ -19,8 +19,18 @@ struct ThumbnailData
bool is_valid() const; bool is_valid() const;
}; };
typedef std::vector<ThumbnailData> ThumbnailsList; using ThumbnailsList = std::vector<ThumbnailData>;
typedef std::function<void(ThumbnailsList & thumbnails, const Vec2ds & sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)> ThumbnailsGeneratorCallback;
struct ThumbnailsParams
{
const Vec2ds sizes;
bool printable_only;
bool parts_only;
bool show_bed;
bool transparent_background;
};
typedef std::function<ThumbnailsList(const ThumbnailsParams&)> ThumbnailsGeneratorCallback;
} // namespace Slic3r } // namespace Slic3r

View file

@ -223,11 +223,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
layer_tools.extruder_override = extruder_override; layer_tools.extruder_override = extruder_override;
// What extruders are required to print this object layer? // What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { for (const LayerRegion *layerm : layer->regions()) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; const PrintRegion &region = layerm->region();
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions()[region_id];
if (! layerm->perimeters.entities.empty()) { if (! layerm->perimeters.entities.empty()) {
bool something_nonoverriddable = true; bool something_nonoverriddable = true;
@ -688,16 +685,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
for (const LayerRegion *layerm : this_layer->regions()) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { const auto &region = layerm->region();
const auto& region = *object->print()->regions()[region_id];
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue; continue;
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill; bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
if (print.config().infill_first != perimeters_done || wipe_into_infill_only) { if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region)) if (!is_overriddable(*fill, print.config(), *object, region))
@ -721,7 +716,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// Now the same for perimeters - see comments above for explanation: // Now the same for perimeters - see comments above for explanation:
if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done) if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
{ {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { for (const ExtrusionEntity* ee : layerm->perimeters.entities) {
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) { if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
set_extruder_override(fill, copy, new_extruder, num_of_copies); set_extruder_override(fill, copy, new_extruder, num_of_copies);
@ -762,13 +757,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
size_t num_of_copies = object->instances().size(); size_t num_of_copies = object->instances().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { for (const LayerRegion *layerm : this_layer->regions()) {
const auto& region = *object->print()->regions()[region_id]; const auto &region = layerm->region();
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue; continue;
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region) if (!is_overriddable(*fill, print.config(), *object, region)
@ -791,7 +785,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
} }
// Now the same for perimeters - see comments above for explanation: // Now the same for perimeters - see comments above for explanation:
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections for (const ExtrusionEntity* ee : layerm->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy)) if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);

View file

@ -500,9 +500,9 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
ToolChangeResult result; ToolChangeResult result;
result.priming = priming; result.priming = priming;
result.initial_tool = int(old_tool); result.initial_tool = int(old_tool);
result.new_tool = int(this->m_current_tool); result.new_tool = int(m_current_tool);
result.print_z = this->m_z_pos; result.print_z = m_z_pos;
result.layer_height = this->m_layer_height; result.layer_height = m_layer_height;
result.elapsed_time = writer.elapsed_time(); result.elapsed_time = writer.elapsed_time();
result.start_pos = writer.start_pos_rotated(); result.start_pos = writer.start_pos_rotated();
result.end_pos = priming ? writer.pos() : writer.pos_rotated(); result.end_pos = priming ? writer.pos() : writer.pos_rotated();
@ -546,10 +546,24 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
m_extra_loading_move = float(config.extra_loading_move); m_extra_loading_move = float(config.extra_loading_move);
m_set_extruder_trimpot = config.high_current_on_filament_swap; m_set_extruder_trimpot = config.high_current_on_filament_swap;
} }
// Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes // Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
const std::vector<Vec2d>& bed_points = config.bed_shape.values; const std::vector<Vec2d>& bed_points = config.bed_shape.values;
BoundingBoxf bb(bed_points);
m_bed_width = float(bb.size().x());
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed); m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
m_bed_width = float(BoundingBoxf(bed_points).size().x());
if (m_bed_shape == CircularBed) {
// this may still be a custom bed, check that the points are roughly on a circle
double r2 = std::pow(m_bed_width/2., 2.);
double lim2 = std::pow(m_bed_width/10., 2.);
Vec2d center = bb.center();
for (const Vec2d& pt : bed_points)
if (std::abs(std::pow(pt.x()-center.x(), 2.) + std::pow(pt.y()-center.y(), 2.) - r2) > lim2) {
m_bed_shape = CustomBed;
break;
}
}
m_bed_bottom_left = m_bed_shape == RectangularBed m_bed_bottom_left = m_bed_shape == RectangularBed
? Vec2f(bed_points.front().x(), bed_points.front().y()) ? Vec2f(bed_points.front().x(), bed_points.front().y())
: Vec2f::Zero(); : Vec2f::Zero();
@ -616,7 +630,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
bool /*last_wipe_inside_wipe_tower*/) bool /*last_wipe_inside_wipe_tower*/)
{ {
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false); this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
this->m_current_tool = tools.front(); m_current_tool = tools.front();
// The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210]. // The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
// Due to the XYZ calibration, this working space may shrink slightly from all directions, // Due to the XYZ calibration, this working space may shrink slightly from all directions,
@ -625,10 +639,12 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f); float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f);
box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
// In case of a circular bed, place it so it goes across the diameter and hope it will fit if (m_bed_shape == CircularBed) {
if (m_bed_shape == CircularBed) cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f);
cleaning_box.translate(-m_bed_width/2 + m_bed_width * 0.03f, -m_bed_width * 0.12f); float total_width_half = tools.size() * prime_section_width / 2.f;
if (m_bed_shape == RectangularBed) cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f))));
}
else
cleaning_box.translate(m_bed_bottom_left); cleaning_box.translate(m_bed_bottom_left);
std::vector<ToolChangeResult> results; std::vector<ToolChangeResult> results;

View file

@ -164,10 +164,9 @@ public:
m_current_layer_finished = false; m_current_layer_finished = false;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL; m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
if (is_first_layer) { if (is_first_layer) {
this->m_num_layer_changes = 0; m_num_layer_changes = 0;
this->m_num_tool_changes = 0; m_num_tool_changes = 0;
} } else
else
++ m_num_layer_changes; ++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height: // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
@ -277,7 +276,8 @@ private:
// Bed properties // Bed properties
enum { enum {
RectangularBed, RectangularBed,
CircularBed CircularBed,
CustomBed
} m_bed_shape; } m_bed_shape;
float m_bed_width; // width of the bed bounding box float m_bed_width; // width of the bed bounding box
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds) Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)

View file

@ -13,13 +13,13 @@ namespace Slic3r {
void GCodeReader::apply_config(const GCodeConfig &config) void GCodeReader::apply_config(const GCodeConfig &config)
{ {
m_config = config; m_config = config;
m_extrusion_axis = m_config.get_extrusion_axis()[0]; m_extrusion_axis = get_extrusion_axis(m_config)[0];
} }
void GCodeReader::apply_config(const DynamicPrintConfig &config) void GCodeReader::apply_config(const DynamicPrintConfig &config)
{ {
m_config.apply(config, true); m_config.apply(config, true);
m_extrusion_axis = m_config.get_extrusion_axis()[0]; m_extrusion_axis = get_extrusion_axis(m_config)[0];
} }
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command) const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)

View file

@ -18,7 +18,7 @@ namespace Slic3r {
void GCodeWriter::apply_print_config(const PrintConfig &print_config) void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{ {
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis(); m_extrusion_axis = get_extrusion_axis(this->config);
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware; bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware;
m_max_acceleration = std::lrint((is_marlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ? m_max_acceleration = std::lrint((is_marlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ?

View file

@ -1083,8 +1083,7 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol
} }
} }
bool bool MedialAxis::validate_edge(const VD::edge_type* edge)
MedialAxis::validate_edge(const VD::edge_type* edge)
{ {
// prevent overflows and detect almost-infinite edges // prevent overflows and detect almost-infinite edges
#ifndef CLIPPERLIB_INT32 #ifndef CLIPPERLIB_INT32

View file

@ -22,12 +22,14 @@
#pragma warning(pop) #pragma warning(pop)
#endif // _MSC_VER #endif // _MSC_VER
namespace ClipperLib { namespace Slic3r {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Slic3r { namespace Geometry { namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Geometry {
// Generic result of an orientation predicate. // Generic result of an orientation predicate.
enum Orientation enum Orientation
@ -530,6 +532,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
} }
} } } } // namespace Slicer::Geometry
#endif #endif

View file

@ -27,7 +27,7 @@ bool Layer::empty() const
return true; return true;
} }
LayerRegion* Layer::add_region(PrintRegion* print_region) LayerRegion* Layer::add_region(const PrintRegion *print_region)
{ {
m_regions.emplace_back(new LayerRegion(this, print_region)); m_regions.emplace_back(new LayerRegion(this, print_region));
return m_regions.back(); return m_regions.back();
@ -39,11 +39,11 @@ void Layer::make_slices()
ExPolygons slices; ExPolygons slices;
if (m_regions.size() == 1) { if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices // optimization: if we only have one region, take its slices
slices = m_regions.front()->slices; slices = to_expolygons(m_regions.front()->slices.surfaces);
} else { } else {
Polygons slices_p; Polygons slices_p;
for (LayerRegion *layerm : m_regions) for (LayerRegion *layerm : m_regions)
polygons_append(slices_p, to_polygons(layerm->slices)); polygons_append(slices_p, to_polygons(layerm->slices.surfaces));
slices = union_ex(slices_p); slices = union_ex(slices_p);
} }
@ -102,10 +102,10 @@ ExPolygons Layer::merged(float offset_scaled) const
} }
Polygons polygons; Polygons polygons;
for (LayerRegion *layerm : m_regions) { for (LayerRegion *layerm : m_regions) {
const PrintRegionConfig &config = layerm->region()->config(); const PrintRegionConfig &config = layerm->region().config();
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty. // Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0) if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled)); append(polygons, offset(layerm->slices.surfaces, offset_scaled));
} }
ExPolygons out = union_ex(polygons); ExPolygons out = union_ex(polygons);
if (offset_scaled2 != 0.f) if (offset_scaled2 != 0.f)
@ -134,7 +134,7 @@ void Layer::make_perimeters()
continue; continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id; BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done[region_id] = true; done[region_id] = true;
const PrintRegionConfig &config = (*layerm)->region()->config(); const PrintRegionConfig &config = (*layerm)->region().config();
// find compatible regions // find compatible regions
LayerRegionPtrs layerms; LayerRegionPtrs layerms;
@ -142,7 +142,7 @@ void Layer::make_perimeters()
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
if (! (*it)->slices.empty()) { if (! (*it)->slices.empty()) {
LayerRegion* other_layerm = *it; LayerRegion* other_layerm = *it;
const PrintRegionConfig &other_config = other_layerm->region()->config(); const PrintRegionConfig &other_config = other_layerm->region().config();
if (config.perimeter_extruder == other_config.perimeter_extruder if (config.perimeter_extruder == other_config.perimeter_extruder
&& config.perimeters == other_config.perimeters && config.perimeters == other_config.perimeters
&& config.perimeter_speed == other_config.perimeter_speed && config.perimeter_speed == other_config.perimeter_speed
@ -180,12 +180,12 @@ void Layer::make_perimeters()
for (LayerRegion *layerm : layerms) { for (LayerRegion *layerm : layerms) {
for (Surface &surface : layerm->slices.surfaces) for (Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface); slices[surface.extra_perimeters].emplace_back(surface);
if (layerm->region()->config().fill_density > layerm_config->region()->config().fill_density) if (layerm->region().config().fill_density > layerm_config->region().config().fill_density)
layerm_config = layerm; layerm_config = layerm;
} }
// merge the surfaces assigned to each group // merge the surfaces assigned to each group
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices) for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front()); new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front());
} }
// make perimeters // make perimeters
@ -196,7 +196,7 @@ void Layer::make_perimeters()
if (!fill_surfaces.surfaces.empty()) { if (!fill_surfaces.surfaces.empty()) {
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) { for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
// Separate the fill surfaces. // Separate the fill surfaces.
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); ExPolygons expp = intersection_ex(fill_surfaces.surfaces, (*l)->slices.surfaces);
(*l)->fill_expolygons = expp; (*l)->fill_expolygons = expp;
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front()); (*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
} }

View file

@ -22,8 +22,7 @@ class LayerRegion
public: public:
Layer* layer() { return m_layer; } Layer* layer() { return m_layer; }
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }
PrintRegion* region() { return m_region; } const PrintRegion& region() const { return *m_region; }
const PrintRegion* region() const { return m_region; }
// collection of surfaces generated by slicing the original geometry // collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal // divided by type top/bottom/internal
@ -86,12 +85,12 @@ public:
protected: protected:
friend class Layer; friend class Layer;
LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {} LayerRegion(Layer *layer, const PrintRegion *region) : m_layer(layer), m_region(region) {}
~LayerRegion() {} ~LayerRegion() {}
private: private:
Layer *m_layer; Layer *m_layer;
PrintRegion *m_region; const PrintRegion *m_region;
}; };
@ -126,9 +125,9 @@ public:
std::vector<BoundingBox> lslices_bboxes; std::vector<BoundingBox> lslices_bboxes;
size_t region_count() const { return m_regions.size(); } size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
LayerRegion* get_region(int idx) { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; }
LayerRegion* add_region(PrintRegion* print_region); LayerRegion* add_region(const PrintRegion *print_region);
const LayerRegionPtrs& regions() const { return m_regions; } const LayerRegionPtrs& regions() const { return m_regions; }
// Test whether whether there are any slices assigned to this layer. // Test whether whether there are any slices assigned to this layer.
bool empty() const; bool empty() const;
@ -196,7 +195,7 @@ protected:
// between the raft and the object first layer. // between the raft and the object first layer.
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
Layer(id, object, height, print_z, slice_z) {} Layer(id, object, height, print_z, slice_z) {}
virtual ~SupportLayer() {} virtual ~SupportLayer() = default;
}; };
} }

View file

@ -27,13 +27,14 @@ Flow LayerRegion::flow(FlowRole role, double layer_height) const
Flow LayerRegion::bridging_flow(FlowRole role) const Flow LayerRegion::bridging_flow(FlowRole role) const
{ {
const PrintRegion &region = *this->region(); const PrintRegion &region = this->region();
const PrintRegionConfig &region_config = region.config(); const PrintRegionConfig &region_config = region.config();
if (this->layer()->object()->config().thick_bridges) { const PrintObject &print_object = *this->layer()->object();
if (print_object.config().thick_bridges) {
// The old Slic3r way (different from all other slicers): Use rounded extrusions. // The old Slic3r way (different from all other slicers): Use rounded extrusions.
// Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
// Applies default bridge spacing. // Applies default bridge spacing.
return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter); return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter);
} else { } else {
@ -49,19 +50,17 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
// in place. However we're now only using its boundaries (which are invariant) // in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case // so we're safe. This guarantees idempotence of prepare_infill() also in case
// that combine_infill() turns some fill_surface into VOID surfaces. // that combine_infill() turns some fill_surface into VOID surfaces.
// Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces));
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
// Collect polygons per surface type. // Collect polygons per surface type.
std::vector<Polygons> polygons_by_surface; std::vector<SurfacesPtr> by_surface;
polygons_by_surface.assign(size_t(stCount), Polygons()); by_surface.assign(size_t(stCount), SurfacesPtr());
for (Surface &surface : this->slices.surfaces) for (Surface &surface : this->slices.surfaces)
polygons_append(polygons_by_surface[(size_t)surface.surface_type], surface.expolygon); by_surface[size_t(surface.surface_type)].emplace_back(&surface);
// Trim surfaces by the fill_boundaries. // Trim surfaces by the fill_boundaries.
this->fill_surfaces.surfaces.clear(); this->fill_surfaces.surfaces.clear();
for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) { for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) {
const Polygons &polygons = polygons_by_surface[surface_type]; const SurfacesPtr &this_surfaces = by_surface[surface_type];
if (! polygons.empty()) if (! this_surfaces.empty())
this->fill_surfaces.append(intersection_ex(polygons, fill_boundaries), SurfaceType(surface_type)); this->fill_surfaces.append(intersection_ex(this_surfaces, this->fill_expolygons), SurfaceType(surface_type));
} }
} }
@ -71,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
this->thin_fills.clear(); this->thin_fills.clear();
const PrintConfig &print_config = this->layer()->object()->print()->config(); const PrintConfig &print_config = this->layer()->object()->print()->config();
const PrintRegionConfig &region_config = this->region()->config(); const PrintRegionConfig &region_config = this->region().config();
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer! // This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
bool spiral_vase = print_config.spiral_vase && bool spiral_vase = print_config.spiral_vase &&
//FIXME account for raft layers. //FIXME account for raft layers.
@ -112,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered) void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
{ {
const bool has_infill = this->region()->config().fill_density.value > 0.; const bool has_infill = this->region().config().fill_density.value > 0.;
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN)); const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -180,11 +179,11 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
if (bridges.empty()) if (bridges.empty())
{ {
fill_boundaries = union_(fill_boundaries, true); fill_boundaries = union_safety_offset(fill_boundaries);
} else } else
{ {
// 1) Calculate the inflated bridge regions, each constrained to its island. // 1) Calculate the inflated bridge regions, each constrained to its island.
ExPolygons fill_boundaries_ex = union_ex(fill_boundaries, true); ExPolygons fill_boundaries_ex = union_safety_offset_ex(fill_boundaries);
std::vector<Polygons> bridges_grown; std::vector<Polygons> bridges_grown;
std::vector<BoundingBox> bridge_bboxes; std::vector<BoundingBox> bridge_bboxes;
@ -221,7 +220,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!"; BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
} else { } else {
// Found an island, to which this bridge region belongs. Trim it, // Found an island, to which this bridge region belongs. Trim it,
polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island])); polys = intersection(polys, fill_boundaries_ex[idx_island]);
} }
bridge_bboxes.push_back(get_extents(polys)); bridge_bboxes.push_back(get_extents(polys));
bridges_grown.push_back(std::move(polys)); bridges_grown.push_back(std::move(polys));
@ -239,7 +238,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
for (size_t j = i + 1; j < bridges.size(); ++ j) { for (size_t j = i + 1; j < bridges.size(); ++ j) {
if (! bridge_bboxes[i].overlap(bridge_bboxes[j])) if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
continue; continue;
if (intersection(bridges_grown[i], bridges_grown[j], false).empty()) if (intersection(bridges_grown[i], bridges_grown[j]).empty())
continue; continue;
// The two bridge regions intersect. Give them the same group id. // The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != size_t(-1)) { if (bridge_group[j] != size_t(-1)) {
@ -286,7 +285,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf("Processing bridge at layer %zu:\n", this->layer()->id()); printf("Processing bridge at layer %zu:\n", this->layer()->id());
#endif #endif
double custom_angle = Geometry::deg2rad(this->region()->config().bridge_angle.value); double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
if (bd.detect_angle(custom_angle)) { if (bd.detect_angle(custom_angle)) {
bridges[idx_last].bridge_angle = bd.angle; bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->has_support()) { if (this->layer()->object()->has_support()) {
@ -299,7 +298,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
bridges[idx_last].bridge_angle = custom_angle; bridges[idx_last].bridge_angle = custom_angle;
} }
// without safety offset, artifacts are generated (GH #2494) // without safety offset, artifacts are generated (GH #2494)
surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]); surfaces_append(bottom, union_safety_offset_ex(grown), bridges[idx_last]);
} }
fill_boundaries = to_polygons(fill_boundaries_ex); fill_boundaries = to_polygons(fill_boundaries_ex);
@ -325,11 +324,11 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
if (s1.empty()) if (s1.empty())
continue; continue;
Polygons polys; Polygons polys;
polygons_append(polys, std::move(s1)); polygons_append(polys, to_polygons(std::move(s1)));
for (size_t j = i + 1; j < top.size(); ++ j) { for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j]; Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) { if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, std::move(s2)); polygons_append(polys, to_polygons(std::move(s2)));
s2.clear(); s2.clear();
} }
} }
@ -339,7 +338,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
surfaces_append( surfaces_append(
new_surfaces, new_surfaces,
// Don't use a safety offset as fill_boundaries were already united using the safety offset. // Don't use a safety offset as fill_boundaries were already united using the safety offset.
intersection_ex(polys, fill_boundaries, false), intersection_ex(polys, fill_boundaries),
s1); s1);
} }
} }
@ -351,11 +350,11 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
if (s1.empty()) if (s1.empty())
continue; continue;
Polygons polys; Polygons polys;
polygons_append(polys, std::move(s1)); polygons_append(polys, to_polygons(std::move(s1)));
for (size_t j = i + 1; j < internal.size(); ++ j) { for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j]; Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) { if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, std::move(s2)); polygons_append(polys, to_polygons(std::move(s2)));
s2.clear(); s2.clear();
} }
} }
@ -385,21 +384,21 @@ void LayerRegion::prepare_fill_surfaces()
bool spiral_vase = this->layer()->object()->print()->config().spiral_vase; bool spiral_vase = this->layer()->object()->print()->config().spiral_vase;
// if no solid layers are requested, turn top/bottom surfaces to internal // if no solid layers are requested, turn top/bottom surfaces to internal
if (! spiral_vase && this->region()->config().top_solid_layers == 0) { if (! spiral_vase && this->region().config().top_solid_layers == 0) {
for (Surface &surface : this->fill_surfaces.surfaces) for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.is_top()) if (surface.is_top())
surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
} }
if (this->region()->config().bottom_solid_layers == 0) { if (this->region().config().bottom_solid_layers == 0) {
for (Surface &surface : this->fill_surfaces.surfaces) for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.is_bottom()) // (surface.surface_type == stBottom) if (surface.is_bottom()) // (surface.surface_type == stBottom)
surface.surface_type = stInternal; surface.surface_type = stInternal;
} }
// turn too small internal regions into solid regions according to the user setting // turn too small internal regions into solid regions according to the user setting
if (! spiral_vase && this->region()->config().fill_density.value > 0) { if (! spiral_vase && this->region().config().fill_density.value > 0) {
// scaling an area requires two calls! // scaling an area requires two calls!
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); double min_area = scale_(scale_(this->region().config().solid_infill_below_area.value));
for (Surface &surface : this->fill_surfaces.surfaces) for (Surface &surface : this->fill_surfaces.surfaces)
if (surface.surface_type == stInternal && surface.area() <= min_area) if (surface.surface_type == stInternal && surface.area() <= min_area)
surface.surface_type = stInternalSolid; surface.surface_type = stInternalSolid;
@ -423,7 +422,7 @@ void LayerRegion::trim_surfaces(const Polygons &trimming_polygons)
for (const Surface &surface : this->slices.surfaces) for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal); assert(surface.surface_type == stInternal);
#endif /* NDEBUG */ #endif /* NDEBUG */
this->slices.set(intersection_ex(to_polygons(std::move(this->slices.surfaces)), trimming_polygons, false), stInternal); this->slices.set(intersection_ex(this->slices.surfaces, trimming_polygons), stInternal);
} }
void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons) void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons)
@ -432,10 +431,9 @@ void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_comp
for (const Surface &surface : this->slices.surfaces) for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal); assert(surface.surface_type == stInternal);
#endif /* NDEBUG */ #endif /* NDEBUG */
ExPolygons slices_expolygons = to_expolygons(std::move(this->slices.surfaces)); ExPolygons surfaces = to_expolygons(std::move(this->slices.surfaces));
Polygons slices_polygons = to_polygons(slices_expolygons); Polygons tmp = intersection(surfaces, trimming_polygons);
Polygons tmp = intersection(slices_polygons, trimming_polygons, false); append(tmp, diff(surfaces, offset(offset_ex(surfaces, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
append(tmp, diff(slices_polygons, offset(offset_ex(slices_expolygons, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
this->slices.set(union_ex(tmp), stInternal); this->slices.set(union_ex(tmp), stInternal);
} }

View file

@ -4,6 +4,8 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "Point.hpp" #include "Point.hpp"
#include <type_traits>
namespace Slic3r { namespace Slic3r {
class BoundingBox; class BoundingBox;
@ -20,12 +22,28 @@ Linef3 transform(const Linef3& line, const Transform3d& t);
namespace line_alg { namespace line_alg {
template<class L, class En = void> struct Traits {
static constexpr int Dim = L::Dim;
using Scalar = typename L::Scalar;
static Vec<Dim, Scalar>& get_a(L &l) { return l.a; }
static Vec<Dim, Scalar>& get_b(L &l) { return l.b; }
static const Vec<Dim, Scalar>& get_a(const L &l) { return l.a; }
static const Vec<Dim, Scalar>& get_b(const L &l) { return l.b; }
};
template<class L> const constexpr int Dim = Traits<remove_cvref_t<L>>::Dim;
template<class L> using Scalar = typename Traits<remove_cvref_t<L>>::Scalar;
template<class L> auto get_a(L &&l) { return Traits<remove_cvref_t<L>>::get_a(l); }
template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l); }
// Distance to the closest point of line. // Distance to the closest point of line.
template<class L, class T, int N> template<class L>
double distance_to_squared(const L &line, const Vec<N, T> &point) double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
{ {
const Vec<N, double> v = (line.b - line.a).template cast<double>(); const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
const Vec<N, double> va = (point - line.a).template cast<double>(); const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt const double l2 = v.squaredNorm(); // avoid a sqrt
if (l2 == 0.0) if (l2 == 0.0)
// a == b case // a == b case
@ -35,12 +53,12 @@ double distance_to_squared(const L &line, const Vec<N, T> &point)
// It falls where t = [(this-a) . (b-a)] / |b-a|^2 // It falls where t = [(this-a) . (b-a)] / |b-a|^2
const double t = va.dot(v) / l2; const double t = va.dot(v) / l2;
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
else if (t > 1.0) return (point - line.b).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment else if (t > 1.0) return (point - get_b(line)).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment
return (t * v - va).squaredNorm(); return (t * v - va).squaredNorm();
} }
template<class L, class T, int N> template<class L>
double distance_to(const L &line, const Vec<N, T> &point) double distance_to(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
{ {
return std::sqrt(distance_to_squared(line, point)); return std::sqrt(distance_to_squared(line, point));
} }
@ -84,6 +102,9 @@ public:
Point a; Point a;
Point b; Point b;
static const constexpr int Dim = 2;
using Scalar = Point::Scalar;
}; };
class ThickLine : public Line class ThickLine : public Line
@ -107,6 +128,9 @@ public:
Vec3crd a; Vec3crd a;
Vec3crd b; Vec3crd b;
static const constexpr int Dim = 3;
using Scalar = Vec3crd::Scalar;
}; };
class Linef class Linef
@ -117,6 +141,9 @@ public:
Vec2d a; Vec2d a;
Vec2d b; Vec2d b;
static const constexpr int Dim = 2;
using Scalar = Vec2d::Scalar;
}; };
class Linef3 class Linef3
@ -133,6 +160,9 @@ public:
Vec3d a; Vec3d a;
Vec3d b; Vec3d b;
static const constexpr int Dim = 3;
using Scalar = Vec3d::Scalar;
}; };
BoundingBox get_extents(const Lines &lines); BoundingBox get_extents(const Lines &lines);

View file

@ -106,8 +106,8 @@ template<class C> bool all_of(const C &container)
}); });
} }
template<class T> //template<class T>
using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>; //using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, class = IntegerOnly<I>> template<class T, class I, class = IntegerOnly<I>>

View file

@ -14,55 +14,9 @@
#include <boost/multiprecision/integer.hpp> #include <boost/multiprecision/integer.hpp>
#endif #endif
#include <libnest2d/geometry_traits.hpp> #include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/utils/rotcalipers.hpp> #include <libnest2d/utils/rotcalipers.hpp>
namespace libnest2d {
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
template<> struct CoordType<Slic3r::Point> { using Type = coord_t; };
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
template<> struct ContourType<Slic3r::ExPolygon> { using Type = Slic3r::Points; };
template<> struct ContourType<Slic3r::Polygon> { using Type = Slic3r::Points; };
namespace pointlike {
template<> inline coord_t x(const Slic3r::Point& p) { return p.x(); }
template<> inline coord_t y(const Slic3r::Point& p) { return p.y(); }
template<> inline coord_t& x(Slic3r::Point& p) { return p.x(); }
template<> inline coord_t& y(Slic3r::Point& p) { return p.y(); }
} // pointlike
namespace shapelike {
template<> inline Slic3r::Points& contour(Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; }
template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();}
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.cbegin(); }
template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();}
template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); }
template<> inline Slic3r::ExPolygon create<Slic3r::ExPolygon>(Slic3r::Points&& contour)
{
Slic3r::ExPolygon expoly; expoly.contour.points.swap(contour);
return expoly;
}
template<> inline Slic3r::Polygon create<Slic3r::Polygon>(Slic3r::Points&& contour)
{
Slic3r::Polygon poly; poly.points.swap(contour);
return poly;
}
} // shapelike
} // libnest2d
namespace Slic3r { namespace Slic3r {
// Used as compute type. // Used as compute type.
@ -74,13 +28,22 @@ using Rational = boost::rational<boost::multiprecision::int128_t>;
using Rational = boost::rational<__int128>; using Rational = boost::rational<__int128>;
#endif #endif
template<class P>
libnest2d::RotatedBox<Point, Unit> minAreaBoundigBox_(
const P &p, MinAreaBoundigBox::PolygonLevel lvl)
{
P chull = lvl == MinAreaBoundigBox::pcConvex ?
p :
libnest2d::sl::convexHull(p);
libnest2d::removeCollinearPoints(chull);
return libnest2d::minAreaBoundingBox<P, Unit, Rational>(chull);
}
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
{ {
const Polygon &chull = pc == pcConvex ? p : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());
@ -89,11 +52,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
{ {
const ExPolygon &chull = pc == pcConvex ? p : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());
@ -102,11 +61,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc)
{ {
const Points &chull = pc == pcConvex ? pts : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(pts, pc);
libnest2d::sl::convexHull(pts);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());

View file

@ -26,12 +26,8 @@ public:
}; };
// Constructors with various types of geometry data used in Slic3r. // Constructors with various types of geometry data used in Slic3r.
// If the convexity is known apriory, pcConvex can be used to skip // If the convexity is known apriory, pcConvex can be used to skip
// convex hull calculation. It is very important that the input polygons // convex hull calculation.
// do NOT have any collinear points (except for the first and the last
// vertex being the same -- meaning a closed polygon for boost)
// To make sure this constraint is satisfied, you can call
// remove_collinear_points on the input polygon before handing over here)
explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple);

View file

@ -833,18 +833,6 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
return out; return out;
} }
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh ModelObject::full_raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
{
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
return mesh;
}
const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
{ {
@ -1822,7 +1810,7 @@ void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_le
this->set_mesh(std::move(mesh)); this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull(); TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(mesh_trafo, fix_left_handed); convex_hull.transform(mesh_trafo, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull)); m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }
@ -1834,7 +1822,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
this->set_mesh(std::move(mesh)); this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull(); TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(matrix, fix_left_handed); convex_hull.transform(matrix, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull)); m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }

View file

@ -180,8 +180,8 @@ private:
class LayerHeightProfile final : public ObjectWithTimestamp { class LayerHeightProfile final : public ObjectWithTimestamp {
public: public:
// Assign the content if the timestamp differs, don't assign an ObjectID. // Assign the content if the timestamp differs, don't assign an ObjectID.
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } } void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
std::vector<coordf_t> get() const throw() { return m_data; } std::vector<coordf_t> get() const throw() { return m_data; }
bool empty() const throw() { return m_data.empty(); } bool empty() const throw() { return m_data.empty(); }
@ -289,8 +289,6 @@ public:
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
// The same as above, but producing a lightweight indexed_triangle_set. // The same as above, but producing a lightweight indexed_triangle_set.
indexed_triangle_set raw_indexed_triangle_set() const; indexed_triangle_set raw_indexed_triangle_set() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh full_raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing. // This bounding box is only used for the actual slicing.
const BoundingBoxf3& raw_bounding_box() const; const BoundingBoxf3& raw_bounding_box() const;
@ -510,8 +508,8 @@ enum class ConversionType : int {
class FacetsAnnotation final : public ObjectWithTimestamp { class FacetsAnnotation final : public ObjectWithTimestamp {
public: public:
// Assign the content if the timestamp differs, don't assign an ObjectID. // Assign the content if the timestamp differs, don't assign an ObjectID.
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } } void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } } void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; } const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
bool set(const TriangleSelector& selector); bool set(const TriangleSelector& selector);
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const; indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
@ -686,6 +684,7 @@ protected:
friend class SLAPrint; friend class SLAPrint;
friend class Model; friend class Model;
friend class ModelObject; friend class ModelObject;
friend void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new);
// Copies IDs of both the ModelVolume and its config. // Copies IDs of both the ModelVolume and its config.
explicit ModelVolume(const ModelVolume &rhs) = default; explicit ModelVolume(const ModelVolume &rhs) = default;

View file

@ -84,6 +84,13 @@ public:
static Points _douglas_peucker(const Points &points, const double tolerance); static Points _douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double& tolerance); static Points visivalingam(const Points& pts, const double& tolerance);
inline auto begin() { return points.begin(); }
inline auto begin() const { return points.begin(); }
inline auto end() { return points.end(); }
inline auto end() const { return points.end(); }
inline auto cbegin() const { return points.begin(); }
inline auto cend() const { return points.end(); }
}; };
class MultiPoint3 class MultiPoint3

View file

@ -52,7 +52,7 @@ public:
PointType* operator->() const { return &m_data->at(m_idx).point; } PointType* operator->() const { return &m_data->at(m_idx).point; }
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; } MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); } IndexType size() const { assert(this->valid()); return m_data->size(); }
iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; } iterator& remove() { m_idx = m_data->remove(*this).m_idx; return *this; }
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); } iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
private: private:
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {} iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
@ -162,10 +162,10 @@ public:
return out; return out;
}; };
bool empty() const { return this->m_size == 0; } bool empty() const { return m_size == 0; }
size_t size() const { return this->m_size; } size_t size() const { return m_size; }
size_t capacity() const { return this->m_data.capacity(); } size_t capacity() const { return m_data.capacity(); }
bool valid() const { return this->m_size >= 3; } bool valid() const { return m_size >= 3; }
void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); } void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); }
iterator begin() { return { this, m_head }; } iterator begin() { return { this, m_head }; }

View file

@ -121,8 +121,8 @@ protected:
if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff); if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval); if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
if(this->m_stopcr.max_iterations() > 0) if(m_stopcr.max_iterations() > 0)
nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations()); nlopt_set_maxeval(nl.ptr, m_stopcr.max_iterations());
} }
template<class Fn, size_t N> template<class Fn, size_t N>

View file

@ -8,6 +8,7 @@
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <cassert> #include <cassert>
#include <optional>
namespace Slic3r { namespace opt { namespace Slic3r { namespace opt {

View file

@ -103,6 +103,7 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes. // Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
// Based on https://www.lemoda.net/c/write-png/ // Based on https://www.lemoda.net/c/write-png/
// png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY // png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY
//FIXME maybe better to use tdefl_write_image_to_png_file_in_memory() instead?
static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data) static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data)
{ {
bool result = false; bool result = false;

View file

@ -349,9 +349,7 @@ void PerimeterGenerator::process()
coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3)); coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3));
ExPolygons expp = offset2_ex( ExPolygons expp = offset2_ex(
// medial axis requires non-overlapping geometry // medial axis requires non-overlapping geometry
diff_ex(to_polygons(last), diff_ex(last, offset(offsets, float(ext_perimeter_width / 2.) + ClipperSafetyOffset)),
offset(offsets, float(ext_perimeter_width / 2.)),
true),
- float(min_width / 2.), float(min_width / 2.)); - float(min_width / 2.), float(min_width / 2.));
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
for (ExPolygon &ex : expp) for (ExPolygon &ex : expp)
@ -498,8 +496,7 @@ void PerimeterGenerator::process()
ExPolygons gaps_ex = diff_ex( ExPolygons gaps_ex = diff_ex(
//FIXME offset2 would be enough and cheaper. //FIXME offset2 would be enough and cheaper.
offset2_ex(gaps, - float(min / 2.), float(min / 2.)), offset2_ex(gaps, - float(min / 2.), float(min / 2.)),
offset2_ex(gaps, - float(max / 2.), float(max / 2.)), offset2_ex(gaps, - float(max / 2.), float(max / 2. + ClipperSafetyOffset)));
true);
ThickPolylines polylines; ThickPolylines polylines;
for (const ExPolygon &ex : gaps_ex) for (const ExPolygon &ex : gaps_ex)
ex.medial_axis(max, min, &polylines); ex.medial_axis(max, min, &polylines);
@ -514,7 +511,7 @@ void PerimeterGenerator::process()
and use zigzag). */ and use zigzag). */
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing, //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
// therefore it may cover the area, but no the volume. // therefore it may cover the area, but no the volume.
last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f)); last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
this->gap_fill->append(std::move(gap_fill.entities)); this->gap_fill->append(std::move(gap_fill.entities));
} }
} }

View file

@ -17,42 +17,42 @@ class BoundingBox;
class Line; class Line;
class MultiPoint; class MultiPoint;
class Point; class Point;
typedef Point Vector; using Vector = Point;
// Eigen types, to replace the Slic3r's own types in the future. // Eigen types, to replace the Slic3r's own types in the future.
// Vector types with a fixed point coordinate base type. // Vector types with a fixed point coordinate base type.
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd; using Vec2crd = Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd; using Vec3crd = Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int, 2, 1, Eigen::DontAlign> Vec2i; using Vec2i = Eigen::Matrix<int, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i; using Vec3i = Eigen::Matrix<int, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign> Vec2i32; using Vec2i32 = Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64; using Vec2i64 = Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i32; using Vec3i32 = Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64; using Vec3i64 = Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign>;
// Vector types with a double coordinate base type. // Vector types with a double coordinate base type.
typedef Eigen::Matrix<float, 2, 1, Eigen::DontAlign> Vec2f; using Vec2f = Eigen::Matrix<float, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f; using Vec3f = Eigen::Matrix<float, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d; using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d; using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
typedef std::vector<Point> Points; using Points = std::vector<Point>;
typedef std::vector<Point*> PointPtrs; using PointPtrs = std::vector<Point*>;
typedef std::vector<const Point*> PointConstPtrs; using PointConstPtrs = std::vector<const Point*>;
typedef std::vector<Vec3crd> Points3; using Points3 = std::vector<Vec3crd>;
typedef std::vector<Vec2d> Pointfs; using Pointfs = std::vector<Vec2d>;
typedef std::vector<Vec2d> Vec2ds; using Vec2ds = std::vector<Vec2d>;
typedef std::vector<Vec3d> Pointf3s; using Pointf3s = std::vector<Vec3d>;
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f; using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d; using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
typedef Eigen::Matrix<float, 3, 3, Eigen::DontAlign> Matrix3f; using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 3, 3, Eigen::DontAlign> Matrix3d; using Matrix3d = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>;
typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f; using Transform2f = Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d; using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f; using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d; using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); } inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
@ -101,7 +101,7 @@ template<int N, class T> using Vec = Eigen::Matrix<T, N, 1, Eigen::DontAlign, N
class Point : public Vec2crd class Point : public Vec2crd
{ {
public: public:
typedef coord_t coord_type; using coord_type = coord_t;
Point() : Vec2crd(0, 0) {} Point() : Vec2crd(0, 0) {}
Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {} Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
@ -337,7 +337,7 @@ public:
} }
private: private:
typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type; using map_type = typename std::unordered_multimap<Vec2crd, ValueType, PointHash>;
PointAccessor m_point_accessor; PointAccessor m_point_accessor;
map_type m_map; map_type m_map;
coord_t m_search_radius; coord_t m_search_radius;
@ -439,11 +439,11 @@ inline Point align_to_grid(Point coord, Point spacing, Point base)
#include <boost/polygon/polygon.hpp> #include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon { namespace boost { namespace polygon {
template <> template <>
struct geometry_concept<Slic3r::Point> { typedef point_concept type; }; struct geometry_concept<Slic3r::Point> { using type = point_concept; };
template <> template <>
struct point_traits<Slic3r::Point> { struct point_traits<Slic3r::Point> {
typedef coord_t coordinate_type; using coordinate_type = coord_t;
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
return static_cast<coordinate_type>(point((orient == HORIZONTAL) ? 0 : 1)); return static_cast<coordinate_type>(point((orient == HORIZONTAL) ? 0 : 1));
@ -452,7 +452,7 @@ namespace boost { namespace polygon {
template <> template <>
struct point_mutable_traits<Slic3r::Point> { struct point_mutable_traits<Slic3r::Point> {
typedef coord_t coordinate_type; using coordinate_type = coord_t;
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
point((orient == HORIZONTAL) ? 0 : 1) = value; point((orient == HORIZONTAL) ? 0 : 1) = value;
} }

View file

@ -33,32 +33,16 @@ Polyline Polygon::split_at_index(int index) const
return polyline; return polyline;
} }
/*
int64_t Polygon::area2x() const
{
size_t n = poly.size();
if (n < 3)
return 0;
int64_t a = 0;
for (size_t i = 0, j = n - 1; i < n; ++i)
a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1));
j = i;
}
return -a * 0.5;
}
*/
double Polygon::area(const Points &points) double Polygon::area(const Points &points)
{ {
size_t n = points.size();
if (n < 3)
return 0.;
double a = 0.; double a = 0.;
for (size_t i = 0, j = n - 1; i < n; ++i) { if (points.size() >= 3) {
a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1)); Vec2d p1 = points.back().cast<double>();
j = i; for (const Point &p : points) {
Vec2d p2 = p.cast<double>();
a += cross2(p1, p2);
p1 = p2;
}
} }
return 0.5 * a; return 0.5 * a;
} }
@ -70,7 +54,7 @@ double Polygon::area() const
bool Polygon::is_counter_clockwise() const bool Polygon::is_counter_clockwise() const
{ {
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); return ClipperLib::Orientation(this->points);
} }
bool Polygon::is_clockwise() const bool Polygon::is_clockwise() const
@ -169,19 +153,22 @@ void Polygon::triangulate_convex(Polygons* polygons) const
} }
// center of mass // center of mass
// source: https://en.wikipedia.org/wiki/Centroid
Point Polygon::centroid() const Point Polygon::centroid() const
{ {
double area_temp = this->area(); double area_sum = 0.;
double x_temp = 0; Vec2d c(0., 0.);
double y_temp = 0; if (points.size() >= 3) {
Vec2d p1 = points.back().cast<double>();
Polyline polyline = this->split_at_first_point(); for (const Point &p : points) {
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) { Vec2d p2 = p.cast<double>();
x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); double a = cross2(p1, p2);
y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() ); area_sum += a;
c += (p1 + p2) * a;
p1 = p2;
}
} }
return Point(Vec2d(c / (3. * area_sum)));
return Point(x_temp/(6*area_temp), y_temp/(6*area_temp));
} }
// find all concave vertices (i.e. having an internal angle greater than the supplied angle) // find all concave vertices (i.e. having an internal angle greater than the supplied angle)

View file

@ -72,6 +72,9 @@ public:
// Projection of a point onto the polygon. // Projection of a point onto the polygon.
Point point_projection(const Point &point) const; Point point_projection(const Point &point) const;
std::vector<float> parameter_by_length() const; std::vector<float> parameter_by_length() const;
using iterator = Points::iterator;
using const_iterator = Points::const_iterator;
}; };
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; } inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
@ -90,6 +93,8 @@ inline double total_length(const Polygons &polylines) {
return total; return total;
} }
inline double area(const Polygon &poly) { return poly.area(); }
inline double area(const Polygons &polys) inline double area(const Polygons &polys)
{ {
double s = 0.; double s = 0.;
@ -217,6 +222,24 @@ inline Polylines to_polylines(Polygons &&polys)
return polylines; return polylines;
} }
inline Polygons to_polygons(const std::vector<Points> &paths)
{
Polygons out;
out.reserve(paths.size());
for (const Points &path : paths)
out.emplace_back(path);
return out;
}
inline Polygons to_polygons(std::vector<Points> &&paths)
{
Polygons out;
out.reserve(paths.size());
for (const Points &path : paths)
out.emplace_back(std::move(path));
return out;
}
} // Slic3r } // Slic3r
// start Boost // start Boost

View file

@ -78,6 +78,9 @@ public:
bool is_closed() const { return this->points.front() == this->points.back(); } bool is_closed() const { return this->points.front() == this->points.back(); }
}; };
inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; }
inline bool operator!=(const Polyline &lhs, const Polyline &rhs) { return lhs.points != rhs.points; }
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! // Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
#ifdef PERL_UCHAR_MIN #ifdef PERL_UCHAR_MIN
class PolylineCollection class PolylineCollection
@ -124,6 +127,24 @@ inline Lines to_lines(const Polylines &polys)
return lines; return lines;
} }
inline Polylines to_polylines(const std::vector<Points> &paths)
{
Polylines out;
out.reserve(paths.size());
for (const Points &path : paths)
out.emplace_back(path);
return out;
}
inline Polylines to_polylines(std::vector<Points> &&paths)
{
Polylines out;
out.reserve(paths.size());
for (const Points &path : paths)
out.emplace_back(std::move(path));
return out;
}
inline void polylines_append(Polylines &dst, const Polylines &src) inline void polylines_append(Polylines &dst, const Polylines &src)
{ {
dst.insert(dst.end(), src.begin(), src.end()); dst.insert(dst.end(), src.begin(), src.end());

View file

@ -296,6 +296,13 @@ void Preset::normalize(DynamicPrintConfig &config)
if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled) if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled)
gap_fill_enabled->value = false; gap_fill_enabled->value = false;
} }
if (auto *first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height", false); first_layer_height && first_layer_height->percent)
if (const auto *layer_height = config.option<ConfigOptionFloat>("layer_height", false); layer_height) {
// Legacy conversion - first_layer_height moved from PrintObject setting to a Print setting, thus we are getting rid of the dependency
// of first_layer_height on PrintObject specific layer_height. Covert the first layer heigth to an absolute value.
first_layer_height->value = first_layer_height->get_abs_value(layer_height->value);
first_layer_height->percent = false;
}
} }
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config) std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
@ -617,11 +624,17 @@ const std::vector<std::string>& Preset::sla_printer_options()
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
m_type(type), m_type(type),
m_edited_preset(type, "", false), m_edited_preset(type, "", false),
#if ENABLE_PROJECT_DIRTY_STATE
m_saved_preset(type, "", false),
#endif // ENABLE_PROJECT_DIRTY_STATE
m_idx_selected(0) m_idx_selected(0)
{ {
// Insert just the default preset. // Insert just the default preset.
this->add_default_preset(keys, defaults, default_name); this->add_default_preset(keys, defaults, default_name);
m_edited_preset.config.apply(m_presets.front().config); m_edited_preset.config.apply(m_presets.front().config);
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
} }
void PresetCollection::reset(bool delete_files) void PresetCollection::reset(bool delete_files)
@ -798,7 +811,10 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset. // The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this->get_edited_preset().config.apply_only(combined_config, keys, true); this->get_edited_preset().config.apply_only(combined_config, keys, true);
this->update_dirty(); this->update_dirty();
assert(this->get_edited_preset().is_dirty); #if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
assert(this->get_edited_preset().is_dirty);
return std::make_pair(&(*it), this->get_edited_preset().is_dirty); return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
} }
if (inherits.empty()) { if (inherits.empty()) {
@ -1063,7 +1079,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
size_t PresetCollection::first_visible_idx() const size_t PresetCollection::first_visible_idx() const
{ {
size_t idx = m_default_suppressed ? m_num_default_presets : 0; size_t idx = m_default_suppressed ? m_num_default_presets : 0;
for (; idx < this->m_presets.size(); ++ idx) for (; idx < m_presets.size(); ++ idx)
if (m_presets[idx].is_visible) if (m_presets[idx].is_visible)
break; break;
if (idx == m_presets.size()) if (idx == m_presets.size())
@ -1208,6 +1224,9 @@ Preset& PresetCollection::select_preset(size_t idx)
idx = first_visible_idx(); idx = first_visible_idx();
m_idx_selected = idx; m_idx_selected = idx;
m_edited_preset = m_presets[idx]; m_edited_preset = m_presets[idx];
#if ENABLE_PROJECT_DIRTY_STATE
update_saved_preset_from_current_preset();
#endif // ENABLE_PROJECT_DIRTY_STATE
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets; bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i) for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible; m_presets[i].is_visible = default_visible;
@ -1275,7 +1294,7 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
assert(it != new_vendors.end()); assert(it != new_vendors.end());
preset.vendor = &it->second; preset.vendor = &it->second;
} }
this->m_presets.emplace(it, std::move(preset)); m_presets.emplace(it, std::move(preset));
} else } else
duplicates.emplace_back(std::move(preset.name)); duplicates.emplace_back(std::move(preset.name));
} }

View file

@ -346,6 +346,11 @@ public:
Preset& get_edited_preset() { return m_edited_preset; } Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; }
#if ENABLE_PROJECT_DIRTY_STATE
// Return the last saved preset.
const Preset& get_saved_preset() const { return m_saved_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist. // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const; PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); } PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
@ -365,8 +370,16 @@ public:
// Return a preset by an index. If the preset is active, a temporary copy is returned. // Return a preset by an index. If the preset is active, a temporary copy is returned.
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; } Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); } const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
#if ENABLE_PROJECT_DIRTY_STATE
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
update_saved_preset_from_current_preset();
}
#else
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; } void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a preset by its name. If the preset is active, a temporary copy is returned. // Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned. // If a preset is not found by its name, null is returned.
Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false); Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false);
@ -440,6 +453,16 @@ public:
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
#if ENABLE_PROJECT_DIRTY_STATE
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> saved_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
// Copy edited preset into saved preset.
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
#endif // ENABLE_PROJECT_DIRTY_STATE
// Return a sorted list of system preset names. // Return a sorted list of system preset names.
// Used for validating the "inherits" flag when importing user's config bundles. // Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets. // Returns names of all system presets including the former names of these presets.
@ -527,6 +550,11 @@ private:
std::map<std::string, std::string> m_map_system_profile_renamed; std::map<std::string, std::string> m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user. // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset; Preset m_edited_preset;
#if ENABLE_PROJECT_DIRTY_STATE
// Contains a copy of the last saved selected preset.
Preset m_saved_preset;
#endif // ENABLE_PROJECT_DIRTY_STATE
// Selected preset. // Selected preset.
size_t m_idx_selected; size_t m_idx_selected;
// Is the "- default -" preset suppressed? // Is the "- default -" preset suppressed?

View file

@ -31,6 +31,9 @@ namespace Slic3r {
template class PrintState<PrintStep, psCount>; template class PrintState<PrintStep, psCount>;
template class PrintState<PrintObjectStep, posCount>; template class PrintState<PrintObjectStep, posCount>;
PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {}
PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {}
void Print::clear() void Print::clear()
{ {
tbb::mutex::scoped_lock lock(this->state_mutex()); tbb::mutex::scoped_lock lock(this->state_mutex());
@ -39,24 +42,10 @@ void Print::clear()
for (PrintObject *object : m_objects) for (PrintObject *object : m_objects)
delete object; delete object;
m_objects.clear(); m_objects.clear();
for (PrintRegion *region : m_regions) m_print_regions.clear();
delete region;
m_regions.clear();
m_model.clear_objects(); m_model.clear_objects();
} }
PrintRegion* Print::add_region()
{
m_regions.emplace_back(new PrintRegion(this));
return m_regions.back();
}
PrintRegion* Print::add_region(const PrintRegionConfig &config)
{
m_regions.emplace_back(new PrintRegion(this, config));
return m_regions.back();
}
// Called by Print::apply(). // Called by Print::apply().
// This method only accepts PrintConfig option keys. // This method only accepts PrintConfig option keys.
bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys) bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys)
@ -273,15 +262,10 @@ bool Print::is_step_done(PrintObjectStep step) const
std::vector<unsigned int> Print::object_extruders() const std::vector<unsigned int> Print::object_extruders() const
{ {
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
extruders.reserve(m_regions.size() * 3); extruders.reserve(m_print_regions.size() * m_objects.size() * 3);
std::vector<unsigned char> region_used(m_regions.size(), false);
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes) for (const PrintRegion &region : object->all_regions())
if (! volumes_per_region.empty()) region.collect_object_printing_extruders(*this, extruders);
region_used[&volumes_per_region - &object->region_volumes.front()] = true;
for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region)
if (region_used[idx_region])
m_regions[idx_region]->collect_object_printing_extruders(extruders);
sort_remove_duplicates(extruders); sort_remove_duplicates(extruders);
return extruders; return extruders;
} }
@ -345,242 +329,6 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max; return nozzle_diameter_max;
} }
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
{
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
std::vector<ModelVolumeWithStatus> old_volumes;
old_volumes.reserve(model_object_dst.volumes.size());
for (const ModelVolume *model_volume : model_object_dst.volumes)
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
model_object_dst.volumes.clear();
model_object_dst.volumes.reserve(model_object_new.volumes.size());
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
ModelVolumeWithStatus key(model_volume_src, false);
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
// The volume was found in the old list. Just copy it.
assert(! it->second); // not consumed yet
it->second = true;
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
model_object_dst.volumes.emplace_back(model_volume_dst);
if (model_volume_dst->is_support_modifier()) {
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
model_volume_dst->set_type(model_volume_src->type());
model_volume_dst->set_transformation(model_volume_src->get_transformation());
}
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
} else {
// The volume was not found in the old list. Create a new copy.
assert(model_volume_src->is_support_modifier());
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
// Release the non-consumed old volumes (those were deleted from the new list).
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
if (! mv_with_status.second)
delete mv_with_status.first;
}
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
{
size_t i_src, i_dst;
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
if (mv_src.type() != type) {
++ i_src;
continue;
}
if (mv_dst.type() != type) {
++ i_dst;
continue;
}
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config.assign_config(mv_src.config);
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
mv_dst.supported_facets.assign(mv_src.supported_facets);
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
mv_dst.seam_facets.assign(mv_src.seam_facets);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
++ i_dst;
}
}
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
{
assert(lr_dst.size() == lr_src.size());
auto it_src = lr_src.cbegin();
for (auto &kvp_dst : lr_dst) {
const auto &kvp_src = *it_src ++;
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
kvp_dst.second = kvp_src.second;
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
if (*lv < *rv)
return true;
else if (*lv > *rv)
return false;
}
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
if (*lv != *rv)
return false;
return true;
}
struct PrintObjectTrafoAndInstances
{
Transform3d trafo;
PrintInstances instances;
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0;
// Search or insert a trafo.
auto it = trafos.emplace(trafo).first;
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
}
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
auto it2 = lr2.begin();
for (const auto &kvp1 : lr1) {
const auto &kvp2 = *it2 ++;
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
return false;
}
return true;
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
while (it_a != va.end() || it_b != vb.end()) {
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_a;
continue;
}
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_b;
continue;
}
if (it_a == va.end() || it_b == vb.end())
// va or vb contains more Tool Changes than the other.
return true;
assert(it_a->type == CustomGCode::ToolChange);
assert(it_b->type == CustomGCode::ToolChange);
if (*it_a != *it_b)
// The two Tool Changes differ.
return true;
++ it_a;
++ it_b;
}
// There is no change in custom Tool Changes.
return false;
}
// Collect diffs of configuration values at various containers,
// resolve the filament rectract overrides of extruder retract values.
void Print::config_diffs(
const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff,
DynamicPrintConfig &filament_overrides) const
{
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
{
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";
for (const t_config_option_key &opt_key : m_config.keys()) {
const ConfigOption *opt_old = m_config.option(opt_key);
assert(opt_old != nullptr);
const ConfigOption *opt_new = new_full_config.option(opt_key);
// assert(opt_new != nullptr);
if (opt_new == nullptr)
//FIXME This may happen when executing some test cases.
continue;
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
if (*opt_old == *opt_copy)
delete opt_copy;
else {
filament_overrides.set_key_value(opt_key, opt_copy);
print_diff.emplace_back(opt_key);
}
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
}
// Collect changes to object and region configs.
object_diff = m_default_object_config.diff(new_full_config);
region_diff = m_default_region_config.diff(new_full_config);
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = m_full_print_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old)
full_config_diff.emplace_back(opt_key);
}
}
std::vector<ObjectID> Print::print_object_ids() const std::vector<ObjectID> Print::print_object_ids() const
{ {
std::vector<ObjectID> out; std::vector<ObjectID> out;
@ -591,594 +339,6 @@ std::vector<ObjectID> Print::print_object_ids() const
return out; return out;
} }
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Normalize the config.
new_full_config.option("print_settings_id", true);
new_full_config.option("filament_settings_id", true);
new_full_config.option("printer_settings_id", true);
new_full_config.option("physical_printer_settings_id", true);
new_full_config.normalize_fdm();
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
DynamicPrintConfig filament_overrides;
this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides);
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated)
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
update_apply_status(false);
// Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(this->state_mutex());
// The following call may stop the background processing.
if (! print_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
// see "Placeholders do not respect filament overrides." GH issue #3649
m_placeholder_parser.apply_config(filament_overrides);
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
}
class LayerRanges
{
public:
LayerRanges() {}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
void assign(const t_layer_config_ranges &in) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig *cfg = &range.second.get();
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR
// assert(it != m_ranges.end());
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
if (it == m_ranges.end() ||
std::abs(it->first.first - range.first) > EPSILON ||
std::abs(it->first.second - range.second) > EPSILON )
return nullptr; // desired range doesn't found
return (it == m_ranges.end()) ? nullptr : it->second;
}
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
private:
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
};
struct ModelObjectStatus {
enum Status {
Unknown,
Old,
New,
Moved,
Deleted,
};
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
std::set<ModelObjectStatus> model_object_status;
// 1) Synchronize model objects.
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// Stop background processing.
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
update_apply_status(object->invalidate_all_steps());
delete object;
}
m_objects.clear();
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport));
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
m_model.objects.back()->set_model(&m_model);
}
} else {
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
this->call_cancel_callback();
update_apply_status(this->invalidate_step(psGCodeExport));
// Second create a new list of objects.
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
m_model.objects.clear();
m_model.objects.reserve(model.objects.size());
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
for (const ModelObject *mobj : model.objects) {
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
m_model.objects.back()->set_model(&m_model);
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
}
}
bool deleted_any = false;
for (ModelObject *&model_object : model_objects_old) {
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
deleted_any = true;
} else
// Do not delete this ModelObject instance.
model_object = nullptr;
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
PrintObjectPtrs print_objects_old = std::move(m_objects);
m_objects.clear();
m_objects.reserve(print_objects_old.size());
for (PrintObject *print_object : print_objects_old) {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
if (it_status->status == ModelObjectStatus::Deleted) {
update_apply_status(print_object->invalidate_all_steps());
delete print_object;
} else
m_objects.emplace_back(print_object);
}
for (ModelObject *model_object : model_objects_old)
delete model_object;
}
}
}
// 2) Map print objects including their transformation matrices.
struct PrintObjectStatus {
enum Status {
Unknown,
Deleted,
Reused,
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
// Search by id.
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
};
std::multiset<PrintObjectStatus> print_object_status;
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
if (supports_differ) {
this->call_cancel_callback();
update_apply_status(false);
}
// Invalidate just the supports step.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
if (supports_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
update_apply_status(this->invalidate_step(psGCodeExport));
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize Object's config.
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
t_config_option_keys diff = it->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
it->print_object->config_apply_only(new_config, diff, true);
}
}
}
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
}
}
// 4) Generate PrintObjects from ModelObjects and their instances.
{
PrintObjectPtrs print_objects_new;
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
bool new_objects = false;
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
std::vector<const PrintObjectStatus*> old;
if (range.first != range.second) {
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
for (auto it = range.first; it != range.second; ++ it)
if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
print_object_last = print_object;
};
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
}
continue;
}
// Complex case, try to merge the two lists.
// Sort the old lexicographically by their trafos.
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
auto it_old = old.begin();
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
if (it_old != old.end())
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else {
// The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object);
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
}
}
}
if (m_objects != print_objects_new) {
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects || deleted_objects)
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
if (new_objects)
update_apply_status(false);
}
print_object_status.clear();
}
// 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions.
// Update reference counts of regions from the remaining PrintObjects and their volumes.
// Regions with zero references could and should be reused.
for (PrintRegion *region : m_regions)
region->m_refcnt = 0;
for (PrintObject *print_object : m_objects) {
int idx_region = 0;
for (const auto &volumes : print_object->region_volumes) {
if (! volumes.empty())
++ m_regions[idx_region]->m_refcnt;
++ idx_region;
}
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
PrintRegion &region = *m_regions[region_id];
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
if (region_id < print_object->region_volumes.size()) {
for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
// Regions were split. Reset this print_object.
goto print_object_end;
} else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *m_regions[i];
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
// Regions were merged. Reset this print_object.
goto print_object_end;
}
this_region_config_set = true;
}
}
}
continue;
print_object_end:
update_apply_status(print_object->invalidate_all_steps());
// Decrease the references to regions from this volume.
int ireg = 0;
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
if (! volumes.empty())
-- m_regions[ireg]->m_refcnt;
++ ireg;
}
print_object->region_volumes.clear();
}
if (this_region_config_set) {
t_config_option_keys diff = region.config().diff(this_region_config);
if (! diff.empty()) {
// Stop the background process before assigning new configuration to the regions.
for (PrintObject *print_object : m_objects)
if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty())
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff));
region.config_apply_only(this_region_config, diff, false);
}
}
}
// Possibly add new regions for the newly added or resetted PrintObjects.
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
std::vector<int> regions_in_object;
regions_in_object.reserve(64);
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
PrintObject &print_object = *m_objects[i];
bool fresh = print_object.region_volumes.empty();
unsigned int volume_id = 0;
unsigned int idx_region_in_object = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1;
if (&print_object == &print_object0) {
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
// Find an existing print region with the same config.
int idx_empty_slot = -1;
for (int i = 0; i < (int)m_regions.size(); ++ i) {
if (m_regions[i]->m_refcnt == 0) {
if (idx_empty_slot == -1)
idx_empty_slot = i;
} else if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
if (idx_empty_slot == -1) {
region_id = (int)m_regions.size();
this->add_region(config);
} else {
region_id = idx_empty_slot;
m_regions[region_id]->set_config(std::move(config));
}
}
regions_in_object.emplace_back(region_id);
} else
region_id = regions_in_object[idx_region_in_object ++];
// Assign volume to a region.
if (fresh) {
if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id, it_range->first);
}
}
++ volume_id;
}
}
}
// Update SlicingParameters for each object where the SlicingParameters is not valid.
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
// (posSlicing and posSupportMaterial was invalidated).
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
return static_cast<ApplyStatus>(apply_status);
}
bool Print::has_infinite_skirt() const bool Print::has_infinite_skirt() const
{ {
return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1); return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
@ -1253,10 +413,12 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
// Precondition: Print::validate() requires the Print::apply() to be called its invocation. // Precondition: Print::validate() requires the Print::apply() to be called its invocation.
std::string Print::validate(std::string* warning) const std::string Print::validate(std::string* warning) const
{ {
std::vector<unsigned int> extruders = this->extruders();
if (m_objects.empty()) if (m_objects.empty())
return L("All objects are outside of the print volume."); return L("All objects are outside of the print volume.");
if (extruders().empty()) if (extruders.empty())
return L("The supplied settings will cause an empty print."); return L("The supplied settings will cause an empty print.");
if (m_config.complete_objects) { if (m_config.complete_objects) {
@ -1275,20 +437,16 @@ std::string Print::validate(std::string* warning) const
return L("Only a single object may be printed at a time in Spiral Vase mode. " return L("Only a single object may be printed at a time in Spiral Vase mode. "
"Either remove all but the last object, or enable sequential mode by \"complete_objects\"."); "Either remove all but the last object, or enable sequential mode by \"complete_objects\".");
assert(m_objects.size() == 1); assert(m_objects.size() == 1);
size_t num_regions = 0; if (m_objects.front()->all_regions().size() > 1)
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : m_objects.front()->region_volumes)
if (! volumes_per_region.empty())
++ num_regions;
if (num_regions > 1)
return L("The Spiral Vase option can only be used when printing single material objects."); return L("The Spiral Vase option can only be used when printing single material objects.");
} }
if (this->has_wipe_tower() && ! m_objects.empty()) { if (this->has_wipe_tower() && ! m_objects.empty()) {
// Make sure all extruders use same diameter filament and have the same nozzle diameter // Make sure all extruders use same diameter filament and have the same nozzle diameter
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front()); double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
double first_filament_diam = m_config.filament_diameter.get_at(extruders().front()); double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
for (const auto& extruder_idx : extruders()) { for (const auto& extruder_idx : extruders) {
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx); double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
double filament_diam = m_config.filament_diameter.get_at(extruder_idx); double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
@ -1306,7 +464,7 @@ std::string Print::validate(std::string* warning) const
return L("Ooze prevention is currently not supported with the wipe tower enabled."); return L("Ooze prevention is currently not supported with the wipe tower enabled.");
if (m_config.use_volumetric_e) if (m_config.use_volumetric_e)
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
if (m_config.complete_objects && extruders().size() > 1) if (m_config.complete_objects && extruders.size() > 1)
return L("The Wipe Tower is currently not supported for multimaterial sequential prints."); return L("The Wipe Tower is currently not supported for multimaterial sequential prints.");
if (m_objects.size() > 1) { if (m_objects.size() > 1) {
@ -1386,8 +544,6 @@ std::string Print::validate(std::string* warning) const
} }
{ {
std::vector<unsigned int> extruders = this->extruders();
// Find the smallest used nozzle diameter and the number of unique nozzle diameters. // Find the smallest used nozzle diameter and the number of unique nozzle diameters.
double min_nozzle_diameter = std::numeric_limits<double>::max(); double min_nozzle_diameter = std::numeric_limits<double>::max();
double max_nozzle_diameter = 0; double max_nozzle_diameter = 0;
@ -1464,7 +620,8 @@ std::string Print::validate(std::string* warning) const
} }
// validate first_layer_height // validate first_layer_height
double first_layer_height = object->config().get_abs_value("first_layer_height"); assert(! m_config.first_layer_height.percent);
double first_layer_height = m_config.first_layer_height.value;
double first_layer_min_nozzle_diameter; double first_layer_min_nozzle_diameter;
if (object->has_raft()) { if (object->has_raft()) {
// if we have raft layers, only support material extruder is used on first layer // if we have raft layers, only support material extruder is used on first layer
@ -1493,8 +650,8 @@ std::string Print::validate(std::string* warning) const
if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg)) if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg))
return err_msg; return err_msg;
for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" }) for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" })
for (size_t i = 0; i < object->region_volumes.size(); ++ i) for (const PrintRegion &region : object->all_regions())
if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg)) if (! validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
return err_msg; return err_msg;
} }
} }
@ -1561,16 +718,15 @@ BoundingBox Print::total_bounding_box() const
double Print::skirt_first_layer_height() const double Print::skirt_first_layer_height() const
{ {
if (m_objects.empty()) assert(! m_config.first_layer_height.percent);
throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects"); return m_config.first_layer_height.value;
return m_objects.front()->config().get_abs_value("first_layer_height");
} }
Flow Print::brim_flow() const Flow Print::brim_flow() const
{ {
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
if (width.value == 0) if (width.value == 0)
width = m_regions.front()->config().perimeter_extrusion_width; width = m_print_regions.front()->config().perimeter_extrusion_width;
if (width.value == 0) if (width.value == 0)
width = m_objects.front()->config().extrusion_width; width = m_objects.front()->config().extrusion_width;
@ -1582,7 +738,7 @@ Flow Print::brim_flow() const
return Flow::new_from_config_width( return Flow::new_from_config_width(
frPerimeter, frPerimeter,
width, width,
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), (float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().perimeter_extruder-1),
(float)this->skirt_first_layer_height()); (float)this->skirt_first_layer_height());
} }
@ -1590,7 +746,7 @@ Flow Print::skirt_flow() const
{ {
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
if (width.value == 0) if (width.value == 0)
width = m_regions.front()->config().perimeter_extrusion_width; width = m_print_regions.front()->config().perimeter_extrusion_width;
if (width.value == 0) if (width.value == 0)
width = m_objects.front()->config().extrusion_width; width = m_objects.front()->config().extrusion_width;

View file

@ -15,6 +15,9 @@
#include "libslic3r.h" #include "libslic3r.h"
#include <functional>
#include <set>
namespace Slic3r { namespace Slic3r {
class Print; class Print;
@ -57,12 +60,20 @@ enum PrintObjectStep {
// sharing the same config (including the same assigned extruder(s)) // sharing the same config (including the same assigned extruder(s))
class PrintRegion class PrintRegion
{ {
friend class Print; public:
PrintRegion() = default;
PrintRegion(const PrintRegionConfig &config);
PrintRegion(const PrintRegionConfig &config, const size_t config_hash) : m_config(config), m_config_hash(config_hash) {}
PrintRegion(PrintRegionConfig &&config);
PrintRegion(PrintRegionConfig &&config, const size_t config_hash) : m_config(std::move(config)), m_config_hash(config_hash) {}
~PrintRegion() = default;
// Methods NOT modifying the PrintRegion's state: // Methods NOT modifying the PrintRegion's state:
public: public:
const Print* print() const { return m_print; } const PrintRegionConfig& config() const throw() { return m_config; }
const PrintRegionConfig& config() const { return m_config; } size_t config_hash() const throw() { return m_config_hash; }
// Identifier of this PrintRegion in the list of Print::m_print_regions.
int print_region_id() const throw() { return m_print_region_id; }
// 1-based extruder identifier for this region and role. // 1-based extruder identifier for this region and role.
unsigned int extruder(FlowRole role) const; unsigned int extruder(FlowRole role) const;
Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const;
@ -72,29 +83,25 @@ public:
coordf_t bridging_height_avg(const PrintConfig &print_config) const; coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Collect 0-based extruder indices used to print this region's object. // Collect 0-based extruder indices used to print this region's object.
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const; void collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const;
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, const bool has_brim, std::vector<unsigned int> &object_extruders); static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, const bool has_brim, std::vector<unsigned int> &object_extruders);
// Methods modifying the PrintRegion's state: // Methods modifying the PrintRegion's state:
public: public:
Print* print() { return m_print; } void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); }
void set_config(const PrintRegionConfig &config) { m_config = config; } void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); }
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
{ this->m_config.apply_only(other, keys, ignore_nonexistent); } { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); }
protected:
size_t m_refcnt;
private: private:
Print *m_print; friend Print;
PrintRegionConfig m_config; PrintRegionConfig m_config;
size_t m_config_hash;
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {} int m_print_region_id = -1;
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
~PrintRegion() = default;
}; };
inline bool operator==(const PrintRegion &lhs, const PrintRegion &rhs) { return lhs.config_hash() == rhs.config_hash() && lhs.config() == rhs.config(); }
inline bool operator!=(const PrintRegion &lhs, const PrintRegion &rhs) { return ! (lhs == rhs); }
template<typename T> template<typename T>
class ConstVectorOfPtrsAdaptor { class ConstVectorOfPtrsAdaptor {
public: public:
@ -145,15 +152,40 @@ struct PrintInstance
typedef std::vector<PrintInstance> PrintInstances; typedef std::vector<PrintInstance> PrintInstances;
// Region and its volumes (printing volumes or modifier volumes)
struct PrintRegionVolumes
{
// Single volume + Z range assigned to a region.
struct VolumeWithZRange {
// Z range to slice this ModelVolume over.
t_layer_height_range layer_height_range;
// Index of a ModelVolume inside its parent ModelObject.
int volume_idx;
};
// Overriding one region with some other extruder, producing another region.
// The region is owned by PrintObject::m_all_regions.
struct ExtruderOverride {
unsigned int extruder;
// const PrintRegion *region;
};
// The region is owned by PrintObject::m_all_regions.
// const PrintRegion *region;
// Possible overrides of the default region extruder.
std::vector<ExtruderOverride> overrides;
// List of ModelVolume indices and layer ranges of thereof.
std::vector<VolumeWithZRange> volumes;
// Is this region printing in any layer?
// bool printing { false };
};
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount> class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
{ {
private: // Prevents erroneous use by other classes. private: // Prevents erroneous use by other classes.
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited; typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
public: public:
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane. // Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
const Vec3crd& size() const { return m_size; } const Vec3crd& size() const { return m_size; }
const PrintObjectConfig& config() const { return m_config; } const PrintObjectConfig& config() const { return m_config; }
@ -180,9 +212,9 @@ public:
// adds region_id, too, if necessary // adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
if (region_id >= region_volumes.size()) if (region_id >= m_region_volumes.size())
region_volumes.resize(region_id + 1); m_region_volumes.resize(region_id + 1);
region_volumes[region_id].emplace_back(layer_range, volume_id); m_region_volumes[region_id].volumes.push_back({ layer_range, volume_id });
} }
// This is the *total* layer count (including support layers) // This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id // this value is not supposed to be compared with Layer::id
@ -213,7 +245,7 @@ public:
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters. // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
// Returns true, if the layer_height_profile was changed. // Returns true, if the layer_height_profile was changed.
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile); static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
// Collect the slicing parameters, to be used by variable layer thickness algorithm, // Collect the slicing parameters, to be used by variable layer thickness algorithm,
// by the interactive layer height editor and by the printing process itself. // by the interactive layer height editor and by the printing process itself.
@ -222,6 +254,11 @@ public:
const SlicingParameters& slicing_parameters() const { return m_slicing_params; } const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
size_t num_printing_regions() const throw() { return m_all_regions.size(); }
const PrintRegion& printing_region(size_t idx) const throw() { return *m_all_regions[idx]; }
//FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end.
std::vector<std::reference_wrapper<const PrintRegion>> all_regions() const;
bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; } bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; }
bool has_raft() const { return m_config.raft_layers > 0; } bool has_raft() const { return m_config.raft_layers > 0; }
bool has_support_material() const { return this->has_support() || this->has_raft(); } bool has_support_material() const { return this->has_support() || this->has_raft(); }
@ -247,8 +284,8 @@ private:
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances); PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
~PrintObject() = default; ~PrintObject() = default;
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); }
PrintBase::ApplyStatus set_instances(PrintInstances &&instances); PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
// Invalidates the step, and its depending steps in PrintObject and Print. // Invalidates the step, and its depending steps in PrintObject and Print.
bool invalidate_step(PrintObjectStep step); bool invalidate_step(PrintObjectStep step);
@ -296,6 +333,10 @@ private:
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system. // This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
Point m_center_offset; Point m_center_offset;
std::vector<std::unique_ptr<PrintRegion>> m_all_regions;
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<PrintRegionVolumes> m_region_volumes;
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
LayerPtrs m_layers; LayerPtrs m_layers;
SupportLayerPtrs m_support_layers; SupportLayerPtrs m_support_layers;
@ -366,7 +407,7 @@ struct PrintStatistics
double total_weight; double total_weight;
double total_wipe_tower_cost; double total_wipe_tower_cost;
double total_wipe_tower_filament; double total_wipe_tower_filament;
std::map<size_t, float> filament_stats; std::map<size_t, double> filament_stats;
// Config with the filled in print statistics. // Config with the filled in print statistics.
DynamicConfig config() const; DynamicConfig config() const;
@ -395,11 +436,13 @@ class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintObject>
}; };
typedef std::vector<PrintRegion*> PrintRegionPtrs; typedef std::vector<PrintRegion*> PrintRegionPtrs;
/*
typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs; typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs;
class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> { class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> {
friend Print; friend Print;
ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {} ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {}
}; };
*/
// The complete print tray with possibly multiple objects. // The complete print tray with possibly multiple objects.
class Print : public PrintBaseWithState<PrintStep, psCount> class Print : public PrintBaseWithState<PrintStep, psCount>
@ -470,14 +513,14 @@ public:
[object_id](const PrintObject *obj) { return obj->id() == object_id; }); [object_id](const PrintObject *obj) { return obj->id() == object_id; });
return (it == m_objects.end()) ? nullptr : *it; return (it == m_objects.end()) ? nullptr : *it;
} }
ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); } // ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); }
// How many of PrintObject::copies() over all print objects are there? // How many of PrintObject::copies() over all print objects are there?
// If zero, then the print is empty and the print shall not be executed. // If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const; unsigned int num_object_instances() const;
// For Perl bindings. // For Perl bindings.
PrintObjectPtrs& objects_mutable() { return m_objects; } PrintObjectPtrs& objects_mutable() { return m_objects; }
PrintRegionPtrs& regions_mutable() { return m_regions; } PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; } const ExtrusionEntityCollection& brim() const { return m_brim; }
@ -498,26 +541,15 @@ public:
std::string output_filename(const std::string &filename_base = std::string()) const override; std::string output_filename(const std::string &filename_base = std::string()) const override;
// Accessed by SupportMaterial size_t num_print_regions() const throw() { return m_print_regions.size(); }
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
protected: protected:
// methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
PrintRegion* add_region();
PrintRegion* add_region(const PrintRegionConfig &config);
// Invalidates the step, and its depending steps in Print. // Invalidates the step, and its depending steps in Print.
bool invalidate_step(PrintStep step); bool invalidate_step(PrintStep step);
private: private:
void config_diffs(
const DynamicPrintConfig &new_full_config,
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
t_config_option_keys &full_config_diff,
DynamicPrintConfig &filament_overrides) const;
bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector<t_config_option_key> &opt_keys); bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector<t_config_option_key> &opt_keys);
void _make_skirt(); void _make_skirt();
@ -529,14 +561,11 @@ private:
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners() const; std::vector<Point> first_layer_wipe_tower_corners() const;
// Declared here to have access to Model / ModelObject / ModelInstance
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
PrintConfig m_config; PrintConfig m_config;
PrintObjectConfig m_default_object_config; PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config; PrintRegionConfig m_default_region_config;
PrintObjectPtrs m_objects; PrintObjectPtrs m_objects;
PrintRegionPtrs m_regions; PrintRegionPtrs m_print_regions;
// Ordered collections of extrusion paths to build skirt loops and brim. // Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt; ExtrusionEntityCollection m_skirt;

View file

@ -0,0 +1,824 @@
#include "Model.hpp"
#include "Print.hpp"
namespace Slic3r {
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
// Friend to ModelVolume to allow copying.
// static is not accepted by gcc if declared as a friend of ModelObject.
/* static */ void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
{
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
std::vector<ModelVolumeWithStatus> old_volumes;
old_volumes.reserve(model_object_dst.volumes.size());
for (const ModelVolume *model_volume : model_object_dst.volumes)
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
model_object_dst.volumes.clear();
model_object_dst.volumes.reserve(model_object_new.volumes.size());
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
ModelVolumeWithStatus key(model_volume_src, false);
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
// The volume was found in the old list. Just copy it.
assert(! it->second); // not consumed yet
it->second = true;
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
model_object_dst.volumes.emplace_back(model_volume_dst);
if (model_volume_dst->is_support_modifier()) {
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
model_volume_dst->set_type(model_volume_src->type());
model_volume_dst->set_transformation(model_volume_src->get_transformation());
}
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
} else {
// The volume was not found in the old list. Create a new copy.
assert(model_volume_src->is_support_modifier());
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
// Release the non-consumed old volumes (those were deleted from the new list).
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
if (! mv_with_status.second)
delete mv_with_status.first;
}
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
{
size_t i_src, i_dst;
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
if (mv_src.type() != type) {
++ i_src;
continue;
}
if (mv_dst.type() != type) {
++ i_dst;
continue;
}
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config.assign_config(mv_src.config);
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
mv_dst.supported_facets.assign(mv_src.supported_facets);
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
mv_dst.seam_facets.assign(mv_src.seam_facets);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
++ i_dst;
}
}
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
{
assert(lr_dst.size() == lr_src.size());
auto it_src = lr_src.cbegin();
for (auto &kvp_dst : lr_dst) {
const auto &kvp_src = *it_src ++;
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
kvp_dst.second = kvp_src.second;
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
if (*lv < *rv)
return true;
else if (*lv > *rv)
return false;
}
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
const T *rv = rhs.data();
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
if (*lv != *rv)
return false;
return true;
}
struct PrintObjectTrafoAndInstances
{
Transform3d trafo;
PrintInstances instances;
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Reset the XY axes of the transformation.
trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0;
// Search or insert a trafo.
auto it = trafos.emplace(trafo).first;
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
}
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
auto it2 = lr2.begin();
for (const auto &kvp1 : lr1) {
const auto &kvp2 = *it2 ++;
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
return false;
}
return true;
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
while (it_a != va.end() || it_b != vb.end()) {
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_a;
continue;
}
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
// Skip any CustomGCode items, which are not tool changes.
++ it_b;
continue;
}
if (it_a == va.end() || it_b == vb.end())
// va or vb contains more Tool Changes than the other.
return true;
assert(it_a->type == CustomGCode::ToolChange);
assert(it_b->type == CustomGCode::ToolChange);
if (*it_a != *it_b)
// The two Tool Changes differ.
return true;
++ it_a;
++ it_b;
}
// There is no change in custom Tool Changes.
return false;
}
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
static t_config_option_keys print_config_diffs(
const PrintConfig &current_config,
const DynamicPrintConfig &new_full_config,
DynamicPrintConfig &filament_overrides)
{
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";
t_config_option_keys print_diff;
for (const t_config_option_key &opt_key : current_config.keys()) {
const ConfigOption *opt_old = current_config.option(opt_key);
assert(opt_old != nullptr);
const ConfigOption *opt_new = new_full_config.option(opt_key);
// assert(opt_new != nullptr);
if (opt_new == nullptr)
//FIXME This may happen when executing some test cases.
continue;
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
if (*opt_old == *opt_copy)
delete opt_copy;
else {
filament_overrides.set_key_value(opt_key, opt_copy);
print_diff.emplace_back(opt_key);
}
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
return print_diff;
}
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &current_full_config, const DynamicPrintConfig &new_full_config)
{
t_config_option_keys full_config_diff;
for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = current_full_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old)
full_config_diff.emplace_back(opt_key);
}
return full_config_diff;
}
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Normalize the config.
new_full_config.option("print_settings_id", true);
new_full_config.option("filament_settings_id", true);
new_full_config.option("printer_settings_id", true);
new_full_config.option("physical_printer_settings_id", true);
new_full_config.normalize_fdm();
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides;
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides);
t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config);
// Collect changes to object and region configs.
t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);
t_config_option_keys region_diff = m_default_region_config.diff(new_full_config);
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated)
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
update_apply_status(false);
// Grab the lock for the Print / PrintObject milestones.
tbb::mutex::scoped_lock lock(this->state_mutex());
// The following call may stop the background processing.
if (! print_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
// see "Placeholders do not respect filament overrides." GH issue #3649
m_placeholder_parser.apply_config(filament_overrides);
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
}
class LayerRanges
{
public:
LayerRanges() {}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
void assign(const t_layer_config_ranges &in) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig *cfg = &range.second.get();
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR
// assert(it != m_ranges.end());
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
if (it == m_ranges.end() ||
std::abs(it->first.first - range.first) > EPSILON ||
std::abs(it->first.second - range.second) > EPSILON )
return nullptr; // desired range doesn't found
return (it == m_ranges.end()) ? nullptr : it->second;
}
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
private:
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
};
struct ModelObjectStatus {
enum Status {
Unknown,
Old,
New,
Moved,
Deleted,
};
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
std::set<ModelObjectStatus> model_object_status;
// 1) Synchronize model objects.
if (model.id() != m_model.id()) {
// Kill everything, initialize from scratch.
// Stop background processing.
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
for (PrintObject *object : m_objects) {
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
update_apply_status(object->invalidate_all_steps());
delete object;
}
m_objects.clear();
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(num_extruders_changed ||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport));
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
m_model.objects.back()->set_model(&m_model);
}
} else {
// Reorder the objects, add new objects.
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
this->call_cancel_callback();
update_apply_status(this->invalidate_step(psGCodeExport));
// Second create a new list of objects.
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
m_model.objects.clear();
m_model.objects.reserve(model.objects.size());
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
for (const ModelObject *mobj : model.objects) {
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
m_model.objects.back()->set_model(&m_model);
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
m_model.objects.emplace_back(*it);
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
}
}
bool deleted_any = false;
for (ModelObject *&model_object : model_objects_old) {
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
deleted_any = true;
} else
// Do not delete this ModelObject instance.
model_object = nullptr;
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
PrintObjectPtrs print_objects_old = std::move(m_objects);
m_objects.clear();
m_objects.reserve(print_objects_old.size());
for (PrintObject *print_object : print_objects_old) {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
if (it_status->status == ModelObjectStatus::Deleted) {
update_apply_status(print_object->invalidate_all_steps());
delete print_object;
} else
m_objects.emplace_back(print_object);
}
for (ModelObject *model_object : model_objects_old)
delete model_object;
}
}
}
// 2) Map print objects including their transformation matrices.
struct PrintObjectStatus {
enum Status {
Unknown,
Deleted,
Reused,
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
// Search by id.
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
};
std::multiset<PrintObjectStatus> print_object_status;
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
if (supports_differ) {
this->call_cancel_callback();
update_apply_status(false);
}
// Invalidate just the supports step.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
if (supports_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
update_apply_status(this->invalidate_step(psGCodeExport));
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize Object's config.
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
t_config_option_keys diff = it->print_object->config().diff(new_config);
if (! diff.empty()) {
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
it->print_object->config_apply_only(new_config, diff, true);
}
}
}
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
model_object.invalidate_bounding_box();
// Synchronize the content of instances.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
}
}
// 4) Generate PrintObjects from ModelObjects and their instances.
bool print_regions_reshuffled = false;
{
PrintObjectPtrs print_objects_new;
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
bool new_objects = false;
// Walk over all new model objects and check, whether there are matching PrintObjects.
for (ModelObject *model_object : m_model.objects) {
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
std::vector<const PrintObjectStatus*> old;
if (range.first != range.second) {
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
for (auto it = range.first; it != range.second; ++ it)
if (it->status != PrintObjectStatus::Deleted)
old.emplace_back(&(*it));
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
print_object_last = print_object;
};
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
}
continue;
}
// Complex case, try to merge the two lists.
// Sort the old lexicographically by their trafos.
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
auto it_old = old.begin();
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
print_object_apply_config(print_object);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
new_objects = true;
if (it_old != old.end())
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else {
// The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object);
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
}
}
}
if (m_objects != print_objects_new) {
this->call_cancel_callback();
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects || deleted_objects)
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
if (new_objects)
update_apply_status(false);
print_regions_reshuffled = true;
}
print_object_status.clear();
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
for (PrintObject *print_object : m_objects) {
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
bool some_object_region_modified = false;
bool regions_merged = false;
for (size_t region_id = 0; region_id < print_object->m_region_volumes.size(); ++ region_id) {
PrintRegion &region = *print_object->m_all_regions[region_id];
PrintRegionConfig region_config;
bool region_config_set = false;
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : print_object->m_region_volumes[region_id].volumes) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_w_zrange.volume_idx];
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_w_zrange.layer_height_range);
PrintRegionConfig this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
if (region_config_set) {
if (this_region_config != region_config) {
regions_merged = true;
break;
}
} else {
region_config = std::move(this_region_config);
region_config_set = true;
}
}
if (regions_merged)
break;
size_t region_config_hash = region_config.hash();
bool modified = region.config_hash() != region_config_hash || region.config() != region_config;
some_object_region_modified |= modified;
if (some_object_region_modified)
// Verify whether this region was not merged with some other region.
for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *print_object->m_all_regions[i];
if (region_other.config_hash() == region_config_hash && region_other.config() == region_config) {
// Regions were merged. Reset this print_object.
regions_merged = true;
break;
}
}
if (modified) {
// Stop the background process before assigning new configuration to the regions.
t_config_option_keys diff = region.config().diff(region_config);
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), region_config, diff));
region.config_apply_only(region_config, diff, false);
}
}
if (regions_merged) {
// Two regions of a single object were either split or merged. This invalidates the whole slicing.
update_apply_status(print_object->invalidate_all_steps());
print_object->m_region_volumes.clear();
}
}
// Possibly add new regions for the newly added or resetted PrintObjects.
for (size_t idx_print_object = 0; idx_print_object < m_objects.size();) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
if (print_object0.m_region_volumes.empty()) {
// Fresh or completely invalidated print_object. Assign regions.
unsigned int volume_id = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1;
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
size_t hash = config.hash();
for (size_t i = 0; i < print_object0.m_all_regions.size(); ++ i)
if (hash == print_object0.m_all_regions[i]->config_hash() && config == *print_object0.m_all_regions[i]) {
region_id = int(i);
break;
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
region_id = int(print_object0.m_all_regions.size());
print_object0.m_all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash));
}
print_object0.add_region_volume(region_id, volume_id, it_range->first);
}
++ volume_id;
}
print_regions_reshuffled = true;
}
for (++ idx_print_object; idx_print_object < m_objects.size() && m_objects[idx_print_object]->model_object() == &model_object; ++ idx_print_object) {
PrintObject &print_object = *m_objects[idx_print_object];
if (print_object.m_region_volumes.empty()) {
// Copy region volumes and regions from print_object0.
print_object.m_region_volumes = print_object0.m_region_volumes;
print_object.m_all_regions.reserve(print_object0.m_all_regions.size());
for (const std::unique_ptr<Slic3r::PrintRegion> &region : print_object0.m_all_regions)
print_object.m_all_regions.emplace_back(std::make_unique<PrintRegion>(*region));
print_regions_reshuffled = true;
}
}
}
if (print_regions_reshuffled) {
// Update Print::m_print_regions from objects.
struct cmp { bool operator() (const PrintRegion *l, const PrintRegion *r) const { return l->config_hash() == r->config_hash() && l->config() == r->config(); } };
std::set<const PrintRegion*, cmp> region_set;
m_print_regions.clear();
for (PrintObject *print_object : m_objects)
for (std::unique_ptr<Slic3r::PrintRegion> &print_region : print_object->m_all_regions)
if (auto it = region_set.find(print_region.get()); it == region_set.end()) {
int print_region_id = int(m_print_regions.size());
m_print_regions.emplace_back(print_region.get());
print_region->m_print_region_id = print_region_id;
} else {
print_region->m_print_region_id = (*it)->print_region_id();
}
}
// Update SlicingParameters for each object where the SlicingParameters is not valid.
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
// (posSlicing and posSupportMaterial was invalidated).
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
return static_cast<ApplyStatus>(apply_status);
}
} // namespace Slic3r

View file

@ -7,6 +7,7 @@
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <float.h> #include <float.h>
@ -18,6 +19,152 @@ namespace Slic3r {
#define L(s) (s) #define L(s) (s)
#define _(s) Slic3r::I18N::translate(s) #define _(s) Slic3r::I18N::translate(s)
static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values &enum_keys_map)
{
t_config_enum_names names;
int cnt = 0;
for (const auto& kvp : enum_keys_map)
cnt = std::max(cnt, kvp.second);
cnt += 1;
names.assign(cnt, "");
for (const auto& kvp : enum_keys_map)
names[kvp.second] = kvp.first;
return names;
}
#define CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NAME) \
static t_config_enum_names s_keys_names_##NAME = enum_names_from_keys_map(s_keys_map_##NAME); \
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values() { return s_keys_map_##NAME; } \
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names() { return s_keys_names_##NAME; }
static t_config_enum_values s_keys_map_PrinterTechnology {
{ "FFF", ptFFF },
{ "SLA", ptSLA }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrinterTechnology)
static t_config_enum_values s_keys_map_GCodeFlavor {
{ "reprap", gcfRepRapSprinter },
{ "reprapfirmware", gcfRepRapFirmware },
{ "repetier", gcfRepetier },
{ "teacup", gcfTeacup },
{ "makerware", gcfMakerWare },
{ "marlin", gcfMarlinLegacy },
{ "marlinfirmware", gcfMarlinFirmware },
{ "sailfish", gcfSailfish },
{ "smoothie", gcfSmoothie },
{ "mach3", gcfMach3 },
{ "machinekit", gcfMachinekit },
{ "no-extrusion", gcfNoExtrusion }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeFlavor)
static t_config_enum_values s_keys_map_MachineLimitsUsage {
{ "emit_to_gcode", int(MachineLimitsUsage::EmitToGCode) },
{ "time_estimate_only", int(MachineLimitsUsage::TimeEstimateOnly) },
{ "ignore", int(MachineLimitsUsage::Ignore) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(MachineLimitsUsage)
static t_config_enum_values s_keys_map_PrintHostType {
{ "octoprint", htOctoPrint },
{ "duet", htDuet },
{ "flashair", htFlashAir },
{ "astrobox", htAstroBox },
{ "repetier", htRepetier }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
static t_config_enum_values s_keys_map_AuthorizationType {
{ "key", atKeyPassword },
{ "user", atUserPassword }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(AuthorizationType)
static t_config_enum_values s_keys_map_FuzzySkinType {
{ "none", int(FuzzySkinType::None) },
{ "external", int(FuzzySkinType::External) },
{ "all", int(FuzzySkinType::All) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FuzzySkinType)
static t_config_enum_values s_keys_map_InfillPattern {
{ "rectilinear", ipRectilinear },
{ "monotonic", ipMonotonic },
{ "alignedrectilinear", ipAlignedRectilinear },
{ "grid", ipGrid },
{ "triangles", ipTriangles },
{ "stars", ipStars },
{ "cubic", ipCubic },
{ "line", ipLine },
{ "concentric", ipConcentric },
{ "honeycomb", ipHoneycomb },
{ "3dhoneycomb", ip3DHoneycomb },
{ "gyroid", ipGyroid },
{ "hilbertcurve", ipHilbertCurve },
{ "archimedeanchords", ipArchimedeanChords },
{ "octagramspiral", ipOctagramSpiral },
{ "adaptivecubic", ipAdaptiveCubic },
{ "supportcubic", ipSupportCubic }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
static t_config_enum_values s_keys_map_IroningType {
{ "top", int(IroningType::TopSurfaces) },
{ "topmost", int(IroningType::TopmostOnly) },
{ "solid", int(IroningType::AllSolid) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(IroningType)
static t_config_enum_values s_keys_map_SupportMaterialPattern {
{ "rectilinear", smpRectilinear },
{ "rectilinear-grid", smpRectilinearGrid },
{ "honeycomb", smpHoneycomb }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern)
static t_config_enum_values s_keys_map_SupportMaterialStyle {
{ "grid", smsGrid },
{ "snug", smsSnug }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
static t_config_enum_values s_keys_map_SupportMaterialInterfacePattern {
{ "auto", smipAuto },
{ "rectilinear", smipRectilinear },
{ "concentric", smipConcentric }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialInterfacePattern)
static t_config_enum_values s_keys_map_SeamPosition {
{ "random", spRandom },
{ "nearest", spNearest },
{ "aligned", spAligned },
{ "rear", spRear }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition)
static const t_config_enum_values s_keys_map_SLADisplayOrientation = {
{ "landscape", sladoLandscape},
{ "portrait", sladoPortrait}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLADisplayOrientation)
static const t_config_enum_values s_keys_map_SLAPillarConnectionMode = {
{"zigzag", slapcmZigZag},
{"cross", slapcmCross},
{"dynamic", slapcmDynamic}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAPillarConnectionMode)
static const t_config_enum_values s_keys_map_BrimType = {
{"no_brim", btNoBrim},
{"outer_only", btOuterOnly},
{"inner_only", btInnerOnly},
{"outer_and_inner", btOuterAndInner}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType)
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology) static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
{ {
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options) for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
@ -995,10 +1142,8 @@ void PrintConfigDef::init_fff_params()
def->label = L("First layer height"); def->label = L("First layer height");
def->category = L("Layers and Perimeters"); def->category = L("Layers and Perimeters");
def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker "
"bottom layer to improve adhesion and tolerance for non perfect build plates. " "bottom layer to improve adhesion and tolerance for non perfect build plates.");
"This can be expressed as an absolute value or as a percentage (for example: 150%) " def->sidetext = L("mm");
"over the default layer height.");
def->sidetext = L("mm or %");
def->ratio_over = "layer_height"; def->ratio_over = "layer_height";
def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false)); def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false));
@ -3610,7 +3755,7 @@ std::string DynamicPrintConfig::validate()
FullPrintConfig fpc; FullPrintConfig fpc;
fpc.apply(*this, true); fpc.apply(*this, true);
// Verify this print options through the FullPrintConfig. // Verify this print options through the FullPrintConfig.
return fpc.validate(); return Slic3r::validate(fpc);
} }
default: default:
//FIXME no validation on SLA data? //FIXME no validation on SLA data?
@ -3619,135 +3764,135 @@ std::string DynamicPrintConfig::validate()
} }
//FIXME localize this function. //FIXME localize this function.
std::string FullPrintConfig::validate() std::string validate(const FullPrintConfig &cfg)
{ {
// --layer-height // --layer-height
if (this->get_abs_value("layer_height") <= 0) if (cfg.get_abs_value("layer_height") <= 0)
return "Invalid value for --layer-height"; return "Invalid value for --layer-height";
if (fabs(fmod(this->get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
return "--layer-height must be a multiple of print resolution"; return "--layer-height must be a multiple of print resolution";
// --first-layer-height // --first-layer-height
if (this->get_abs_value("first_layer_height") <= 0) if (cfg.first_layer_height.value <= 0)
return "Invalid value for --first-layer-height"; return "Invalid value for --first-layer-height";
// --filament-diameter // --filament-diameter
for (double fd : this->filament_diameter.values) for (double fd : cfg.filament_diameter.values)
if (fd < 1) if (fd < 1)
return "Invalid value for --filament-diameter"; return "Invalid value for --filament-diameter";
// --nozzle-diameter // --nozzle-diameter
for (double nd : this->nozzle_diameter.values) for (double nd : cfg.nozzle_diameter.values)
if (nd < 0.005) if (nd < 0.005)
return "Invalid value for --nozzle-diameter"; return "Invalid value for --nozzle-diameter";
// --perimeters // --perimeters
if (this->perimeters.value < 0) if (cfg.perimeters.value < 0)
return "Invalid value for --perimeters"; return "Invalid value for --perimeters";
// --solid-layers // --solid-layers
if (this->top_solid_layers < 0) if (cfg.top_solid_layers < 0)
return "Invalid value for --top-solid-layers"; return "Invalid value for --top-solid-layers";
if (this->bottom_solid_layers < 0) if (cfg.bottom_solid_layers < 0)
return "Invalid value for --bottom-solid-layers"; return "Invalid value for --bottom-solid-layers";
if (this->use_firmware_retraction.value && if (cfg.use_firmware_retraction.value &&
this->gcode_flavor.value != gcfSmoothie && cfg.gcode_flavor.value != gcfSmoothie &&
this->gcode_flavor.value != gcfRepRapSprinter && cfg.gcode_flavor.value != gcfRepRapSprinter &&
this->gcode_flavor.value != gcfRepRapFirmware && cfg.gcode_flavor.value != gcfRepRapFirmware &&
this->gcode_flavor.value != gcfMarlinLegacy && cfg.gcode_flavor.value != gcfMarlinLegacy &&
this->gcode_flavor.value != gcfMarlinFirmware && cfg.gcode_flavor.value != gcfMarlinFirmware &&
this->gcode_flavor.value != gcfMachinekit && cfg.gcode_flavor.value != gcfMachinekit &&
this->gcode_flavor.value != gcfRepetier) cfg.gcode_flavor.value != gcfRepetier)
return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware"; return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware";
if (this->use_firmware_retraction.value) if (cfg.use_firmware_retraction.value)
for (unsigned char wipe : this->wipe.values) for (unsigned char wipe : cfg.wipe.values)
if (wipe) if (wipe)
return "--use-firmware-retraction is not compatible with --wipe"; return "--use-firmware-retraction is not compatible with --wipe";
// --gcode-flavor // --gcode-flavor
if (! print_config_def.get("gcode_flavor")->has_enum_value(this->gcode_flavor.serialize())) if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize()))
return "Invalid value for --gcode-flavor"; return "Invalid value for --gcode-flavor";
// --fill-pattern // --fill-pattern
if (! print_config_def.get("fill_pattern")->has_enum_value(this->fill_pattern.serialize())) if (! print_config_def.get("fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
return "Invalid value for --fill-pattern"; return "Invalid value for --fill-pattern";
// --top-fill-pattern // --top-fill-pattern
if (! print_config_def.get("top_fill_pattern")->has_enum_value(this->top_fill_pattern.serialize())) if (! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.top_fill_pattern.serialize()))
return "Invalid value for --top-fill-pattern"; return "Invalid value for --top-fill-pattern";
// --bottom-fill-pattern // --bottom-fill-pattern
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(this->bottom_fill_pattern.serialize())) if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(cfg.bottom_fill_pattern.serialize()))
return "Invalid value for --bottom-fill-pattern"; return "Invalid value for --bottom-fill-pattern";
// --fill-density // --fill-density
if (fabs(this->fill_density.value - 100.) < EPSILON && if (fabs(cfg.fill_density.value - 100.) < EPSILON &&
! print_config_def.get("top_fill_pattern")->has_enum_value(this->fill_pattern.serialize())) ! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
return "The selected fill pattern is not supposed to work at 100% density"; return "The selected fill pattern is not supposed to work at 100% density";
// --infill-every-layers // --infill-every-layers
if (this->infill_every_layers < 1) if (cfg.infill_every_layers < 1)
return "Invalid value for --infill-every-layers"; return "Invalid value for --infill-every-layers";
// --skirt-height // --skirt-height
if (this->skirt_height < 0) if (cfg.skirt_height < 0)
return "Invalid value for --skirt-height"; return "Invalid value for --skirt-height";
// --bridge-flow-ratio // --bridge-flow-ratio
if (this->bridge_flow_ratio <= 0) if (cfg.bridge_flow_ratio <= 0)
return "Invalid value for --bridge-flow-ratio"; return "Invalid value for --bridge-flow-ratio";
// extruder clearance // extruder clearance
if (this->extruder_clearance_radius <= 0) if (cfg.extruder_clearance_radius <= 0)
return "Invalid value for --extruder-clearance-radius"; return "Invalid value for --extruder-clearance-radius";
if (this->extruder_clearance_height <= 0) if (cfg.extruder_clearance_height <= 0)
return "Invalid value for --extruder-clearance-height"; return "Invalid value for --extruder-clearance-height";
// --extrusion-multiplier // --extrusion-multiplier
for (double em : this->extrusion_multiplier.values) for (double em : cfg.extrusion_multiplier.values)
if (em <= 0) if (em <= 0)
return "Invalid value for --extrusion-multiplier"; return "Invalid value for --extrusion-multiplier";
// --default-acceleration // --default-acceleration
if ((this->perimeter_acceleration != 0. || this->infill_acceleration != 0. || this->bridge_acceleration != 0. || this->first_layer_acceleration != 0.) && if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
this->default_acceleration == 0.) cfg.default_acceleration == 0.)
return "Invalid zero value for --default-acceleration when using other acceleration settings"; return "Invalid zero value for --default-acceleration when using other acceleration settings";
// --spiral-vase // --spiral-vase
if (this->spiral_vase) { if (cfg.spiral_vase) {
// Note that we might want to have more than one perimeter on the bottom // Note that we might want to have more than one perimeter on the bottom
// solid layers. // solid layers.
if (this->perimeters > 1) if (cfg.perimeters > 1)
return "Can't make more than one perimeter when spiral vase mode is enabled"; return "Can't make more than one perimeter when spiral vase mode is enabled";
else if (this->perimeters < 1) else if (cfg.perimeters < 1)
return "Can't make less than one perimeter when spiral vase mode is enabled"; return "Can't make less than one perimeter when spiral vase mode is enabled";
if (this->fill_density > 0) if (cfg.fill_density > 0)
return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0"; return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
if (this->top_solid_layers > 0) if (cfg.top_solid_layers > 0)
return "Spiral vase mode is not compatible with top solid layers"; return "Spiral vase mode is not compatible with top solid layers";
if (this->support_material || this->support_material_enforce_layers > 0) if (cfg.support_material || cfg.support_material_enforce_layers > 0)
return "Spiral vase mode is not compatible with support material"; return "Spiral vase mode is not compatible with support material";
} }
// extrusion widths // extrusion widths
{ {
double max_nozzle_diameter = 0.; double max_nozzle_diameter = 0.;
for (double dmr : this->nozzle_diameter.values) for (double dmr : cfg.nozzle_diameter.values)
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" }; const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" };
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
std::string key(widths[i]); std::string key(widths[i]);
key += "_extrusion_width"; key += "_extrusion_width";
if (this->get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter) if (cfg.get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter)
return std::string("Invalid extrusion width (too large): ") + key; return std::string("Invalid extrusion width (too large): ") + key;
} }
} }
// Out of range validation of numeric values. // Out of range validation of numeric values.
for (const std::string &opt_key : this->keys()) { for (const std::string &opt_key : cfg.keys()) {
const ConfigOption *opt = this->optptr(opt_key); const ConfigOption *opt = cfg.optptr(opt_key);
assert(opt != nullptr); assert(opt != nullptr);
const ConfigOptionDef *optdef = print_config_def.get(opt_key); const ConfigOptionDef *optdef = print_config_def.get(opt_key);
assert(optdef != nullptr); assert(optdef != nullptr);
@ -3792,19 +3937,21 @@ std::string FullPrintConfig::validate()
return ""; return "";
} }
// Declare the static caches for each StaticPrintConfig derived class. // Declare and initialize static caches of StaticPrintConfig derived classes.
StaticPrintConfig::StaticCache<class Slic3r::PrintObjectConfig> PrintObjectConfig::s_cache_PrintObjectConfig; #define PRINT_CONFIG_CACHE_ELEMENT_DEFINITION(r, data, CLASS_NAME) StaticPrintConfig::StaticCache<class Slic3r::CLASS_NAME> BOOST_PP_CAT(CLASS_NAME::s_cache_, CLASS_NAME);
StaticPrintConfig::StaticCache<class Slic3r::PrintRegionConfig> PrintRegionConfig::s_cache_PrintRegionConfig; #define PRINT_CONFIG_CACHE_ELEMENT_INITIALIZATION(r, data, CLASS_NAME) Slic3r::CLASS_NAME::initialize_cache();
StaticPrintConfig::StaticCache<class Slic3r::MachineEnvelopeConfig> MachineEnvelopeConfig::s_cache_MachineEnvelopeConfig; #define PRINT_CONFIG_CACHE_INITIALIZE(CLASSES_SEQ) \
StaticPrintConfig::StaticCache<class Slic3r::GCodeConfig> GCodeConfig::s_cache_GCodeConfig; BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_DEFINITION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \
StaticPrintConfig::StaticCache<class Slic3r::PrintConfig> PrintConfig::s_cache_PrintConfig; int print_config_static_initializer() { \
StaticPrintConfig::StaticCache<class Slic3r::FullPrintConfig> FullPrintConfig::s_cache_FullPrintConfig; /* Putting a trace here to avoid the compiler to optimize out this function. */ \
BOOST_LOG_TRIVIAL(trace) << "Initializing StaticPrintConfigs"; \
StaticPrintConfig::StaticCache<class Slic3r::SLAMaterialConfig> SLAMaterialConfig::s_cache_SLAMaterialConfig; BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_INITIALIZATION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \
StaticPrintConfig::StaticCache<class Slic3r::SLAPrintConfig> SLAPrintConfig::s_cache_SLAPrintConfig; return 1; \
StaticPrintConfig::StaticCache<class Slic3r::SLAPrintObjectConfig> SLAPrintObjectConfig::s_cache_SLAPrintObjectConfig; }
StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig> SLAPrinterConfig::s_cache_SLAPrinterConfig; PRINT_CONFIG_CACHE_INITIALIZE((
StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig; PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig,
SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig))
static int print_config_static_initialized = print_config_static_initializer();
CLIActionsConfigDef::CLIActionsConfigDef() CLIActionsConfigDef::CLIActionsConfigDef()
{ {

File diff suppressed because it is too large Load diff

View file

@ -97,6 +97,15 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
return status; return status;
} }
std::vector<std::reference_wrapper<const PrintRegion>> PrintObject::all_regions() const
{
std::vector<std::reference_wrapper<const PrintRegion>> out;
out.reserve(m_all_regions.size());
for (size_t i = 0; i < m_all_regions.size(); ++ i)
out.emplace_back(*m_all_regions[i]);
return out;
}
// Called by make_perimeters() // Called by make_perimeters()
// 1) Decides Z positions of the layers, // 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions // 2) Initializes layers and their regions
@ -173,8 +182,8 @@ void PrintObject::make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating // but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing // inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects // hollow objects
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id]; const PrintRegion &region = this->printing_region(region_id);
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue; continue;
@ -186,7 +195,7 @@ void PrintObject::make_perimeters()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id]; LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id];
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id]; const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id];
const Polygons upper_layerm_polygons = upper_layerm.slices; const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces);
// Filter upper layer polygons in intersection_ppl by their bounding boxes? // Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
const double total_loop_length = total_length(upper_layerm_polygons); const double total_loop_length = total_length(upper_layerm_polygons);
@ -294,7 +303,7 @@ void PrintObject::prepare_infill()
// Debugging output. // Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) { for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final"); layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
@ -313,7 +322,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) { for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final"); layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
@ -332,7 +341,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) { for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final"); layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
@ -351,7 +360,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) { for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final"); layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
@ -716,10 +725,10 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posSlice) { } else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial }); invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false; m_slicing_params.valid = false;
} else if (step == posSupportMaterial) { } else if (step == posSupportMaterial) {
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false; m_slicing_params.valid = false;
} }
// Wipe tower depends on the ordering of extruders, which in turn depends on everything. // Wipe tower depends on the ordering of extruders, which in turn depends on everything.
@ -736,19 +745,11 @@ bool PrintObject::invalidate_all_steps()
// First call the "invalidate" functions, which may cancel background processing. // First call the "invalidate" functions, which may cancel background processing.
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
// Then reset some of the depending values. // Then reset some of the depending values.
this->m_slicing_params.valid = false; m_slicing_params.valid = false;
this->region_volumes.clear(); m_region_volumes.clear();
return result; return result;
} }
static const PrintRegion* first_printing_region(const PrintObject &print_object)
{
for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region)
if (!print_object.region_volumes.empty())
return print_object.print()->regions()[idx_region];
return nullptr;
}
// This function analyzes slices of a region (SurfaceCollection slices). // This function analyzes slices of a region (SurfaceCollection slices).
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type stInternal. // Initially all slices are of type stInternal.
@ -769,13 +770,13 @@ void PrintObject::detect_surfaces_type()
// should be visible. // should be visible.
bool spiral_vase = this->print()->config().spiral_vase.value; bool spiral_vase = this->print()->config().spiral_vase.value;
bool interface_shells = ! spiral_vase && m_config.interface_shells.value; bool interface_shells = ! spiral_vase && m_config.interface_shells.value;
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (Layer *layer : m_layers) for (Layer *layer : m_layers)
layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial"); layer->m_regions[region_id]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads. // If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
@ -791,7 +792,7 @@ void PrintObject::detect_surfaces_type()
((num_layers > 1) ? num_layers - 1 : num_layers) : ((num_layers > 1) ? num_layers - 1 : num_layers) :
// In non-spiral vase mode, go over all layers. // In non-spiral vase mode, go over all layers.
m_layers.size()), m_layers.size()),
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) { [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print. // the support from the print.
SurfaceType surface_type_bottom_other = SurfaceType surface_type_bottom_other =
@ -799,9 +800,9 @@ void PrintObject::detect_surfaces_type()
stBottom : stBottomBridge; stBottom : stBottomBridge;
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z; // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
Layer *layer = m_layers[idx_layer]; Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region]; LayerRegion *layerm = layer->m_regions[region_id];
// comparison happens against the *full* slices (considering all regions) // comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested // unless internal shells are requested
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
@ -809,19 +810,14 @@ void PrintObject::detect_surfaces_type()
// collapse very narrow parts (using the safety offset in the diff is not enough) // collapse very narrow parts (using the safety offset in the diff is not enough)
float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f; float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f;
Polygons layerm_slices_surfaces = to_polygons(layerm->slices.surfaces);
// find top surfaces (difference between current surfaces // find top surfaces (difference between current surfaces
// of current layer and upper one) // of current layer and upper one)
Surfaces top; Surfaces top;
if (upper_layer) { if (upper_layer) {
Polygons upper_slices = interface_shells ? ExPolygons upper_slices = interface_shells ?
to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) : diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) :
to_polygons(upper_layer->lslices); diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes);
surfaces_append(top, surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop);
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
offset_ex(offset_ex(diff_ex(layerm_slices_surfaces, upper_slices, true), -offset), offset),
stTop);
} else { } else {
// if no upper layer, all surfaces of this one are solid // if no upper layer, all surfaces of this one are solid
// we clone surfaces because we're going to clear the slices collection // we clone surfaces because we're going to clear the slices collection
@ -836,17 +832,17 @@ void PrintObject::detect_surfaces_type()
#if 0 #if 0
//FIXME Why is this branch failing t\multi.t ? //FIXME Why is this branch failing t\multi.t ?
Polygons lower_slices = interface_shells ? Polygons lower_slices = interface_shells ?
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces) : to_polygons(lower_layer->get_region(region_id)->slices.surfaces) :
to_polygons(lower_layer->slices); to_polygons(lower_layer->slices);
surfaces_append(bottom, surfaces_append(bottom,
offset2_ex(diff(layerm_slices_surfaces, lower_slices, true), -offset, offset), offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset),
surface_type_bottom_other); surface_type_bottom_other);
#else #else
// Any surface lying on the void is a true bottom bridge (an overhang) // Any surface lying on the void is a true bottom bridge (an overhang)
surfaces_append( surfaces_append(
bottom, bottom,
offset2_ex( offset2_ex(
diff(layerm_slices_surfaces, to_polygons(lower_layer->lslices), true), diff_ex(layerm->slices.surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),
-offset, offset), -offset, offset),
surface_type_bottom_other); surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces // if user requested internal shells, we need to identify surfaces
@ -857,10 +853,10 @@ void PrintObject::detect_surfaces_type()
surfaces_append( surfaces_append(
bottom, bottom,
offset2_ex( offset2_ex(
diff( diff_ex(
intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces), lower_layer->m_regions[region_id]->slices.surfaces,
true), ApplySafetyOffset::Yes),
-offset, offset), -offset, offset),
stBottom); stBottom);
} }
@ -882,9 +878,7 @@ void PrintObject::detect_surfaces_type()
// if $Slic3r::debug; // if $Slic3r::debug;
Polygons top_polygons = to_polygons(std::move(top)); Polygons top_polygons = to_polygons(std::move(top));
top.clear(); top.clear();
surfaces_append(top, surfaces_append(top, diff_ex(top_polygons, bottom), stTop);
diff_ex(top_polygons, to_polygons(bottom), false),
stTop);
} }
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
@ -894,21 +888,24 @@ void PrintObject::detect_surfaces_type()
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green"))); expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown"))); expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black"))); expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, idx_region, layer->print_z).c_str(), expolygons_with_attributes); SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
} }
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// save surfaces to layer // save surfaces to layer
Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces; Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces;
surfaces_out.clear(); Surfaces surfaces_backup;
if (! interface_shells) {
surfaces_backup = std::move(surfaces_out);
surfaces_out.clear();
}
const Surfaces &surfaces_prev = interface_shells ? layerm->slices.surfaces : surfaces_backup;
// find internal surfaces (difference between top/bottom surfaces and others) // find internal surfaces (difference between top/bottom surfaces and others)
{ {
Polygons topbottom = to_polygons(top); Polygons topbottom = to_polygons(top);
polygons_append(topbottom, to_polygons(bottom)); polygons_append(topbottom, to_polygons(bottom));
surfaces_append(surfaces_out, surfaces_append(surfaces_out, diff_ex(surfaces_prev, topbottom), stInternal);
diff_ex(layerm_slices_surfaces, topbottom, false),
stInternal);
} }
surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(top));
@ -928,25 +925,25 @@ void PrintObject::detect_surfaces_type()
if (interface_shells) { if (interface_shells) {
// Move surfaces_new to layerm->slices.surfaces // Move surfaces_new to layerm->slices.surfaces
for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]); m_layers[idx_layer]->m_regions[region_id]->slices.surfaces = std::move(surfaces_new[idx_layer]);
} }
if (spiral_vase) { if (spiral_vase) {
if (num_layers > 1) if (num_layers > 1)
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern. // Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
m_layers[num_layers - 1]->m_regions[idx_region]->slices.set_type(stTop); m_layers[num_layers - 1]->m_regions[region_id]->slices.set_type(stTop);
for (size_t i = num_layers; i < m_layers.size(); ++ i) for (size_t i = num_layers; i < m_layers.size(); ++ i)
m_layers[i]->m_regions[idx_region]->slices.set_type(stInternal); m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
} }
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, idx_region](const tbb::blocked_range<size_t>& range) { [this, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region]; LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
layerm->slices_to_fill_surfaces_clipped(); layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final"); layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
@ -954,7 +951,7 @@ void PrintObject::detect_surfaces_type()
} // for each layer of a region } // for each layer of a region
}); });
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end";
} // for each this->print->region_count } // for each this->print->region_count
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.) // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
@ -970,8 +967,8 @@ void PrintObject::process_external_surfaces()
// Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
// over voids, which are supported by the layer below. // over voids, which are supported by the layer below.
bool has_voids = false; bool has_voids = false;
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id)
if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) { if (this->printing_region(region_id).config().fill_density == 0) {
has_voids = true; has_voids = true;
break; break;
} }
@ -1007,12 +1004,12 @@ void PrintObject::process_external_surfaces()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
Polygons voids; Polygons voids;
for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) { for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
if (layerm->region()->config().fill_density.value == 0.) if (layerm->region().config().fill_density.value == 0.)
for (const Surface &surface : layerm->fill_surfaces.surfaces) for (const Surface &surface : layerm->fill_surfaces.surfaces)
// Shrink the holes, let the layer above expand slightly inside the unsupported areas. // Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append(voids, offset(surface.expolygon, unsupported_width)); polygons_append(voids, offset(surface.expolygon, unsupported_width));
} }
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->lslices), voids); surfaces_covered[layer_idx] = diff(m_layers[layer_idx]->lslices, voids);
} }
} }
); );
@ -1020,7 +1017,7 @@ void PrintObject::process_external_surfaces()
BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
} }
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
@ -1028,7 +1025,7 @@ void PrintObject::process_external_surfaces()
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces( m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(
(layer_idx == 0) ? nullptr : m_layers[layer_idx - 1], (layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
(layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]); (layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
} }
@ -1053,7 +1050,7 @@ void PrintObject::discover_vertical_shells()
Polygons holes; Polygons holes;
}; };
bool spiral_vase = this->print()->config().spiral_vase.value; bool spiral_vase = this->print()->config().spiral_vase.value;
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
coordf_t min_layer_height = this->slicing_parameters().min_layer_height; coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
// Does this region possibly produce more than 1 top or bottom layer? // Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) { auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
@ -1068,14 +1065,14 @@ void PrintObject::discover_vertical_shells()
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0; num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
}; };
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry()); std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) { if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials. // is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region? // Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false; bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
const PrintRegionConfig &config = m_print->get_region(idx_region)->config(); const PrintRegionConfig &config = this->printing_region(region_id).config();
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) { if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
has_extra_layers = true; has_extra_layers = true;
break; break;
@ -1091,7 +1088,7 @@ void PrintObject::discover_vertical_shells()
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
const size_t num_regions = this->region_volumes.size(); const size_t num_regions = this->num_printing_regions();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
const Layer &layer = *m_layers[idx_layer]; const Layer &layer = *m_layers[idx_layer];
@ -1103,21 +1100,21 @@ void PrintObject::discover_vertical_shells()
static size_t debug_idx = 0; static size_t debug_idx = 0;
++ debug_idx; ++ debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) { for (size_t region_id = 0; region_id < num_regions; ++ region_id) {
LayerRegion &layerm = *layer.m_regions[idx_region]; LayerRegion &layerm = *layer.m_regions[region_id];
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
// Top surfaces. // Top surfaces.
append(cache.top_surfaces, offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing)); append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing));
append(cache.top_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing)); append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), min_perimeter_infill_spacing));
// Bottom surfaces. // Bottom surfaces.
append(cache.bottom_surfaces, offset(to_expolygons(layerm.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
// Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only. // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only.
// First find the maxium number of perimeters per region slice. // First find the maxium number of perimeters per region slice.
unsigned int perimeters = 0; unsigned int perimeters = 0;
for (Surface &s : layerm.slices.surfaces) for (Surface &s : layerm.slices.surfaces)
perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters); perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
perimeters += layerm.region()->config().perimeters.value; perimeters += layerm.region().config().perimeters.value;
// Then calculate the infill offset. // Then calculate the infill offset.
if (perimeters > 0) { if (perimeters > 0) {
Flow extflow = layerm.flow(frExternalPerimeter); Flow extflow = layerm.flow(frExternalPerimeter);
@ -1129,8 +1126,8 @@ void PrintObject::discover_vertical_shells()
polygons_append(cache.holes, to_polygons(layerm.fill_expolygons)); polygons_append(cache.holes, to_polygons(layerm.fill_expolygons));
} }
// Save some computing time by reducing the number of polygons. // Save some computing time by reducing the number of polygons.
cache.top_surfaces = union_(cache.top_surfaces, false); cache.top_surfaces = union_(cache.top_surfaces);
cache.bottom_surfaces = union_(cache.bottom_surfaces, false); cache.bottom_surfaces = union_(cache.bottom_surfaces);
// For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print. // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print.
if (perimeter_offset > 0.) { if (perimeter_offset > 0.) {
// The layer.lslices are forced to merge by expanding them first. // The layer.lslices are forced to merge by expanding them first.
@ -1145,17 +1142,17 @@ void PrintObject::discover_vertical_shells()
} }
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} }
cache.holes = union_(cache.holes, false); cache.holes = union_(cache.holes);
} }
}); });
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
} }
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
PROFILE_BLOCK(discover_vertical_shells_region); PROFILE_BLOCK(discover_vertical_shells_region);
const PrintRegion &region = *m_print->get_region(idx_region); const PrintRegion &region = this->printing_region(region_id);
if (! region.config().ensure_vertical_shell_thickness.value) if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells(). // This region will be handled by discover_horizontal_shells().
continue; continue;
@ -1169,38 +1166,38 @@ void PrintObject::discover_vertical_shells()
if (! top_bottom_surfaces_all_regions) { if (! top_bottom_surfaces_all_regions) {
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
// is calculated over a single material. // is calculated over a single material.
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : cache top / bottom";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
Layer &layer = *m_layers[idx_layer]; Layer &layer = *m_layers[idx_layer];
LayerRegion &layerm = *layer.m_regions[idx_region]; LayerRegion &layerm = *layer.m_regions[region_id];
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f; float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
// Top surfaces. // Top surfaces.
auto &cache = cache_top_botom_regions[idx_layer]; auto &cache = cache_top_botom_regions[idx_layer];
cache.top_surfaces = offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing); cache.top_surfaces = offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing);
append(cache.top_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing)); append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), min_perimeter_infill_spacing));
// Bottom surfaces. // Bottom surfaces.
cache.bottom_surfaces = offset(to_expolygons(layerm.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing); cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing);
append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing)); append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
// Holes over all regions. Only collect them once, they are valid for all idx_region iterations. // Holes over all regions. Only collect them once, they are valid for all region_id iterations.
if (cache.holes.empty()) { if (cache.holes.empty()) {
for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region) for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id)
polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons)); polygons_append(cache.holes, to_polygons(layer.regions()[region_id]->fill_expolygons));
} }
} }
}); });
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end : cache top / bottom";
} }
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, idx_region, &cache_top_botom_regions] [this, region_id, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1212,8 +1209,8 @@ void PrintObject::discover_vertical_shells()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Layer *layer = m_layers[idx_layer]; Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[idx_region]; LayerRegion *layerm = layer->m_regions[region_id];
const PrintRegionConfig &region_config = layerm->region()->config(); const PrintRegionConfig &region_config = layerm->region().config();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
@ -1269,7 +1266,7 @@ void PrintObject::discover_vertical_shells()
polygons_append(shell, cache.top_surfaces); polygons_append(shell, cache.top_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper // Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once. // than running the union_ all at once.
shell = union_(shell, false); shell = union_(shell);
} }
} }
} }
@ -1288,7 +1285,7 @@ void PrintObject::discover_vertical_shells()
polygons_append(shell, cache.bottom_surfaces); polygons_append(shell, cache.bottom_surfaces);
// Running the union_ using the Clipper library piece by piece is cheaper // Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once. // than running the union_ all at once.
shell = union_(shell, false); shell = union_(shell);
} }
} }
} }
@ -1308,7 +1305,7 @@ void PrintObject::discover_vertical_shells()
} }
#endif #endif
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
shell_ex = union_ex(shell, true); shell_ex = union_safety_offset_ex(shell);
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} }
@ -1354,7 +1351,7 @@ void PrintObject::discover_vertical_shells()
// Trim the shells region by the internal & internal void surfaces. // Trim the shells region by the internal & internal void surfaces.
const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid }; const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid };
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3)); const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3));
shell = intersection(shell, polygonsInternal, true); shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes);
polygons_append(shell, diff(polygonsInternal, holes)); polygons_append(shell, diff(polygonsInternal, holes));
if (shell.empty()) if (shell.empty())
continue; continue;
@ -1392,14 +1389,14 @@ void PrintObject::discover_vertical_shells()
polygons_append(shell, intersection(offset(too_narrow, margin), polygonsInternal)); polygons_append(shell, intersection(offset(too_narrow, margin), polygonsInternal));
} }
#endif #endif
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false); ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before)); Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
// Source shell. // Source shell.
svg.draw(union_ex(shell_before, true)); svg.draw(union_safety_offset_ex(shell_before));
// Shell trimmed to the internal surfaces. // Shell trimmed to the internal surfaces.
svg.draw_outline(union_ex(shell, true), "black", "blue", scale_(0.05)); svg.draw_outline(union_safety_offset_ex(shell), "black", "blue", scale_(0.05));
// Regularized infill region. // Regularized infill region.
svg.draw_outline(new_internal_solid, "red", "magenta", scale_(0.05)); svg.draw_outline(new_internal_solid, "red", "magenta", scale_(0.05));
svg.Close(); svg.Close();
@ -1407,16 +1404,8 @@ void PrintObject::discover_vertical_shells()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Trim the internal & internalvoid by the shell. // Trim the internal & internalvoid by the shell.
Slic3r::ExPolygons new_internal = diff_ex( Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), shell);
to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)), Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), shell);
shell,
false
);
Slic3r::ExPolygons new_internal_void = diff_ex(
to_polygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)),
shell,
false
);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
@ -1435,11 +1424,11 @@ void PrintObject::discover_vertical_shells()
} // for each layer } // for each layer
}); });
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) { for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) {
LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region); LayerRegion *layerm = m_layers[idx_layer]->get_region(region_id);
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
} }
@ -1457,8 +1446,8 @@ void PrintObject::bridge_over_infill()
{ {
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id]; const PrintRegion &region = this->printing_region(region_id);
// skip bridging in case there are no voids // skip bridging in case there are no voids
if (region.config().fill_density.value == 100) if (region.config().fill_density.value == 100)
@ -1521,8 +1510,8 @@ void PrintObject::bridge_over_infill()
#endif #endif
// compute the remaning internal solid surfaces as difference // compute the remaning internal solid surfaces as difference
ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes);
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes);
// build the new collection of fill_surfaces // build the new collection of fill_surfaces
layerm->fill_surfaces.remove_type(stInternalSolid); layerm->fill_surfaces.remove_type(stInternalSolid);
for (ExPolygon &ex : to_bridge) for (ExPolygon &ex : to_bridge)
@ -1680,6 +1669,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
object_extruders); object_extruders);
} }
sort_remove_duplicates(object_extruders); sort_remove_duplicates(object_extruders);
//FIXME add painting extruders
if (object_max_z <= 0.f) if (object_max_z <= 0.f)
object_max_z = (float)model_object.raw_bounding_box().size().z(); object_max_z = (float)model_object.raw_bounding_box().size().z();
@ -1690,10 +1680,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
std::vector<unsigned int> PrintObject::object_extruders() const std::vector<unsigned int> PrintObject::object_extruders() const
{ {
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
extruders.reserve(this->region_volumes.size() * 3); extruders.reserve(this->all_regions().size() * 3);
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) for (const PrintRegion &region : this->all_regions())
if (! this->region_volumes[idx_region].empty()) region.collect_object_printing_extruders(*this->print(), extruders);
m_print->get_region(idx_region)->collect_object_printing_extruders(extruders);
sort_remove_duplicates(extruders); sort_remove_duplicates(extruders);
return extruders; return extruders;
} }
@ -1771,8 +1760,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
layer->lower_layer = prev; layer->lower_layer = prev;
} }
// Make sure all layers contain layer region objects for all regions. // Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id)
layer->add_region(this->print()->get_region(region_id)); layer->add_region(&this->printing_region(region_id));
prev = layer; prev = layer;
} }
} }
@ -1782,16 +1771,15 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
bool has_z_ranges = false; bool has_z_ranges = false;
size_t num_volumes = 0; size_t num_volumes = 0;
size_t num_modifiers = 0; size_t num_modifiers = 0;
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
int last_volume_id = -1; int last_volume_id = -1;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) { for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const int volume_id = volume_and_range.second; const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) { if (model_volume->is_model_part()) {
if (last_volume_id == volume_id) { if (last_volume_id == volume_w_zrange.volume_idx) {
has_z_ranges = true; has_z_ranges = true;
} else { } else {
last_volume_id = volume_id; last_volume_id = volume_w_zrange.volume_idx;
if (all_volumes_single_region == -2) if (all_volumes_single_region == -2)
// first model volume met // first model volume met
all_volumes_single_region = region_id; all_volumes_single_region = region_id;
@ -1814,14 +1802,14 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping. // Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel // slicing in parallel
size_t slicing_mode_normal_below_layer = 0; size_t slicing_mode_normal_below_layer = 0;
if (spiral_vase) { if (spiral_vase) {
// Slice the bottom layers with SlicingMode::Regular. // Slice the bottom layers with SlicingMode::Regular.
// This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase!
const PrintRegionConfig &config = this->print()->regions()[region_id]->config(); const PrintRegionConfig &config = this->printing_region(region_id).config();
slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value);
for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON;
++ slicing_mode_normal_below_layer); ++ slicing_mode_normal_below_layer);
@ -1847,22 +1835,22 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}; };
std::vector<SlicedVolume> sliced_volumes; std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes); sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id]; const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) { for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges[i].second; int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) { if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges; std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first); ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1; size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON) if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second; ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else else
ranges.emplace_back(volumes_and_ranges[j].first); ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel // slicing in parallel
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume)); sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
i = j; i = j;
@ -1891,7 +1879,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
slices = offset_ex(std::move(slices), delta); slices = offset_ex(std::move(slices), delta);
if (! processed.empty()) if (! processed.empty())
// Trim by the slices of already processed regions. // Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed); slices = diff_ex(slices, processed);
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
// Collect the already processed regions to trim the to be processed regions. // Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices); polygons_append(processed, slices);
@ -1899,7 +1887,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
} }
} }
// Collect and union volumes of a single region. // Collect and union volumes of a single region.
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) { for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
ExPolygons expolygons; ExPolygons expolygons;
size_t num_volumes = 0; size_t num_volumes = 0;
for (SlicedVolume &sliced_volume : sliced_volumes) for (SlicedVolume &sliced_volume : sliced_volumes)
@ -1920,8 +1908,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
} }
// Slice all modifier volumes. // Slice all modifier volumes.
if (this->region_volumes.size() > 1) { if (m_region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel // slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs); std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
@ -1934,7 +1922,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) { [this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) { for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id) if (region_id == other_region_id)
continue; continue;
Layer *layer = m_layers[layer_id]; Layer *layer = m_layers[layer_id];
@ -1942,12 +1930,11 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
LayerRegion *other_layerm = layer->m_regions[other_region_id]; LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty()) if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
continue; continue;
Polygons other_slices = to_polygons(other_layerm->slices); ExPolygons my_parts = intersection_ex(other_layerm->slices.surfaces, expolygons_by_layer[layer_id]);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty()) if (my_parts.empty())
continue; continue;
// Remove such parts from original region. // Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal); other_layerm->slices.set(diff_ex(other_layerm->slices.surfaces, my_parts), stInternal);
// Append new parts to our region. // Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal); layerm->slices.append(std::move(my_parts), stInternal);
} }
@ -2034,7 +2021,7 @@ end:
slices = offset_ex(std::move(slices), xy_compensation_scaled); slices = offset_ex(std::move(slices), xy_compensation_scaled);
if (region_id > 0 && clip) if (region_id > 0 && clip)
// Trim by the slices of already processed regions. // Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed); slices = diff_ex(slices, processed);
if (clip && (region_id + 1 < layer->m_regions.size())) if (clip && (region_id + 1 < layer->m_regions.size()))
// Collect the already processed regions to trim the to be processed regions. // Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices); polygons_append(processed, slices);
@ -2075,9 +2062,9 @@ end:
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
{ {
std::vector<const ModelVolume*> volumes; std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) { if (region_id < m_region_volumes.size()) {
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) { for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_model_part()) if (volume->is_model_part())
volumes.emplace_back(volume); volumes.emplace_back(volume);
} }
@ -2085,27 +2072,27 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v
return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes); return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes);
} }
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once. // Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once.
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
{ {
std::vector<ExPolygons> out; std::vector<ExPolygons> out;
if (region_id < this->region_volumes.size()) if (region_id < m_region_volumes.size())
{ {
std::vector<std::vector<t_layer_height_range>> volume_ranges; std::vector<std::vector<t_layer_height_range>> volume_ranges;
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id]; const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
volume_ranges.reserve(volumes_and_ranges.size()); volume_ranges.reserve(volumes_and_ranges.volumes.size());
for (size_t i = 0; i < volumes_and_ranges.size(); ) { for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges[i].second; int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) { if (model_volume->is_modifier()) {
std::vector<t_layer_height_range> ranges; std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first); ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1; size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) { for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) {
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON) if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second; ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else else
ranges.emplace_back(volumes_and_ranges[j].first); ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
} }
volume_ranges.emplace_back(std::move(ranges)); volume_ranges.emplace_back(std::move(ranges));
i = j; i = j;
@ -2127,8 +2114,8 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) { if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
// No modifier in this region was split to layer spans. // No modifier in this region was split to layer spans.
std::vector<const ModelVolume*> volumes; std::vector<const ModelVolume*> volumes;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) { for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second]; const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_modifier()) if (volume->is_modifier())
volumes.emplace_back(volume); volumes.emplace_back(volume);
} }
@ -2136,19 +2123,19 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
} else { } else {
// Some modifier in this region was split to layer spans. // Some modifier in this region was split to layer spans.
std::vector<char> merge; std::vector<char> merge;
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id]; const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) { for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges[i].second; int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) { if (model_volume->is_modifier()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id; BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges; std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first); ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1; size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
ranges.emplace_back(volumes_and_ranges[j].first); ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel // slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume); std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume. // Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
@ -2223,6 +2210,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
TriangleMesh vol_mesh(model_volume.mesh()); TriangleMesh vol_mesh(model_volume.mesh());
vol_mesh.transform(model_volume.get_matrix(), true); vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
mesh.repair(false);
} }
if (mesh.stl.stats.number_of_facets > 0) { if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true); mesh.transform(m_trafo, true);
@ -2365,7 +2353,7 @@ std::string PrintObject::_fix_slicing_errors()
if (lower_surfaces) if (lower_surfaces)
for (const auto &surface : *lower_surfaces) for (const auto &surface : *lower_surfaces)
polygons_append(holes, surface.expolygon.holes); polygons_append(holes, surface.expolygon.holes);
layerm->slices.set(diff_ex(union_(outer), holes, false), stInternal); layerm->slices.set(diff_ex(union_(outer), holes), stInternal);
} }
// Update layer slices after repairing the single regions. // Update layer slices after repairing the single regions.
layer->make_slices(); layer->make_slices();
@ -2423,9 +2411,15 @@ void PrintObject::simplify_slices(double distance)
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void PrintObject::clip_fill_surfaces() void PrintObject::clip_fill_surfaces()
{ {
if (! m_config.infill_only_where_needed.value || if (! m_config.infill_only_where_needed.value)
! std::any_of(this->print()->regions().begin(), this->print()->regions().end(), return;
[](const PrintRegion *region) { return region->config().fill_density > 0; })) bool has_infill = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i)
if (this->printing_region(i).config().fill_density > 0) {
has_infill = true;
break;
}
if (! has_infill)
return; return;
// We only want infill under ceilings; this is almost like an // We only want infill under ceilings; this is almost like an
@ -2478,7 +2472,7 @@ void PrintObject::clip_fill_surfaces()
upper_internal = intersection(overhangs, lower_layer_internal_surfaces); upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
// Apply new internal infill to regions. // Apply new internal infill to regions.
for (LayerRegion *layerm : lower_layer->m_regions) { for (LayerRegion *layerm : lower_layer->m_regions) {
if (layerm->region()->config().fill_density.value == 0) if (layerm->region().config().fill_density.value == 0)
continue; continue;
SurfaceType internal_surface_types[] = { stInternal, stInternalVoid }; SurfaceType internal_surface_types[] = { stInternal, stInternalVoid };
Polygons internal; Polygons internal;
@ -2486,8 +2480,8 @@ void PrintObject::clip_fill_surfaces()
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
polygons_append(internal, std::move(surface.expolygon)); polygons_append(internal, std::move(surface.expolygon));
layerm->fill_surfaces.remove_types(internal_surface_types, 2); layerm->fill_surfaces.remove_types(internal_surface_types, 2);
layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal); layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal);
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid); layerm->fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid);
// If there are voids it means that our internal infill is not adjacent to // If there are voids it means that our internal infill is not adjacent to
// perimeters. In this case it would be nice to add a loop around infill to // perimeters. In this case it would be nice to add a loop around infill to
// make it more robust and nicer. TODO. // make it more robust and nicer. TODO.
@ -2503,12 +2497,12 @@ void PrintObject::discover_horizontal_shells()
{ {
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (size_t i = 0; i < m_layers.size(); ++ i) { for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
Layer *layer = m_layers[i]; Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id]; LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config(); const PrintRegionConfig &region_config = layerm->region().config();
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 && if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) { (i % region_config.solid_infill_every_layers) == 0) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge. // Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
@ -2584,7 +2578,7 @@ void PrintObject::discover_horizontal_shells()
for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces) for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalSolid) if (surface.surface_type == stInternal || surface.surface_type == stInternalSolid)
polygons_append(internal, to_polygons(surface.expolygon)); polygons_append(internal, to_polygons(surface.expolygon));
new_internal_solid = intersection(solid, internal, true); new_internal_solid = intersection(solid, internal, ApplySafetyOffset::Yes);
} }
if (new_internal_solid.empty()) { if (new_internal_solid.empty()) {
// No internal solid needed on this layer. In order to decide whether to continue // No internal solid needed on this layer. In order to decide whether to continue
@ -2611,8 +2605,7 @@ void PrintObject::discover_horizontal_shells()
float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()); float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width());
Polygons too_narrow = diff( Polygons too_narrow = diff(
new_internal_solid, new_internal_solid,
offset2(new_internal_solid, -margin, +margin, jtMiter, 5), offset2(new_internal_solid, -margin, +margin + ClipperSafetyOffset, jtMiter, 5));
true);
// Trim the regularized region by the original region. // Trim the regularized region by the original region.
if (! too_narrow.empty()) if (! too_narrow.empty())
new_internal_solid = solid = diff(new_internal_solid, too_narrow); new_internal_solid = solid = diff(new_internal_solid, too_narrow);
@ -2631,8 +2624,7 @@ void PrintObject::discover_horizontal_shells()
// have the same angle, so the next shell would be grown even more and so on. // have the same angle, so the next shell would be grown even more and so on.
Polygons too_narrow = diff( Polygons too_narrow = diff(
new_internal_solid, new_internal_solid,
offset2(new_internal_solid, -margin, +margin, ClipperLib::jtMiter, 5), offset2(new_internal_solid, -margin, +margin + ClipperSafetyOffset, ClipperLib::jtMiter, 5));
true);
if (! too_narrow.empty()) { if (! too_narrow.empty()) {
// grow the collapsing parts and add the extra area to the neighbor layer // grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this // as well as to our original surfaces so that we support this
@ -2660,15 +2652,12 @@ void PrintObject::discover_horizontal_shells()
// and new ones // and new ones
SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces); SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces);
polygons_append(new_internal_solid, to_polygons(backup.filter_by_type(stInternalSolid))); polygons_append(new_internal_solid, to_polygons(backup.filter_by_type(stInternalSolid)));
ExPolygons internal_solid = union_ex(new_internal_solid, false); ExPolygons internal_solid = union_ex(new_internal_solid);
// assign new internal-solid surfaces to layer // assign new internal-solid surfaces to layer
neighbor_layerm->fill_surfaces.set(internal_solid, stInternalSolid); neighbor_layerm->fill_surfaces.set(internal_solid, stInternalSolid);
// subtract intersections from layer surfaces to get resulting internal surfaces // subtract intersections from layer surfaces to get resulting internal surfaces
Polygons polygons_internal = to_polygons(std::move(internal_solid)); Polygons polygons_internal = to_polygons(std::move(internal_solid));
ExPolygons internal = diff_ex( ExPolygons internal = diff_ex(backup.filter_by_type(stInternal), polygons_internal, ApplySafetyOffset::Yes);
to_polygons(backup.filter_by_type(stInternal)),
polygons_internal,
true);
// assign resulting internal surfaces to layer // assign resulting internal surfaces to layer
neighbor_layerm->fill_surfaces.append(internal, stInternal); neighbor_layerm->fill_surfaces.append(internal, stInternal);
polygons_append(polygons_internal, to_polygons(std::move(internal))); polygons_append(polygons_internal, to_polygons(std::move(internal)));
@ -2679,7 +2668,7 @@ void PrintObject::discover_horizontal_shells()
backup.group(&top_bottom_groups); backup.group(&top_bottom_groups);
for (SurfacesPtr &group : top_bottom_groups) for (SurfacesPtr &group : top_bottom_groups)
neighbor_layerm->fill_surfaces.append( neighbor_layerm->fill_surfaces.append(
diff_ex(to_polygons(group), polygons_internal), diff_ex(group, polygons_internal),
// Use an existing surface as a template, it carries the bridge angle etc. // Use an existing surface as a template, it carries the bridge angle etc.
*group.front()); *group.front());
} }
@ -2689,7 +2678,7 @@ void PrintObject::discover_horizontal_shells()
} // for each region } // for each region
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) { for (const Layer *layer : m_layers) {
const LayerRegion *layerm = layer->m_regions[region_id]; const LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells"); layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
@ -2705,16 +2694,16 @@ void PrintObject::discover_horizontal_shells()
void PrintObject::combine_infill() void PrintObject::combine_infill()
{ {
// Work on each region separately. // Work on each region separately.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id]; const PrintRegion &region = this->printing_region(region_id);
const size_t every = region->config().infill_every_layers.value; const size_t every = region.config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.) if (every < 2 || region.config().fill_density == 0.)
continue; continue;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle. // Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height //FIXME limit the layer height to max_layer_height
double nozzle_diameter = std::min( double nozzle_diameter = std::min(
this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1), this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1),
this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1)); this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1));
// define the combinations // define the combinations
std::vector<size_t> combine(m_layers.size(), 0); std::vector<size_t> combine(m_layers.size(), 0);
{ {
@ -2758,10 +2747,7 @@ void PrintObject::combine_infill()
ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal)); ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal));
// Start looping from the second layer and intersect the current intersection with it. // Start looping from the second layer and intersect the current intersection with it.
for (size_t i = 1; i < layerms.size(); ++ i) for (size_t i = 1; i < layerms.size(); ++ i)
intersection = intersection_ex( intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection);
to_polygons(intersection),
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)),
false);
double area_threshold = layerms.front()->infill_area_threshold(); double area_threshold = layerms.front()->infill_area_threshold();
if (! intersection.empty() && area_threshold > 0.) if (! intersection.empty() && area_threshold > 0.)
intersection.erase(std::remove_if(intersection.begin(), intersection.end(), intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
@ -2781,18 +2767,18 @@ void PrintObject::combine_infill()
0.5f * layerms.back()->flow(frPerimeter).scaled_width() + 0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
// Because fill areas for rectilinear and honeycomb are grown // Because fill areas for rectilinear and honeycomb are grown
// later to overlap perimeters, we need to counteract that too. // later to overlap perimeters, we need to counteract that too.
((region->config().fill_pattern == ipRectilinear || ((region.config().fill_pattern == ipRectilinear ||
region->config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipMonotonic ||
region->config().fill_pattern == ipGrid || region.config().fill_pattern == ipGrid ||
region->config().fill_pattern == ipLine || region.config().fill_pattern == ipLine ||
region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
layerms.back()->flow(frSolidInfill).scaled_width(); layerms.back()->flow(frSolidInfill).scaled_width();
for (ExPolygon &expoly : intersection) for (ExPolygon &expoly : intersection)
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
for (LayerRegion *layerm : layerms) { for (LayerRegion *layerm : layerms) {
Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)); Polygons internal = to_polygons(std::move(layerm->fill_surfaces.filter_by_type(stInternal)));
layerm->fill_surfaces.remove_type(stInternal); layerm->fill_surfaces.remove_type(stInternal);
layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance, false), stInternal); layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance), stInternal);
if (layerm == layerms.back()) { if (layerm == layerms.back()) {
// Apply surfaces back with adjusted depth to the uppermost layer. // Apply surfaces back with adjusted depth to the uppermost layer.
Surface templ(stInternal, ExPolygon()); Surface templ(stInternal, ExPolygon());
@ -2804,7 +2790,7 @@ void PrintObject::combine_infill()
} else { } else {
// Save void surfaces. // Save void surfaces.
layerm->fill_surfaces.append( layerm->fill_surfaces.append(
intersection_ex(internal, intersection_with_clearance, false), intersection_ex(internal, intersection_with_clearance),
stInternalVoid); stInternalVoid);
} }
} }

View file

@ -20,11 +20,12 @@ unsigned int PrintRegion::extruder(FlowRole role) const
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
{ {
ConfigOptionFloatOrPercent config_width; const PrintConfig &print_config = object.print()->config();
ConfigOptionFloatOrPercent config_width;
// Get extrusion width from configuration. // Get extrusion width from configuration.
// (might be an absolute value, or a percent value, or zero for auto) // (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { if (first_layer && print_config.first_layer_extrusion_width.value > 0) {
config_width = m_print->config().first_layer_extrusion_width; config_width = print_config.first_layer_extrusion_width;
} else if (role == frExternalPerimeter) { } else if (role == frExternalPerimeter) {
config_width = m_config.external_perimeter_extrusion_width; config_width = m_config.external_perimeter_extrusion_width;
} else if (role == frPerimeter) { } else if (role == frPerimeter) {
@ -44,7 +45,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
// Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
} }
@ -76,17 +77,17 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con
emplace_extruder(region_config.solid_infill_extruder); emplace_extruder(region_config.solid_infill_extruder);
} }
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const void PrintRegion::collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const
{ {
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder. // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
// If not, then there must be something wrong with the Print::apply() function. // If not, then there must be something wrong with the Print::apply() function.
#ifndef NDEBUG #ifndef NDEBUG
auto num_extruders = (int)print()->config().nozzle_diameter.size(); auto num_extruders = int(print.config().nozzle_diameter.size());
assert(this->config().perimeter_extruder <= num_extruders); assert(this->config().perimeter_extruder <= num_extruders);
assert(this->config().infill_extruder <= num_extruders); assert(this->config().infill_extruder <= num_extruders);
assert(this->config().solid_infill_extruder <= num_extruders); assert(this->config().solid_infill_extruder <= num_extruders);
#endif #endif
collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders); collect_object_printing_extruders(print.config(), this->config(), print.has_brim(), object_extruders);
} }
} }

View file

@ -4,7 +4,6 @@
#include <libslic3r/SLA/RasterBase.hpp> #include <libslic3r/SLA/RasterBase.hpp>
#include "libslic3r/ExPolygon.hpp" #include "libslic3r/ExPolygon.hpp"
#include "libslic3r/MTUtils.hpp" #include "libslic3r/MTUtils.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
// For rasterizing // For rasterizing
#include <agg/agg_basics.h> #include <agg/agg_basics.h>
@ -21,10 +20,7 @@
namespace Slic3r { namespace Slic3r {
inline const Polygon& contour(const ExPolygon& p) { return p.contour; } inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
inline const Polygons& holes(const ExPolygon& p) { return p.holes; } inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
namespace sla { namespace sla {
@ -77,8 +73,6 @@ protected:
double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; } double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; }
double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; } double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; }
agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); } agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); }
double getPx(const ClipperLib::IntPoint &p) { return p.X * m_pxdim_scaled.w_mm; }
double getPy(const ClipperLib::IntPoint& p) { return p.Y * m_pxdim_scaled.h_mm; }
template<class PointVec> agg::path_storage _to_path(const PointVec& v) template<class PointVec> agg::path_storage _to_path(const PointVec& v)
{ {
@ -168,7 +162,6 @@ public:
} }
void draw(const ExPolygon &poly) override { _draw(poly); } void draw(const ExPolygon &poly) override { _draw(poly); }
void draw(const ClipperLib::Polygon &poly) override { _draw(poly); }
EncodedRaster encode(RasterEncoder encoder) const override EncodedRaster encode(RasterEncoder encoder) const override
{ {

View file

@ -42,9 +42,10 @@ Point ConcaveHull::centroid(const Points &pp)
// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound // As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
// mode // mode
static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths, template<typename PolygonsProvider>
coord_t delta, static ClipperLib::Paths fast_offset(PolygonsProvider &&paths,
ClipperLib::JoinType jointype) coord_t delta,
ClipperLib::JoinType jointype)
{ {
using ClipperLib::ClipperOffset; using ClipperLib::ClipperOffset;
using ClipperLib::etClosedPolygon; using ClipperLib::etClosedPolygon;
@ -61,7 +62,7 @@ static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
return {}; return {};
} }
offs.AddPaths(paths, jointype, etClosedPolygon); offs.AddPaths(std::forward<PolygonsProvider>(paths), jointype, etClosedPolygon);
Paths result; Paths result;
offs.Execute(result, static_cast<double>(delta)); offs.Execute(result, static_cast<double>(delta));
@ -157,11 +158,9 @@ ExPolygons ConcaveHull::to_expolygons() const
ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta) ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
{ {
ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons()); ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(
paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound); fast_offset(fast_offset(ClipperUtils::PolygonsProvider(hull.polygons()), 2 * delta, ClipperLib::jtRound), -delta, ClipperLib::jtRound));
paths = fast_offset(paths, -delta, ClipperLib::jtRound); for (ExPolygon &p : ret) p.holes.clear();
ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths);
for (ExPolygon &p : ret) p.holes = {};
return ret; return ret;
} }

View file

@ -179,10 +179,10 @@ PadSkeleton divide_blueprint(const ExPolygons &bp)
ret.outer.reserve(size_t(ptree.Total())); ret.outer.reserve(size_t(ptree.Total()));
for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) { for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) {
ExPolygon poly(ClipperPath_to_Slic3rPolygon(node->Contour)); ExPolygon poly;
poly.contour.points = std::move(node->Contour);
for (ClipperLib::PolyTree::PolyNode *child : node->Childs) { for (ClipperLib::PolyTree::PolyNode *child : node->Childs) {
poly.holes.emplace_back( poly.holes.emplace_back(std::move(child->Contour));
ClipperPath_to_Slic3rPolygon(child->Contour));
traverse_pt(child->Childs, &ret.inner); traverse_pt(child->Childs, &ret.inner);
} }
@ -342,18 +342,18 @@ public:
template<class...Args> template<class...Args>
ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args) ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args)
{ {
ExPolygons tmp = offset_ex(poly.contour, float(delta), args...); Polygons tmp = offset(poly.contour, float(delta), args...);
if (tmp.empty()) return {}; if (tmp.empty()) return {};
Polygons holes = poly.holes; Polygons holes = poly.holes;
for (auto &h : holes) h.reverse(); for (auto &h : holes) h.reverse();
tmp = diff_ex(to_polygons(tmp), holes); ExPolygons tmp2 = diff_ex(tmp, holes);
if (tmp.empty()) return {}; if (tmp2.empty()) return {};
return tmp.front(); return std::move(tmp2.front());
} }
bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg,

View file

@ -11,8 +11,6 @@
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>
#include <libslic3r/SLA/Concurrency.hpp> #include <libslic3r/SLA/Concurrency.hpp>
namespace ClipperLib { struct Polygon; }
namespace Slic3r { namespace Slic3r {
template<class T> using uqptr = std::unique_ptr<T>; template<class T> using uqptr = std::unique_ptr<T>;
@ -92,7 +90,6 @@ public:
/// Draw a polygon with holes. /// Draw a polygon with holes.
virtual void draw(const ExPolygon& poly) = 0; virtual void draw(const ExPolygon& poly) = 0;
virtual void draw(const ClipperLib::Polygon& poly) = 0;
/// Get the resolution of the raster. /// Get the resolution of the raster.
virtual Resolution resolution() const = 0; virtual Resolution resolution() const = 0;

View file

@ -12,11 +12,9 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
#include "ExPolygonCollection.hpp" #include "ExPolygonCollection.hpp"
#include "MinAreaBoundingBox.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "libnest2d/backends/clipper/geometries.hpp"
#include "libnest2d/utils/rotcalipers.hpp"
#include <iostream> #include <iostream>
#include <random> #include <random>
@ -181,9 +179,8 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
} }
} }
if (! top.islands_below.empty()) { if (! top.islands_below.empty()) {
Polygons top_polygons = to_polygons(*top.polygon);
Polygons bottom_polygons = top.polygons_below(); Polygons bottom_polygons = top.polygons_below();
top.overhangs = diff_ex(top_polygons, bottom_polygons); top.overhangs = diff_ex(*top.polygon, bottom_polygons);
if (! top.overhangs.empty()) { if (! top.overhangs.empty()) {
// Produce 2 bands around the island, a safe band for dangling overhangs // Produce 2 bands around the island, a safe band for dangling overhangs
@ -193,7 +190,7 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
auto overh_mask = offset(bottom_polygons, slope_offset, ClipperLib::jtSquare); auto overh_mask = offset(bottom_polygons, slope_offset, ClipperLib::jtSquare);
// Absolutely hopeless overhangs are those outside the unsafe band // Absolutely hopeless overhangs are those outside the unsafe band
top.overhangs = diff_ex(top_polygons, overh_mask); top.overhangs = diff_ex(*top.polygon, overh_mask);
// Now cut out the supported core from the safe band // Now cut out the supported core from the safe band
// and cut the safe band from the unsafe band to get distinct // and cut the safe band from the unsafe band to get distinct
@ -201,8 +198,8 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
overh_mask = diff(overh_mask, dangl_mask); overh_mask = diff(overh_mask, dangl_mask);
dangl_mask = diff(dangl_mask, bottom_polygons); dangl_mask = diff(dangl_mask, bottom_polygons);
top.dangling_areas = intersection_ex(top_polygons, dangl_mask); top.dangling_areas = intersection_ex(*top.polygon, dangl_mask);
top.overhangs_slopes = intersection_ex(top_polygons, overh_mask); top.overhangs_slopes = intersection_ex(*top.polygon, overh_mask);
top.overhangs_area = 0.f; top.overhangs_area = 0.f;
std::vector<std::pair<ExPolygon*, float>> expolys_with_areas; std::vector<std::pair<ExPolygon*, float>> expolys_with_areas;
@ -400,7 +397,7 @@ std::vector<Vec2f> sample_expolygon(const ExPolygons &expolys, float samples_per
void sample_expolygon_boundary(const ExPolygon & expoly, void sample_expolygon_boundary(const ExPolygon & expoly,
float samples_per_mm, float samples_per_mm,
std::vector<Vec2f> &out, std::vector<Vec2f> &out,
std::mt19937 & rng) std::mt19937 & /*rng*/)
{ {
double point_stepping_scaled = scale_(1.f) / samples_per_mm; double point_stepping_scaled = scale_(1.f) / samples_per_mm;
for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) { for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) {
@ -553,9 +550,8 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure
// auto bb = get_extents(islands); // auto bb = get_extents(islands);
if (flags & icfIsNew) { if (flags & icfIsNew) {
auto chull_ex = ExPolygonCollection{islands}.convex_hull(); auto chull = ExPolygonCollection{islands}.convex_hull();
auto chull = Slic3rMultiPoint_to_ClipperPath(chull_ex); auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex};
auto rotbox = libnest2d::minAreaBoundingBox(chull);
Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())}; Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())};
if (bbdim.x() > bbdim.y()) std::swap(bbdim.x(), bbdim.y()); if (bbdim.x() > bbdim.y()) std::swap(bbdim.x(), bbdim.y());

View file

@ -90,7 +90,7 @@ public:
float overlap_area(const Structure &rhs) const { float overlap_area(const Structure &rhs) const {
double out = 0.; double out = 0.;
if (this->bbox.overlap(rhs.bbox)) { if (this->bbox.overlap(rhs.bbox)) {
Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false); Polygons polys = intersection(*this->polygon, *rhs.polygon);
for (const Polygon &poly : polys) for (const Polygon &poly : polys)
out += poly.area(); out += poly.area();
} }

View file

@ -9,7 +9,6 @@
#include "Point.hpp" #include "Point.hpp"
#include "MTUtils.hpp" #include "MTUtils.hpp"
#include "Zipper.hpp" #include "Zipper.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
namespace Slic3r { namespace Slic3r {
@ -268,7 +267,7 @@ protected:
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); } void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
{ this->m_config.apply_only(other, keys, ignore_nonexistent); } { m_config.apply_only(other, keys, ignore_nonexistent); }
void set_trafo(const Transform3d& trafo, bool left_handed) { void set_trafo(const Transform3d& trafo, bool left_handed) {
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; }); m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
@ -483,7 +482,7 @@ public:
// The collection of slice records for the current level. // The collection of slice records for the current level.
std::vector<std::reference_wrapper<const SliceRecord>> m_slices; std::vector<std::reference_wrapper<const SliceRecord>> m_slices;
std::vector<ClipperLib::Polygon> m_transformed_slices; ExPolygons m_transformed_slices;
template<class Container> void transformed_slices(Container&& c) template<class Container> void transformed_slices(Container&& c)
{ {
@ -507,7 +506,7 @@ public:
auto slices() const -> const decltype (m_slices)& { return m_slices; } auto slices() const -> const decltype (m_slices)& { return m_slices; }
const std::vector<ClipperLib::Polygon> & transformed_slices() const { const ExPolygons & transformed_slices() const {
return m_transformed_slices; return m_transformed_slices;
} }
}; };

View file

@ -16,9 +16,6 @@
#include <libslic3r/ClipperUtils.hpp> #include <libslic3r/ClipperUtils.hpp>
// For geometry algorithms with native Clipper types (no copies and conversions)
#include <libnest2d/backends/clipper/geometries.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include "I18N.hpp" #include "I18N.hpp"
@ -717,55 +714,49 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) {
report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
} }
using ClipperPoint = ClipperLib::IntPoint; //static ClipperPolygons polyunion(const ClipperPolygons &subjects)
using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d //{
using ClipperPolygons = std::vector<ClipperPolygon>; // ClipperLib::Clipper clipper;
static ClipperPolygons polyunion(const ClipperPolygons &subjects) // bool closed = true;
{
ClipperLib::Clipper clipper;
bool closed = true; // for(auto& path : subjects) {
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
// }
for(auto& path : subjects) { // auto mode = ClipperLib::pftPositive;
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
}
auto mode = ClipperLib::pftPositive; // return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
//}
return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); //static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
} //{
// ClipperLib::Clipper clipper;
static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) // bool closed = true;
{
ClipperLib::Clipper clipper;
bool closed = true; // for(auto& path : subjects) {
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
// }
for(auto& path : subjects) { // for(auto& path : clips) {
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); // clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); // clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
} // }
for(auto& path : clips) { // auto mode = ClipperLib::pftPositive;
clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
}
auto mode = ClipperLib::pftPositive; // return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
//}
return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
}
// get polygons for all instances in the object // get polygons for all instances in the object
static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o) static ExPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
{ {
namespace sl = libnest2d::sl;
if (!record.print_obj()) return {}; if (!record.print_obj()) return {};
ClipperPolygons polygons; ExPolygons polygons;
auto &input_polygons = record.get_slice(o); auto &input_polygons = record.get_slice(o);
auto &instances = record.print_obj()->instances(); auto &instances = record.print_obj()->instances();
bool is_lefthanded = record.print_obj()->is_left_handed(); bool is_lefthanded = record.print_obj()->is_left_handed();
@ -776,43 +767,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
for (size_t i = 0; i < instances.size(); ++i) for (size_t i = 0; i < instances.size(); ++i)
{ {
ClipperPolygon poly; ExPolygon poly;
// We need to reverse if is_lefthanded is true but // We need to reverse if is_lefthanded is true but
bool needreverse = is_lefthanded; bool needreverse = is_lefthanded;
// should be a move // should be a move
poly.Contour.reserve(polygon.contour.size() + 1); poly.contour.points.reserve(polygon.contour.size() + 1);
auto& cntr = polygon.contour.points; auto& cntr = polygon.contour.points;
if(needreverse) if(needreverse)
for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
poly.Contour.emplace_back(it->x(), it->y()); poly.contour.points.emplace_back(it->x(), it->y());
else else
for(auto& p : cntr) for(auto& p : cntr)
poly.Contour.emplace_back(p.x(), p.y()); poly.contour.points.emplace_back(p.x(), p.y());
for(auto& h : polygon.holes) { for(auto& h : polygon.holes) {
poly.Holes.emplace_back(); poly.holes.emplace_back();
auto& hole = poly.Holes.back(); auto& hole = poly.holes.back();
hole.reserve(h.points.size() + 1); hole.points.reserve(h.points.size() + 1);
if(needreverse) if(needreverse)
for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
hole.emplace_back(it->x(), it->y()); hole.points.emplace_back(it->x(), it->y());
else else
for(auto& p : h.points) for(auto& p : h.points)
hole.emplace_back(p.x(), p.y()); hole.points.emplace_back(p.x(), p.y());
} }
if(is_lefthanded) { if(is_lefthanded) {
for(auto& p : poly.Contour) p.X = -p.X; for(auto& p : poly.contour) p.x() = -p.x();
for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; for(auto& h : poly.holes) for(auto& p : h) p.x() = -p.x();
} }
sl::rotate(poly, double(instances[i].rotation)); poly.rotate(double(instances[i].rotation));
sl::translate(poly, ClipperPoint{instances[i].shift.x(), poly.translate(Point{instances[i].shift.x(), instances[i].shift.y()});
instances[i].shift.y()});
polygons.emplace_back(std::move(poly)); polygons.emplace_back(std::move(poly));
} }
@ -878,9 +868,6 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
print_statistics.clear(); print_statistics.clear();
// libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); };
const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0;
const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0;
@ -913,7 +900,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
// Going to parallel: // Going to parallel:
auto printlayerfn = [this, auto printlayerfn = [this,
// functions and read only vars // functions and read only vars
areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
// write vars // write vars
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
@ -931,8 +918,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
// Calculation of the consumed material // Calculation of the consumed material
ClipperPolygons model_polygons; ExPolygons model_polygons;
ClipperPolygons supports_polygons; ExPolygons supports_polygons;
size_t c = std::accumulate(layer.slices().begin(), size_t c = std::accumulate(layer.slices().begin(),
layer.slices().end(), layer.slices().end(),
@ -954,44 +941,44 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
for(const SliceRecord& record : layer.slices()) { for(const SliceRecord& record : layer.slices()) {
ClipperPolygons modelslices = get_all_polygons(record, soModel); ExPolygons modelslices = get_all_polygons(record, soModel);
for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp)); for(ExPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
ClipperPolygons supportslices = get_all_polygons(record, soSupport); ExPolygons supportslices = get_all_polygons(record, soSupport);
for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp)); for(ExPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
} }
model_polygons = polyunion(model_polygons); model_polygons = union_ex(model_polygons);
double layer_model_area = 0; double layer_model_area = 0;
for (const ClipperPolygon& polygon : model_polygons) for (const ExPolygon& polygon : model_polygons)
layer_model_area += areafn(polygon); layer_model_area += area(polygon);
if (layer_model_area < 0 || layer_model_area > 0) { if (layer_model_area < 0 || layer_model_area > 0) {
Lock lck(mutex); models_volume += layer_model_area * l_height; Lock lck(mutex); models_volume += layer_model_area * l_height;
} }
if(!supports_polygons.empty()) { if(!supports_polygons.empty()) {
if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); if(model_polygons.empty()) supports_polygons = union_ex(supports_polygons);
else supports_polygons = polydiff(supports_polygons, model_polygons); else supports_polygons = diff_ex(supports_polygons, model_polygons);
// allegedly, union of subject is done withing the diff according to the pftPositive polyFillType // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
} }
double layer_support_area = 0; double layer_support_area = 0;
for (const ClipperPolygon& polygon : supports_polygons) for (const ExPolygon& polygon : supports_polygons)
layer_support_area += areafn(polygon); layer_support_area += area(polygon);
if (layer_support_area < 0 || layer_support_area > 0) { if (layer_support_area < 0 || layer_support_area > 0) {
Lock lck(mutex); supports_volume += layer_support_area * l_height; Lock lck(mutex); supports_volume += layer_support_area * l_height;
} }
// Here we can save the expensively calculated polygons for printing // Here we can save the expensively calculated polygons for printing
ClipperPolygons trslices; ExPolygons trslices;
trslices.reserve(model_polygons.size() + supports_polygons.size()); trslices.reserve(model_polygons.size() + supports_polygons.size());
for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); for(ExPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); for(ExPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
layer.transformed_slices(polyunion(trslices)); layer.transformed_slices(union_ex(trslices));
// Calculation of the slow and fast layers to the future controlling those values on FW // Calculation of the slow and fast layers to the future controlling those values on FW
@ -1074,7 +1061,7 @@ void SLAPrint::Steps::rasterize()
PrintLayer& printlayer = m_print->m_printer_input[idx]; PrintLayer& printlayer = m_print->m_printer_input[idx];
if(canceled()) return; if(canceled()) return;
for (const ClipperLib::Polygon& poly : printlayer.transformed_slices()) for (const ExPolygon& poly : printlayer.transformed_slices())
raster.draw(poly); raster.draw(poly);
// Status indication guarded with the spinlock // Status indication guarded with the spinlock

View file

@ -273,8 +273,8 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo
std::ostringstream d; std::ostringstream d;
d << "M "; d << "M ";
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) { for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
d << to_svg_x(scale * p->X - origin(0)) << " "; d << to_svg_x(scale * p->x() - origin(0)) << " ";
d << to_svg_y(scale * p->Y - origin(1)) << " "; d << to_svg_y(scale * p->y() - origin(1)) << " ";
} }
if (closed) d << "z"; if (closed) d << "z";
return d.str(); return d.str();

Some files were not shown because too many files have changed in this diff Show more