Merge branch 'master' into fs_svg

# Conflicts:
#	src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
#	src/slic3r/GUI/SurfaceDrag.cpp
This commit is contained in:
Filip Sykala - NTB T15p 2023-03-22 09:39:54 +01:00
commit 6540681e92
24 changed files with 700 additions and 522 deletions

View File

@ -1,3 +1,5 @@
min_slic3r_version = 2.6.0-alpha4
1.1.1 Initial official version
1.0.1 Initial Version
min_slic3r_version = 2.6.0-alpha1
1.0.1 Disabled thick bridges.
1.0.0 Initial Version

View File

@ -1,14 +1,14 @@
# Print profiles for the AnkerMake printers.
# https://github.com/prusa3d/PrusaSlicer/pull/9075 by @just-trey
[vendor]
# Vendor name will be shown by the Config Wizard.
name = Anker
name = AnkerMake
# 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.
config_version = 1.0.1
config_version = 1.1.1
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
# The printer models will be shown by the Configuration Wizard in this order,
# also the first model installed & the first nozzle installed will be activated after install.
@ -20,8 +20,9 @@ variants = 0.4
technology = FFF
family = AnkerMake
bed_model = M5-bed.stl
bed_texture = M5-texture.svg
default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER
bed_texture = M5-texture_v2.svg
thumbnail = M5_thumbnail_v2.png
default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER;
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@ -31,8 +32,8 @@ default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER;
avoid_crossing_perimeters = 0
bridge_acceleration = 2500
bridge_angle = 0
bridge_flow_ratio = 0.95
bridge_speed = 150
bridge_flow_ratio = 1
bridge_speed = 50
brim_separation = 0.1
brim_type = outer_only
brim_width = 0
@ -40,32 +41,36 @@ clip_multipart_objects = 1
complete_objects = 0
default_acceleration = 2500
dont_support_bridges = 1
elefant_foot_compensation = 0.1
elefant_foot_compensation = 0.2
ensure_vertical_shell_thickness = 1
external_perimeter_speed = 150
external_perimeters_first = 0
extra_perimeters = 0
extruder_clearance_height = 30
extruder_clearance_radius = 45
extrusion_width = 0.4
external_perimeter_extrusion_width = 0.44
fill_angle = 45
fill_density = 15%
fill_pattern = cubic
fill_density = 10%
fill_pattern = grid
first_layer_acceleration = 2500
first_layer_acceleration_over_raft = 0
first_layer_extrusion_width = 200%
first_layer_extrusion_width = 0.4
first_layer_speed = 50%
first_layer_speed_over_raft = 30
gap_fill_enabled = 1
gap_fill_speed = 150
gcode_comments = 0
infill_acceleration = 2500
infill_anchor = 600%
infill_anchor_max = 50
infill_anchor = 2.5
infill_anchor_max = 12
infill_every_layers = 1
infill_extruder = 1
infill_first = 0
infill_extrusion_width = 0.4
infill_only_where_needed = 0
infill_overlap = 23%
infill_overlap = 10%
infill_speed = 250
interface_shells = 0
max_print_speed = 250
@ -76,18 +81,18 @@ min_skirt_length = 4
notes =
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{digits(layer_height,1,2)}mm_{filament_type[0]}_{printer_model}.gcode
output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}.gcode
overhangs = 1
perimeter_acceleration = 2500
perimeter_extruder = 1
perimeter_extrusion_width = 0
perimeter_generator = arachne
perimeter_extrusion_width = 0.4
perimeter_generator = classic
perimeter_speed = 250
perimeters = 3
post_process =
print_settings_id =
raft_layers = 0
resolution = 0
resolution = 0.01
seam_position = aligned
single_extruder_multi_material_priming = 0
skirt_distance = 3
@ -97,32 +102,37 @@ small_perimeter_speed = 150
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_speed = 175
solid_infill_extrusion_width = 0.4
solid_infill_speed = 250
spiral_vase = 0
standby_temperature_delta = -5
support_material_auto = 0
support_material = 0
support_material_angle = 0
support_material_buildplate_only = 0
support_material_contact_distance = 0.15
support_material_contact_distance = 0.1
support_material_enforce_layers = 0
support_material_extruder = 0
support_material_interface_contact_loops = 0
support_material_interface_extruder = 0
support_material_interface_layers = 2
support_material_interface_spacing = 0.2
support_material_interface_speed = 100%
support_material_interface_speed = 80%
support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 125
support_material_speed = 150
support_material_synchronize_layers = 0
support_material_threshold = 40
support_material_threshold = 55
support_material_with_sheath = 0
support_material_xy_spacing = 60%
support_material_xy_spacing = 50%
thick_bridges = 0
thin_walls = 0
top_solid_infill_speed = 150
travel_speed = 300
travel_speed_z = 10
top_infill_extrusion_width = 0.4
top_fill_pattern = rectilinear
bottom_fill_pattern = rectilinear
travel_speed = 250
travel_speed_z = 0
wipe_tower = 0
wipe_tower_bridging = 10
wipe_tower_rotation_angle = 0
@ -131,86 +141,49 @@ wipe_tower_x = 170
wipe_tower_y = 140
xy_size_compensation = 0
[print:*0.08mm*]
inherits = *common*
layer_height = 0.08
first_layer_height = 0.12
bottom_solid_layers = 9
top_solid_layers = 11
bridge_flow_ratio = 0.70
[print:*0.10mm*]
inherits = *common*
layer_height = 0.10
first_layer_height = 0.14
first_layer_height = 0.10
bottom_solid_layers = 7
top_solid_layers = 9
bridge_flow_ratio = 0.70
[print:*0.12mm*]
inherits = *common*
layer_height = 0.12
first_layer_height = 0.16
bottom_solid_layers = 6
top_solid_layers = 7
bridge_flow_ratio = 0.70
[print:*0.16mm*]
inherits = *common*
layer_height = 0.16
first_layer_height = 0.20
bottom_solid_layers = 5
top_solid_layers = 7
bridge_flow_ratio = 0.85
bridge_flow_ratio = 1
[print:*0.20mm*]
inherits = *common*
layer_height = 0.20
first_layer_height = 0.24
first_layer_height = 0.14
bottom_solid_layers = 4
top_solid_layers = 5
[print:*0.24mm*]
[print:*0.30mm*]
inherits = *common*
layer_height = 0.24
first_layer_height = 0.28
layer_height = 0.30
first_layer_height = 0.21
bottom_solid_layers = 3
top_solid_layers = 4
[print:*0.28mm*]
inherits = *common*
layer_height = 0.28
first_layer_height = 0.28
bottom_solid_layers = 3
top_solid_layers = 4
[print:0.08 mm SUPERDETAIL (0.4 mm nozzle) @ANKER]
inherits = *0.08mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER]
inherits = *0.10mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.12 mm DETAIL (0.4 mm nozzle) @ANKER]
inherits = *0.12mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER]
inherits = *0.16mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER]
inherits = *0.20mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.24 mm DRAFT (0.4 mm nozzle) @ANKER]
inherits = *0.24mm*
[print:0.30 mm SUPERDRAFT (0.4 mm nozzle) @ANKER]
inherits = *0.30mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
[print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @ANKER]
inherits = *0.28mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
# When submitting new filaments please print the following temperature tower at 0.1mm layer height:
# https://www.thingiverse.com/thing:2615842
# Pay particular attention to bridging, overhangs and retractions.
# Also print the following bed adhesion test at 0.1 layer height as well:
# https://www.prusaprinters.org/prints/4634-bed-adhesion-warp-test
# At least for PLA, please keep bed temp at 60, as many Creality printers do not have any ABL
# So having some leeway to get good bed adhesion is not a luxury for many users
[filament:*common*]
cooling = 0
@ -235,47 +208,47 @@ filament_type = PLA
filament_density = 1.24
filament_cost = 20
first_layer_bed_temperature = 60
first_layer_temperature = 210
first_layer_temperature = 230
fan_always_on = 1
max_fan_speed = 100
min_fan_speed = 100
bridge_fan_speed = 100
disable_fan_first_layers = 2
temperature = 205
disable_fan_first_layers = 1
temperature = 200
[filament:*PLA+*]
inherits = *common*
bed_temperature = 60
fan_below_layer_time = 100
filament_colour = #DDDDDD
filament_type = PLA
filament_type = PLA+
filament_density = 1.24
filament_cost = 20
first_layer_bed_temperature = 60
first_layer_temperature = 220
first_layer_temperature = 230
fan_always_on = 1
max_fan_speed = 100
min_fan_speed = 100
bridge_fan_speed = 100
disable_fan_first_layers = 2
temperature = 210
disable_fan_first_layers = 1
temperature = 200
[filament:*PET*]
inherits = *common*
bed_temperature = 70
bed_temperature = 80
disable_fan_first_layers = 2
fan_below_layer_time = 20
filament_colour = #DDDDDD
filament_type = PETG
filament_density = 1.27
filament_cost = 30
first_layer_bed_temperature = 70
first_layer_temperature = 240
first_layer_bed_temperature = 80
first_layer_temperature = 260
fan_always_on = 1
max_fan_speed = 50
min_fan_speed = 50
bridge_fan_speed = 100
temperature = 240
temperature = 260
[filament:*ABS*]
inherits = *common*
@ -286,14 +259,14 @@ filament_colour = #DDDDDD
filament_type = ABS
filament_density = 1.04
filament_cost = 20
first_layer_bed_temperature = 100
first_layer_temperature = 245
first_layer_bed_temperature = 90
first_layer_temperature = 260
fan_always_on = 0
max_fan_speed = 0
min_fan_speed = 0
bridge_fan_speed = 30
top_fan_speed = 0
temperature = 245
temperature = 260
[filament:Generic PLA @ANKER]
inherits = *PLA*
@ -324,8 +297,8 @@ deretract_speed = 60
extruder_colour = #FCE94F
extruder_offset = 0x0
gcode_flavor = marlin
silent_mode = 0
remaining_times = 0
silent_mode = 1
remaining_times = 1
machine_max_acceleration_e = 2500
machine_max_acceleration_extruding = 2500
machine_max_acceleration_retracting = 2500
@ -338,8 +311,8 @@ machine_max_feedrate_x = 300
machine_max_feedrate_y = 300
machine_max_feedrate_z = 20
machine_max_jerk_e = 3
machine_max_jerk_x = 30
machine_max_jerk_y = 30
machine_max_jerk_x = 15
machine_max_jerk_y = 15
machine_max_jerk_z = 0.3
machine_min_extruding_rate = 0
machine_min_travel_rate = 0
@ -347,10 +320,10 @@ layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z}
max_print_height = 250
printer_notes =
printer_settings_id =
retract_before_travel = 2
retract_before_wipe = 70%
retract_before_travel = 3
retract_before_wipe = 0
retract_layer_change = 1
retract_length_toolchange = 1
retract_length_toolchange = 4
retract_lift = 0
retract_lift_above = 0
retract_lift_below = 0
@ -365,10 +338,10 @@ use_firmware_retraction = 0
use_relative_e_distances = 0
use_volumetric_e = 0
variable_layer_height = 1
wipe = 1
wipe = 0
z_offset = 0
default_filament_profile = "Generic PLA+ @ANKER"
start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for nozzle temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime)
default_filament_profile = Generic PLA+ @ANKER
start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nM420 S1; restore saved Auto Bed Leveling data\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime)
end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84
[printer:*M5*]
@ -376,12 +349,12 @@ inherits = *common*
bed_shape = 0x0,235-0,235x235,0x235
max_print_height = 250
printer_model = M5
retract_length = 1.25
retract_length = 3
retract_speed = 60
deretract_speed = 60
retract_before_travel = 1
retract_before_travel = 3
retract_before_wipe = 0%
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_ANKERMAKE\nPRINTER_MODEL_M5
printer_notes = Don 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_ANKERMAKE\nPRINTER_MODEL_M5
[printer:AnkerMake M5 (0.4 mm nozzle)]
inherits = *M5*
@ -389,5 +362,5 @@ nozzle_diameter = 0.4
printer_variant = 0.4
min_layer_height = 0.08
max_layer_height = 0.32
retract_lift_above = 0.2
default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER"
retract_lift_above = 0
default_print_profile = 0.2 mm OPTIMAL (0.4 mm nozzle) @ANKER

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.6.0-alpha5
1.9.0-alpha0 Updated output filename format.
1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL).
1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%.
min_slic3r_version = 2.6.0-alpha1

View File

@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.7.0-alpha2
config_version = 1.9.0-alpha0
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -192,7 +192,7 @@ notes =
overhangs = 1
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
perimeters = 2
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
@ -390,7 +390,7 @@ support_material_xy_spacing = 80%
support_material_interface_spacing = 0.2
support_material_spacing = 2.2
raft_first_layer_expansion = 2
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
infill_anchor = 2
infill_anchor_max = 15
thick_bridges = 0

View File

@ -165,9 +165,9 @@ inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(si
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
inline AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
using TreeType = AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;

View File

@ -957,7 +957,7 @@ namespace Slic3r {
try
{
res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
CallbackData* data = (CallbackData*)pOpaque;
if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.parse_error()) {
char error_buf[1024];
@ -991,7 +991,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading cut information data to buffer");
return;
@ -1053,7 +1053,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading config data to buffer");
return;
@ -1073,7 +1073,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading layer heights profile data to buffer");
return;
@ -1135,7 +1135,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading layer config ranges data to buffer");
return;
@ -1192,7 +1192,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading sla support points data to buffer");
return;
@ -1274,7 +1274,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer(size_t(stat.m_uncomp_size), 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading sla support points data to buffer");
return;
@ -1379,7 +1379,7 @@ namespace Slic3r {
return false;
}
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, parser_buffer, (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading config data to buffer");
return false;
@ -1399,7 +1399,7 @@ namespace Slic3r {
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading custom Gcodes per height data to buffer");
return;

View File

@ -965,7 +965,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
try
{
res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
CallbackData* data = (CallbackData*)pOpaque;
if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->ctx.error())
{

View File

@ -18,7 +18,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
{
std::string buf(size_t(entry.m_uncomp_size), '\0');
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index,
buf.data(), buf.size(), 0))
throw Slic3r::FileIOError(zip.get_errorstr());
@ -35,7 +35,7 @@ EntryBuffer read_entry(const mz_zip_archive_file_stat &entry,
{
std::vector<uint8_t> buf(entry.m_uncomp_size);
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index,
buf.data(), buf.size(), 0))
throw Slic3r::FileIOError(zip.get_errorstr());

View File

@ -1,11 +1,14 @@
#include "Layer.hpp"
#include "ClipperZUtils.hpp"
#include "ClipperUtils.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "Print.hpp"
#include "Fill/Fill.hpp"
#include "ShortestPath.hpp"
#include "SVG.hpp"
#include "BoundingBox.hpp"
#include "clipper/clipper.hpp"
#include <boost/log/trivial.hpp>
@ -180,6 +183,31 @@ static void connect_layer_slices(
break;
}
}
if (!found) {
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
// example of failing link on two layers, each with single polygon without holes.
// layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
Polygon contour_poly;
for (const auto& p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_above.lslices_ex[l];
// it is potentially slow, but should be executed rarely
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_above.lslices[l]).empty()) {
found = true;
j = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
}
} else {
// Index of an island above. Look-it up in the island below.
assert(j < m_offset_end);
@ -194,6 +222,23 @@ static void connect_layer_slices(
break;
}
}
if (!found) { // Explanation for aditional check is above.
Polygon contour_poly;
for (const auto &p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_below.lslices_ex[l];
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_below.lslices[l]).empty()) {
found = true;
i = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
}
}
} else {
assert(assert_intersection_valid(i, j));

View File

@ -171,8 +171,13 @@ namespace client
struct OptWithPos {
OptWithPos() {}
OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range) : opt(opt), it_range(it_range) {}
ConfigOptionConstPtr opt = nullptr;
ConfigOptionConstPtr opt { nullptr };
bool writable { false };
// -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered.
int index { -1 };
boost::iterator_range<Iterator> it_range;
bool has_index() const { return index != -1; }
};
template<typename ITERATOR>
@ -582,7 +587,7 @@ namespace client
param1.set_s(buf);
}
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
static void regex_op(const expr &lhs, boost::iterator_range<Iterator> &rhs, char op, expr &out)
{
const std::string *subject = nullptr;
if (lhs.type() == TYPE_STRING) {
@ -596,7 +601,7 @@ namespace client
bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern));
if (op == '!')
result = ! result;
lhs.set_b(result);
out.set_b(result);
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) {
// Syntax error in the regular expression
boost::throw_exception(qi::expectation_failure<Iterator>(
@ -604,8 +609,37 @@ namespace client
}
}
static void regex_matches (expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '='); }
static void regex_doesnt_match(expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '!'); }
static void regex_matches (expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '=', lhs); }
static void regex_doesnt_match(expr &lhs, boost::iterator_range<Iterator> &rhs) { return regex_op(lhs, rhs, '!', lhs); }
static void one_of_test_init(expr &out) {
out.set_b(false);
}
template<bool RegEx>
static void one_of_test(const expr &match, const expr &pattern, expr &out) {
if (! out.b()) {
if (match.type() != TYPE_STRING)
match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value");
if (pattern.type() != TYPE_STRING)
match.throw_exception("one_of(): Pattern has to be a string value");
if (RegEx) {
try {
out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s())));
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &) {
// Syntax error in the regular expression
pattern.throw_exception("Regular expression compilation failed");
}
} else
out.set_b(match.s() == pattern.s());
}
}
static void one_of_test_regex(const expr &match, boost::iterator_range<Iterator> &pattern, expr &out) {
if (! out.b()) {
if (match.type() != TYPE_STRING)
match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value");
regex_op(match, pattern, '=', out);
}
}
static void logical_op(expr &lhs, expr &rhs, char op)
{
@ -688,6 +722,7 @@ namespace client
const DynamicConfig *external_config = nullptr;
const DynamicConfig *config = nullptr;
const DynamicConfig *config_override = nullptr;
mutable DynamicConfig *config_outputs = nullptr;
size_t current_extruder_id = 0;
PlaceholderParser::ContextData *context_data = nullptr;
// If false, the macro_processor will evaluate a full macro.
@ -713,6 +748,7 @@ namespace client
}
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; }
template <typename Iterator>
static void legacy_variable_expansion(
@ -788,130 +824,213 @@ namespace client
OptWithPos<Iterator> &output)
{
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
if (opt == nullptr)
ctx->throw_exception("Not a variable name", opt_key);
if (opt == nullptr) {
opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end()));
if (opt == nullptr)
ctx->throw_exception("Not a variable name", opt_key);
output.writable = true;
}
output.opt = opt;
output.it_range = opt_key;
}
template <typename Iterator>
static void scalar_variable_reference(
static void store_variable_index(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
expr<Iterator> &output)
OptWithPos<Iterator> &opt,
int index,
Iterator it_end,
OptWithPos<Iterator> &output)
{
if (opt.opt->is_vector())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
switch (opt.opt->type()) {
case coFloat: output.set_d(opt.opt->getFloat()); break;
case coInt: output.set_i(opt.opt->getInt()); break;
case coString: output.set_s(static_cast<const ConfigOptionString*>(opt.opt)->value); break;
case coPercent: output.set_d(opt.opt->getFloat()); break;
case coEnum:
case coPoint: output.set_s(opt.opt->serialize()); break;
case coBool: output.set_b(opt.opt->getBool()); break;
case coFloatOrPercent:
{
std::string opt_key(opt.it_range.begin(), opt.it_range.end());
if (boost::ends_with(opt_key, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast<unsigned int>(ctx->current_extruder_id)));
} else if (! static_cast<const ConfigOptionFloatOrPercent*>(opt.opt)->percent) {
// Not a percent, just return the value.
output.set_d(opt.opt->getFloat());
} else {
// Resolve dependencies using the "ratio_over" link to a parent value.
const ConfigOptionDef *opt_def = print_config_def.get(opt_key);
assert(opt_def != nullptr);
double v = opt.opt->getFloat() * 0.01; // percent to ratio
for (;;) {
const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over);
if (opt_parent == nullptr)
ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range);
if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
assert(opt_parent->type() == coFloatOrPercent);
v *= Flow::extrusion_width(opt_def->ratio_over, static_cast<const ConfigOptionFloatOrPercent*>(opt_parent), *ctx, static_cast<unsigned int>(ctx->current_extruder_id));
break;
}
if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) {
v *= opt_parent->getFloat();
if (opt_parent->type() == coFloat || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_parent)->percent)
break;
v *= 0.01; // percent to ratio
}
// Continue one level up in the "ratio_over" hierarchy.
opt_def = print_config_def.get(opt_def->ratio_over);
assert(opt_def != nullptr);
}
output.set_d(v);
}
break;
}
default:
ctx->throw_exception("Unknown scalar variable type", opt.it_range);
}
output.it_range = opt.it_range;
if (! opt.opt->is_vector())
ctx->throw_exception("Cannot index a scalar variable", opt.it_range);
if (index < 0)
ctx->throw_exception("Referencing a vector variable with a negative index", opt.it_range);
output = opt;
output.index = index;
output.it_range.end() = it_end;
}
template <typename Iterator>
static void vector_variable_reference(
static void variable_value(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
int &index,
Iterator it_end,
expr<Iterator> &output)
{
if (opt.opt->is_scalar())
ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index);
switch (opt.opt->type()) {
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
case coInts: output.set_i(static_cast<const ConfigOptionInts *>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings *>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
case coPoints: output.set_s(to_string(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx])); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
//case coEnums: output.set_s(opt.opt->vserialize()[idx]); break;
default:
ctx->throw_exception("Unknown vector variable type", opt.it_range);
if (opt.opt->is_vector()) {
if (! opt.has_index())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index);
switch (opt.opt->type()) {
case coFloats: output.set_d(static_cast<const ConfigOptionFloats *>(opt.opt)->values[idx]); break;
case coInts: output.set_i(static_cast<const ConfigOptionInts *>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings *>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
case coPoints: output.set_s(to_string(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx])); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
//case coEnums: output.set_s(opt.opt->vserialize()[idx]); break;
default:
ctx->throw_exception("Unknown vector variable type", opt.it_range);
}
} else {
assert(opt.opt->is_scalar());
switch (opt.opt->type()) {
case coFloat: output.set_d(opt.opt->getFloat()); break;
case coInt: output.set_i(opt.opt->getInt()); break;
case coString: output.set_s(static_cast<const ConfigOptionString*>(opt.opt)->value); break;
case coPercent: output.set_d(opt.opt->getFloat()); break;
case coEnum:
case coPoint: output.set_s(opt.opt->serialize()); break;
case coBool: output.set_b(opt.opt->getBool()); break;
case coFloatOrPercent:
{
std::string opt_key(opt.it_range.begin(), opt.it_range.end());
if (boost::ends_with(opt_key, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast<unsigned int>(ctx->current_extruder_id)));
} else if (! static_cast<const ConfigOptionFloatOrPercent*>(opt.opt)->percent) {
// Not a percent, just return the value.
output.set_d(opt.opt->getFloat());
} else {
// Resolve dependencies using the "ratio_over" link to a parent value.
const ConfigOptionDef *opt_def = print_config_def.get(opt_key);
assert(opt_def != nullptr);
double v = opt.opt->getFloat() * 0.01; // percent to ratio
for (;;) {
const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over);
if (opt_parent == nullptr)
ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range);
if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
assert(opt_parent->type() == coFloatOrPercent);
v *= Flow::extrusion_width(opt_def->ratio_over, static_cast<const ConfigOptionFloatOrPercent*>(opt_parent), *ctx, static_cast<unsigned int>(ctx->current_extruder_id));
break;
}
if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) {
v *= opt_parent->getFloat();
if (opt_parent->type() == coFloat || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_parent)->percent)
break;
v *= 0.01; // percent to ratio
}
// Continue one level up in the "ratio_over" hierarchy.
opt_def = print_config_def.get(opt_def->ratio_over);
assert(opt_def != nullptr);
}
output.set_d(v);
}
break;
}
default:
ctx->throw_exception("Unknown scalar variable type", opt.it_range);
}
}
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
output.it_range = opt.it_range;
}
// Return a boolean value, true if the scalar variable referenced by "opt" is nullable and it has a nil value.
// Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value.
template <typename Iterator>
static void is_nil_test_scalar(
static void is_nil_test(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
expr<Iterator> &output)
{
if (opt.opt->is_vector())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
output.set_b(opt.opt->is_nil());
if (opt.opt->is_vector()) {
if (! opt.has_index())
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
output.set_b(static_cast<const ConfigOptionVectorBase*>(opt.opt)->is_nil(opt.index >= int(vec->size()) ? 0 : size_t(opt.index)));
} else {
assert(opt.opt->is_scalar());
output.set_b(opt.opt->is_nil());
}
output.it_range = opt.it_range;
}
// Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value.
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
template <typename Iterator>
static void is_nil_test_vector(
static void variable_assign(
const MyContext *ctx,
OptWithPos<Iterator> &opt,
int &index,
Iterator it_end,
expr<Iterator> &output)
expr<Iterator> &param,
// Not used, just clear it.
std::string &out)
{
if (opt.opt->is_scalar())
ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range);
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index);
output.set_b(static_cast<const ConfigOptionVectorBase*>(opt.opt)->is_nil(idx));
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
if (! opt.writable)
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
if (opt.opt->is_vector()) {
if (! opt.has_index())
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
ConfigOptionVectorBase *vec = const_cast<ConfigOptionVectorBase*>(static_cast<const ConfigOptionVectorBase*>(opt.opt));
if (vec->empty())
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
if (opt.index >= int(vec->size()))
ctx->throw_exception("Index out of range", opt.it_range);
switch (opt.opt->type()) {
case coFloats:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
break;
case coInts:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
break;
case coStrings:
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
break;
case coPercents:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
break;
case coBools:
if (param.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
static_cast<ConfigOptionBools*>(vec)->values[opt.index] = param.b();
break;
default:
ctx->throw_exception("Unsupported output vector variable type", opt.it_range);
}
} else {
assert(opt.opt->is_scalar());
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
switch (wropt->type()) {
case coFloat:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
break;
case coInt:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
break;
case coString:
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
break;
case coPercent:
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
break;
case coBool:
if (param.type() != expr<Iterator>::TYPE_BOOL)
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
static_cast<ConfigOptionBool*>(wropt)->value = param.b();
break;
default:
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
}
}
out.clear();
}
// Verify that the expression returns an integer, which may be used
@ -1011,9 +1130,10 @@ namespace client
{ "multiplicative_expression", "Expecting an expression." },
{ "unary_expression", "Expecting an expression." },
{ "optional_parameter", "Expecting a closing brace or an optional parameter." },
{ "scalar_variable_reference", "Expecting a scalar variable reference."},
{ "is_nil_test", "Expecting a scalar variable reference."},
{ "one_of_list", "Expecting a list of string patterns (simple text or rexep)" },
{ "variable_reference", "Expecting a variable reference."},
{ "is_nil_test", "Expecting a scalar variable reference."},
{ "variable", "Expecting a variable name."},
{ "regular_expression", "Expecting a regular expression."}
};
@ -1131,6 +1251,7 @@ namespace client
qi::_a_type _a;
qi::_b_type _b;
qi::_r1_type _r1;
qi::_r2_type _r2;
// Starting symbol of the grammer.
// The leading eps is required by the "expectation point" operator ">".
@ -1165,7 +1286,9 @@ namespace client
macro =
(kw["if"] > if_else_output(_r1) [_val = _1])
// | (kw["switch"] > switch_output(_r1) [_val = _1])
| additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ];
| (assignment_statement(_r1) [_val = _1])
| (additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ])
;
macro.name("macro");
// An if expression enclosed in {} (the outmost {} are already parsed by the caller).
@ -1257,6 +1380,10 @@ namespace client
);
multiplicative_expression.name("multiplicative_expression");
assignment_statement =
(variable_reference(_r1) >> '=' > additive_expression(_r1))
[px::bind(&MyContext::variable_assign<Iterator>, _r1, _1, _2, _val)];
struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
{ out.it_range = boost::iterator_range<Iterator>(start_pos, start_pos); }
@ -1282,7 +1409,7 @@ namespace client
static void noexpr(expr<Iterator> &out) { out.reset(); }
};
unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
scalar_variable_reference(_r1) [ _val = _1 ]
variable_reference(_r1) [px::bind(&MyContext::variable_value<Iterator>, _r1, _1, _val)]
| (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ]
| (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ]
| (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ]
@ -1299,7 +1426,8 @@ namespace client
[ px::bind(&expr<Iterator>::template digits<true>, _val, _2, _3) ]
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1]
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
@ -1308,33 +1436,40 @@ namespace client
);
unary_expression.name("unary_expression");
one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2];
one_of.name("one_of");
one_of_list =
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
( ',' > *(
(
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
) >> -lit(','))
| eps
);
one_of_list.name("one_of_list");
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
);
optional_parameter.name("optional_parameter");
scalar_variable_reference =
variable_reference(_r1)[_a=_1] >>
(
('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index<Iterator>, _1, _b)] > ']' >
iter_pos[px::bind(&MyContext::vector_variable_reference<Iterator>, _r1, _a, _b, _1, _val)])
| eps[px::bind(&MyContext::scalar_variable_reference<Iterator>, _r1, _a, _val)]
);
scalar_variable_reference.name("scalar variable reference");
is_nil_test = variable_reference(_r1)[px::bind(&MyContext::is_nil_test<Iterator>, _r1, _1, _val)];
is_nil_test.name("is_nil test");
variable_reference = identifier
[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
variable_reference =
variable(_r1)[_a=_1] >>
(
('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index<Iterator>, _1, _b)] > ']' > iter_pos)
[px::bind(&MyContext::store_variable_index<Iterator>, _r1, _a, _b, _2, _val)]
| eps[_val=_a]
);
variable_reference.name("variable reference");
is_nil_test =
variable_reference(_r1)[_a=_1] >>
(
('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index<Iterator>, _1, _b)] > ']' >
iter_pos[px::bind(&MyContext::is_nil_test_vector<Iterator>, _r1, _a, _b, _1, _val)])
| eps[px::bind(&MyContext::is_nil_test_scalar<Iterator>, _r1, _a, _val)]
);
is_nil_test.name("is_nil test");
variable = identifier[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
variable.name("variable reference");
regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']];
regular_expression.name("regular_expression");
@ -1356,6 +1491,7 @@ namespace client
("random")
("round")
("not")
("one_of")
("or")
("true");
@ -1377,9 +1513,11 @@ namespace client
debug(additive_expression);
debug(multiplicative_expression);
debug(unary_expression);
debug(one_of);
debug(one_of_list);
debug(optional_parameter);
debug(scalar_variable_reference);
debug(variable_reference);
debug(variable);
debug(is_nil_test);
debug(regular_expression);
}
@ -1423,13 +1561,17 @@ namespace client
// Evaluate boolean expression into bool.
qi::rule<Iterator, bool(const MyContext*), spirit_encoding::space_type> bool_expr_eval;
// Reference of a scalar variable, or reference to a field of a vector variable.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> scalar_variable_reference;
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> variable_reference;
// Rule to translate an identifier to a ConfigOption, or to fail.
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable_reference;
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable;
// Evaluating whether a nullable variable is nil.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> is_nil_test;
qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit_encoding::space_type> is_nil_test;
// Evaluating "one of" list of patterns.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> &param), spirit_encoding::space_type> one_of_list;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
qi::symbols<char> keywords;
@ -1461,12 +1603,13 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
return output;
}
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context_data) const
{
client::MyContext context;
context.external_config = this->external_config();
context.config = &this->config();
context.config_override = config_override;
context.config_outputs = config_outputs;
context.current_extruder_id = current_extruder_id;
context.context_data = context_data;
return process_macro(templ, context);

View File

@ -55,8 +55,10 @@ public:
// Fill in the template using a macro processing language.
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const;
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context) const;
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const
{ return this->process(templ, current_extruder_id, config_override, nullptr /* config_outputs */, context); }
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);

View File

@ -1618,7 +1618,8 @@ void PrintObject::bridge_over_infill()
if (layer->lower_layer == nullptr) {
continue;
}
auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
// unsupported area will serve as a filter for polygons worth bridging.
Polygons unsupported_area;
Polygons lower_layer_solids;
bool contains_only_lightning = true;
@ -1627,6 +1628,7 @@ void PrintObject::bridge_over_infill()
contains_only_lightning = false;
}
Polygons fill_polys = to_polygons(region->fill_expolygons());
// initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts
unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end());
for (const Surface &surface : region->fill_surfaces()) {
if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) {
@ -1636,25 +1638,30 @@ void PrintObject::bridge_over_infill()
}
}
unsupported_area = closing(unsupported_area, SCALED_EPSILON);
lower_layer_solids = expand(lower_layer_solids, 4 * spacing);
unsupported_area = shrink(unsupported_area, 4 * spacing);
// By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
// NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
lower_layer_solids = expand(lower_layer_solids, 3 * spacing);
// By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
unsupported_area = shrink(unsupported_area, 3 * spacing);
unsupported_area = diff(unsupported_area, lower_layer_solids);
for (const LayerRegion *region : layer->regions()) {
SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid);
for (const Surface *s : region_internal_solids) {
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
// These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) {
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing));
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
// after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
auto area = p.area();
double area = p.area();
if (area < spacing * scale_(12.0) && area > spacing * spacing) {
worth_bridging.push_back(p);
}
}
worth_bridging = intersection(closing(worth_bridging, 2 * spacing), s->expolygon);
worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon);
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning));
#ifdef DEBUG_BRIDGE_OVER_INFILL
@ -1663,7 +1670,7 @@ void PrintObject::bridge_over_infill()
to_lines(unsupported_area));
#endif
#ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(s->expolygon)),
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
to_lines(unsupported_area));
@ -1728,15 +1735,15 @@ void PrintObject::bridge_over_infill()
layer_area_covered_by_candidates[pair.first] = {};
}
// prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise)
// and if the inflated AABB polygons overlap somewhere
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
&layer_area_covered_by_candidates](
tbb::blocked_range<size_t> r) {
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_with_candidates[job_idx];
for (const auto &candidate : surfaces_by_layer.at(lidx)) {
Polygon candiate_inflated_aabb = get_extents(candidate.new_polys)
.inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5)
.polygon();
Polygon candiate_inflated_aabb = get_extents(candidate.new_polys).inflated(scale_(7)).polygon();
layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx),
Polygons{candiate_inflated_aabb});
}
@ -2088,13 +2095,13 @@ void PrintObject::bridge_over_infill()
}
}
deep_infill_area = expand(deep_infill_area, spacing);
deep_infill_area = expand(deep_infill_area, spacing * 1.5);
// Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
Polygons expansion_area;
Polygons total_fill_area;
for (const LayerRegion *region : layer->regions()) {
Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal));
Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_types({stInternal, stInternalSolid}));
expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end());
Polygons fill_polys = to_polygons(region->fill_expolygons());
total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end());

View File

@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable)
void GLCanvas3D::enable_picking(bool enable)
{
m_picking_enabled = enable;
m_selection.set_mode(Selection::Instance);
}
void GLCanvas3D::enable_moving(bool enable)

View File

@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig
m_canvas->allow_multisample(OpenGLManager::can_multisample());
m_canvas->enable_picking(true);
m_canvas->get_selection().set_mode(Selection::Instance);
m_canvas->enable_moving(true);
// XXX: more config from 3D.pm
m_canvas->set_model(model);

View File

@ -51,7 +51,6 @@
#define SHOW_IMGUI_ATLAS
#define SHOW_ICONS_TEXTURE
#define SHOW_FINE_POSITION // draw convex hull around volume
#define SHOW_WX_WEIGHT_INPUT
#define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position
#define ALLOW_OPEN_NEAR_VOLUME
#define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread
@ -399,6 +398,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
return;
}
// Not known situation when could happend this is only for sure
if (!m_is_unknown_font && !m_style_manager.is_active_font())
create_notification_not_valid_font("No active font in style. Select correct one.");
else if (!m_is_unknown_font && !m_style_manager.get_wx_font().IsOk())
create_notification_not_valid_font("WxFont is not loaded properly.");
// Configuration creation
double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor();
float main_toolbar_height = m_parent.get_main_toolbar_height();
@ -1108,21 +1113,24 @@ void GLGizmoEmboss::draw_window()
if (ImGui::Button("add svg")) choose_svg_file();
#endif // ALLOW_DEBUG_MODE
bool is_active_font = m_style_manager.is_active_font();
if (!is_active_font)
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Warning: No font is selected. Select correct one."));
// Setter of indent must be befor disable !!!
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent);
ScopeGuard indent_sc([](){ ImGui::PopStyleVar(/*ImGuiStyleVar_IndentSpacing*/); });
// Disable all except selection of font, when open text from 3mf with unknown font
m_imgui->disabled_begin(m_is_unknown_font);
ScopeGuard unknown_font_sc([&]() {
m_imgui->disabled_end();
});
draw_text_input();
m_imgui->disabled_begin(!is_active_font);
ScopeGuard unknown_font_sc([imgui = m_imgui]() { imgui->disabled_end(/*m_is_unknown_font*/); });
draw_text_input();
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent);
ImGui::Indent();
draw_style_edit();
// When unknown font is inside .3mf only font selection is allowed
m_imgui->disabled_end(/*m_is_unknown_font*/);
draw_font_list_line();
m_imgui->disabled_begin(m_is_unknown_font);
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
draw_height(use_inch);
draw_depth(use_inch);
ImGui::Unindent();
// close advanced style property when unknown font is selected
@ -1139,8 +1147,6 @@ void GLGizmoEmboss::draw_window()
} else if (m_is_advanced_edit_style)
set_minimal_window_size(false);
ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing
ImGui::Separator();
draw_style_list();
@ -1150,8 +1156,6 @@ void GLGizmoEmboss::draw_window()
ImGui::Separator();
draw_model_type();
}
m_imgui->disabled_end(); // !is_active_font
#ifdef SHOW_WX_FONT_DESCRIPTOR
if (is_selected_style)
@ -1644,6 +1648,59 @@ bool GLGizmoEmboss::select_facename(const wxString &facename)
return true;
}
void GLGizmoEmboss::draw_font_list_line()
{
bool exist_stored_style = m_style_manager.exist_stored_style();
bool exist_change_in_font = m_style_manager.is_font_changed();
const std::string& font_text = m_gui_cfg->translations.font;
if (exist_change_in_font || !exist_stored_style)
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, font_text);
else
ImGuiWrapper::text(font_text);
ImGui::SameLine(m_gui_cfg->input_offset);
draw_font_list();
bool exist_change = false;
if (!m_is_unknown_font) {
ImGui::SameLine();
if (draw_italic_button())
exist_change = true;
ImGui::SameLine();
if (draw_bold_button())
exist_change = true;
} else {
// when exist unknown font add confirmation button
ImGui::SameLine();
// Apply for actual selected font
if (ImGui::Button(_u8L("Apply").c_str()))
exist_change = true;
}
EmbossStyle &style = m_style_manager.get_style();
if (exist_change_in_font) {
ImGui::SameLine(ImGui::GetStyle().FramePadding.x);
if (draw_button(m_icons, IconType::undo)) {
const EmbossStyle *stored_style = m_style_manager.get_stored_style();
style.path = stored_style->path;
style.prop.boldness = stored_style->prop.boldness;
style.prop.skew = stored_style->prop.skew;
wxFont new_wx_font = WxFontUtils::load_wxFont(style.path);
if (new_wx_font.IsOk() && m_style_manager.set_wx_font(new_wx_font))
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str());
}
if (exist_change) {
m_style_manager.clear_glyphs_cache();
process();
}
}
void GLGizmoEmboss::draw_font_list()
{
// Set partial
@ -1661,16 +1718,6 @@ void GLGizmoEmboss::draw_font_list()
// When deletation of font appear this variable is set
std::optional<size_t> del_index;
// When is unknown font is inside .3mf only font selection is allowed
// Stop Imgui disable + Guard again start disabling
ScopeGuard unknown_font_sc;
if (m_is_unknown_font) {
m_imgui->disabled_end();
unknown_font_sc.closure = [&]() {
m_imgui->disabled_begin(true);
};
}
// Code
const char *popup_id = "##font_list_popup";
const char *input_id = "##font_list_input";
@ -1797,13 +1844,6 @@ void GLGizmoEmboss::draw_font_list()
store(m_face_names);
}
if (m_is_unknown_font) {
ImGui::SameLine();
// Apply for actual selected font
if (ImGui::Button(_u8L("Apply").c_str()))
process();
}
#ifdef ALLOW_ADD_FONT_BY_FILE
ImGui::SameLine();
// select font file by file browser
@ -2488,89 +2528,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name,
undo_offset, draw_offseted_input);
}
void GLGizmoEmboss::draw_style_edit()
{
{
// Check correct WxFont
const wxFont &wx_font = m_style_manager.get_wx_font();
assert(wx_font.IsOk());
if (!wx_font.IsOk()) {
ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str());
return;
}
}
bool exist_stored_style = m_style_manager.exist_stored_style();
bool exist_change_in_font = m_style_manager.is_font_changed();
const GuiCfg::Translations &tr = m_gui_cfg->translations;
if (exist_change_in_font || !exist_stored_style)
ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font);
else
ImGuiWrapper::text(tr.font);
ImGui::SameLine(m_gui_cfg->input_offset);
draw_font_list();
bool exist_change = false;
if (!m_is_unknown_font) {
ImGui::SameLine();
if (draw_italic_button())
exist_change = true;
ImGui::SameLine();
if (draw_bold_button())
exist_change = true;
}
EmbossStyle &style = m_style_manager.get_style();
if (exist_change_in_font) {
ImGui::SameLine(ImGui::GetStyle().FramePadding.x);
if (draw_button(m_icons, IconType::undo)) {
const EmbossStyle *stored_style = m_style_manager.get_stored_style();
style.path = stored_style->path;
style.prop.boldness = stored_style->prop.boldness;
style.prop.skew = stored_style->prop.skew;
wxFont new_wx_font = WxFontUtils::load_wxFont(style.path);
if (new_wx_font.IsOk() &&
m_style_manager.set_wx_font(new_wx_font))
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str());
}
if (exist_change) {
m_style_manager.clear_glyphs_cache();
process();
}
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
draw_height(use_inch);
draw_depth(use_inch);
#ifdef SHOW_WX_WEIGHT_INPUT
if (wx_font.has_value()) {
ImGui::Text("%s", "weight");
ImGui::SameLine(m_gui_cfg->input_offset);
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
int weight = wx_font->GetNumericWeight();
int min_weight = 1, max_weight = 1000;
if (ImGui::SliderInt("##weight", &weight, min_weight, max_weight)) {
wx_font->SetNumericWeight(weight);
m_style_manager.wx_font_changed();
process();
}
wxFont f = wx_font->Bold();
bool disable = f == *wx_font;
ImGui::SameLine();
if (draw_button(IconType::bold, disable)) {
*wx_font = f;
m_style_manager.wx_font_changed();
process();
}
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", _u8L("wx Make bold").c_str());
}
#endif // SHOW_WX_WEIGHT_INPUT
}
bool GLGizmoEmboss::set_height() {
float &value = m_style_manager.get_style().prop.size_in_mm;
@ -3112,39 +3069,36 @@ bool GLGizmoEmboss::choose_svg_file()
void GLGizmoEmboss::create_notification_not_valid_font(
const TextConfiguration &tc)
{
// not neccessary, but for sure that old notification doesnt exist
if (m_is_unknown_font) remove_notification_not_valid_font();
m_is_unknown_font = true;
auto type = NotificationType::UnknownFont;
auto level =
NotificationManager::NotificationLevel::WarningNotificationLevel;
const EmbossStyle &es = m_style_manager.get_style();
const auto &face_name_opt = es.prop.face_name;
const auto &face_name_3mf_opt = tc.style.prop.face_name;
const std::string &face_name_3mf = tc.style.prop.face_name.value_or(tc.style.path);
const std::string &face_name_3mf = face_name_3mf_opt.has_value() ?
*face_name_3mf_opt :
tc.style.path;
std::string face_name_by_wx;
std::optional<std::string> face_name_by_wx;
if (!face_name_opt.has_value()) {
const wxFont& wx_font = m_style_manager.get_wx_font();
if (wx_font.IsOk()) {
wxString wx_face_name = wx_font.GetFaceName();
face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8());
if (!wx_face_name.empty())
face_name_by_wx = std::string(wx_face_name.ToUTF8().data());
}
}
const std::string &face_name = face_name_opt.has_value() ? *face_name_opt :
(!face_name_by_wx.empty() ? face_name_by_wx : es.path);
const std::string &face_name = face_name_opt.value_or(face_name_by_wx.value_or(es.path));
std::string text =
GUI::format(_L("Can't load exactly same font(\"%1%\"), "
"Aplication selected a similar one(\"%2%\"). "
"You have to specify font for enable edit text."),
face_name_3mf, face_name);
create_notification_not_valid_font(text);
}
void GLGizmoEmboss::create_notification_not_valid_font(const std::string &text) {
// not neccessary, but for sure that old notification doesnt exist
if (m_is_unknown_font)
remove_notification_not_valid_font();
m_is_unknown_font = true;
auto type = NotificationType::UnknownFont;
auto level = NotificationManager::NotificationLevel::WarningNotificationLevel;
auto notification_manager = wxGetApp().plater()->get_notification_manager();
notification_manager->push_notification(type, level, text);
}
@ -3278,7 +3232,6 @@ std::unique_ptr<DataBase> priv::create_emboss_data_base(const std::string &text,
cancel->store(true);
// create new shared ptr to cancel new job
cancel = std::make_shared<std::atomic<bool>>(false);
DataBase base(volume_name, cancel);
FontFileWithCache &font = style_manager.get_font_file_with_cache();
TextConfiguration tc{es, text};

View File

@ -100,8 +100,8 @@ private:
void init_font_name_texture();
struct FaceName;
void draw_font_preview(FaceName &face, bool is_visible);
void draw_font_list_line();
void draw_font_list();
void draw_style_edit();
void draw_height(bool use_inch);
void draw_depth(bool use_inch);
@ -148,6 +148,7 @@ private:
// When open text loaded from .3mf it could be written with unknown font
bool m_is_unknown_font = false;
void create_notification_not_valid_font(const TextConfiguration& tc);
void create_notification_not_valid_font(const std::string& text);
void remove_notification_not_valid_font();
// This configs holds GUI layout size given by translated texts.

View File

@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_
return;
}
ImTextureID id = (void *) icon.tex_id;
ImTextureID id = (void *)static_cast<intptr_t>(icon.tex_id);
const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size;
ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col);
}

View File

@ -3160,6 +3160,8 @@ void Plater::priv::split_object()
// causing original positions not to be kept
std::vector<size_t> idxs = load_model_objects(new_objects);
// clear previosli selection
get_selection().clear();
// select newly added objects
for (size_t idx : idxs)
{

View File

@ -53,8 +53,8 @@ static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const
return nearest_offset;
}
// Calculate scale in world
static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
// Calculate scale in world for check in debug
[[maybe_unused]] static std::optional<double> calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir)
{
Vec3d from_dir = from * dir;
Vec3d to_dir = to * dir;
@ -80,6 +80,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// allow moving with object again
canvas.enable_moving(true);
canvas.enable_picking(true);
surface_drag.reset();
// only left up is correct
@ -168,6 +169,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event,
// disable moving with object by mouse
canvas.enable_moving(false);
canvas.enable_picking(false);
return true;
}

View File

@ -399,7 +399,7 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
std::string name(stat.m_filename);
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
BOOST_LOG_TRIVIAL(error) << "Failed to unzip " << stat.m_filename;
continue;

View File

@ -1,25 +1,31 @@
#include "RaycastManager.hpp"
#include <utility>
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
using namespace Slic3r::GUI;
namespace priv {
namespace{
using namespace Slic3r;
static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr);
static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id);
static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){
void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr);
const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id);
RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){
return std::make_pair(instance.id().id, volume.id().id); }
static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) {
return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; }
static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) {
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key);
bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) {
return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); }
bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) {
return is_lower_key(i1.first, i2.first); };
template<typename VecType> inline void erase(std::vector<VecType> &vec, const std::vector<bool> &flags);
}
void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes)
{
// actualize MeshRaycaster
priv::actualize(m_meshes, object.volumes, skip, meshes);
::actualize(m_meshes, object.volumes, skip, meshes);
// check if inscance was removed
std::vector<bool> removed_transf(m_transformations.size(), {true});
@ -32,8 +38,8 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes
for (const ModelInstance *instance : object.instances) {
const Transform3d &instrance_tr = instance->get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = priv::create_key(*volume, *instance);
auto item = priv::find(m_transformations, key);
TrKey key = ::create_key(*volume, *instance);
auto item = ::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time
item->second = transformation;
@ -41,19 +47,17 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes
removed_transf[index] = false;
} else {
// add new transformation
m_transformations.emplace_back(std::make_pair(key, transformation));
m_transformations.emplace_back(key, transformation);
need_sort = true;
}
}
}
// clean other transformation
for (int i = removed_transf.size() - 1; i >= 0; --i)
if (removed_transf[i])
m_transformations.erase(m_transformations.begin() + i);
::erase(m_transformations, removed_transf);
if (need_sort)
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower);
}
void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes)
@ -61,7 +65,7 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip,
const ModelVolumePtrs &volumes = instance.get_object()->volumes;
// actualize MeshRaycaster
priv::actualize(m_meshes, volumes, skip, meshes);
::actualize(m_meshes, volumes, skip, meshes);
// check if inscance was removed
std::vector<bool> removed_transf(m_transformations.size(), {true});
@ -74,8 +78,8 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip,
const Transform3d &volume_tr = volume->get_matrix();
const Transform3d &instrance_tr = instance.get_matrix();
Transform3d transformation = instrance_tr * volume_tr;
TrKey key = priv::create_key(*volume, instance);
auto item = priv::find(m_transformations, key);
TrKey key = ::create_key(*volume, instance);
auto item = ::find(m_transformations, key);
if (item != m_transformations.end()) {
// actualize transformation all the time
item->second = transformation;
@ -83,40 +87,35 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip,
removed_transf[index] = false;
} else {
// add new transformation
m_transformations.emplace_back(std::make_pair(key, transformation));
m_transformations.emplace_back(key, transformation);
need_sort = true;
}
}
// clean other transformation
for (int i = removed_transf.size() - 1; i >= 0; --i)
if (removed_transf[i])
m_transformations.erase(m_transformations.begin() + i);
::erase(m_transformations, removed_transf);
if (need_sort)
std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower);
std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower);
}
std::optional<RaycastManager::Hit> RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const
{
// Improve: it is not neccessaru to use AABBMesh and calc normal for every hit
struct Result
{
const AABBMesh *mesh = nullptr;
double squared_distance;
int face;
Vec3d hit_world;
const Transform3d *tramsformation;
const TrKey *key;
}result;
// Results
const AABBMesh *hit_mesh = nullptr;
double hit_squared_distance = 0.;
int hit_face = -1;
Vec3d hit_world;
const Transform3d *hit_tramsformation = nullptr;
const TrKey *hit_key = nullptr;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
for (const auto &[key, transformation]: m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id)) continue;
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
const Transform3d &transformation = item.second;
Transform3d inv = transformation.inverse();
// transform input into mesh world
@ -129,46 +128,46 @@ std::optional<RaycastManager::Hit> RaycastManager::first_hit(const Vec3d& point,
const AABBMesh::hit_result &hit = hits.front();
// convert to world
Vec3d hit_world = transformation * hit.position();
double squared_distance = (point - hit_world).squaredNorm();
if (result.mesh != nullptr &&
result.squared_distance < squared_distance)
Vec3d world = transformation * hit.position();
double squared_distance = (point - world).squaredNorm();
if (hit_mesh != nullptr &&
hit_squared_distance < squared_distance)
continue; // exist closer one
result.mesh = mesh;
result.squared_distance = squared_distance;
result.face = hit.face();
result.hit_world = hit_world;
result.tramsformation = &transformation;
result.key = &key;
hit_mesh = mesh;
hit_squared_distance = squared_distance;
hit_face = hit.face();
hit_world = world;
hit_tramsformation = &transformation;
hit_key = &key;
}
if (result.mesh == nullptr)
if (hit_mesh == nullptr)
return {};
// Calculate normal from transformed triangle
// NOTE: Anisotropic transformation of normal is not perpendiculat to triangle
const Vec3i tri = result.mesh->indices(result.face);
Vec3d pts[3];
auto tr = result.tramsformation->linear();
const Vec3i tri = hit_mesh->indices(hit_face);
std::array<Vec3d,3> pts;
auto tr = hit_tramsformation->linear();
for (int i = 0; i < 3; ++i)
pts[i] = tr * result.mesh->vertices(tri[i]).cast<double>();
pts[i] = tr * hit_mesh->vertices(tri[i]).cast<double>();
Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]);
if (has_reflection(*hit_tramsformation))
normal_world *= -1;
normal_world.normalize();
SurfacePoint<double> point_world{result.hit_world, normal_world};
return RaycastManager::Hit{point_world, *result.key, result.squared_distance};
SurfacePoint<double> point_world{hit_world, normal_world};
return RaycastManager::Hit{point_world, *hit_key, hit_squared_distance};
}
std::optional<RaycastManager::Hit> RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const
{
std::optional<Hit> closest;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
for (const auto &[key, transformation] : m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id)) continue;
const Transform3d &transformation = item.second;
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
Transform3d tr_inv = transformation.inverse();
Vec3d mesh_point = tr_inv * point;
@ -196,14 +195,12 @@ std::optional<RaycastManager::Hit> RaycastManager::closest_hit(const Vec3d &poin
std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &point, const ISkip *skip) const
{
std::optional<ClosePoint> closest;
for (const auto &item : m_transformations) {
const TrKey &key = item.first;
for (const auto &[key, transformation] : m_transformations) {
size_t volume_id = key.second;
if (skip != nullptr && skip->skip(volume_id))
continue;
const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id);
const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id);
if (mesh == nullptr) continue;
const Transform3d &transformation = item.second;
Transform3d tr_inv = transformation.inverse();
Vec3d mesh_point = tr_inv * point;
@ -222,17 +219,15 @@ std::optional<RaycastManager::ClosePoint> RaycastManager::closest(const Vec3d &p
}
Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const {
// TODO: transformations are sorted use lower bound
auto item = std::find_if(m_transformations.begin(),
m_transformations.end(),
[&tr_key](const TrItem &it) -> bool {
return it.first == tr_key;
});
if (item == m_transformations.end()) return Transform3d::Identity();
return item->second;
auto tr = ::find(m_transformations, tr_key);
if (tr == m_transformations.end())
return Transform3d::Identity();
return tr->second;
}
void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs)
namespace {
void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs)
{
// check if volume was removed
std::vector<bool> removed_meshes(meshes.size(), {true});
@ -242,33 +237,33 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu
size_t oid = volume->id().id;
if (skip != nullptr && skip->skip(oid))
continue;
auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; });
if (item == meshes.end()) {
// exist AABB in inputs ?
if (inputs != nullptr) {
auto input = std::find_if(inputs->begin(), inputs->end(),
[oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; });
if (input != inputs->end()) {
meshes.emplace_back(std::move(*input));
continue;
}
}
// add new raycaster
bool calculate_epsilon = true;
auto mesh = std::make_unique<AABBMesh>(volume->mesh(), calculate_epsilon);
meshes.emplace_back(std::make_pair(oid, std::move(mesh)));
need_sort = true;
} else {
auto is_oid = [oid](const RaycastManager::Mesh &it) { return oid == it.first; };
if (auto item = std::find_if(meshes.begin(), meshes.end(), is_oid);
item != meshes.end()) {
size_t index = item - meshes.begin();
removed_meshes[index] = false;
continue;
}
// exist AABB in inputs ?
if (inputs != nullptr) {
auto input = std::find_if(inputs->begin(), inputs->end(), is_oid);
if (input != inputs->end()) {
meshes.emplace_back(std::move(*input));
need_sort = true;
continue;
}
}
// add new raycaster
bool calculate_epsilon = true;
auto mesh = std::make_unique<AABBMesh>(volume->mesh(), calculate_epsilon);
meshes.emplace_back(std::make_pair(oid, std::move(mesh)));
need_sort = true;
}
// clean other raycasters
for (int i = removed_meshes.size() - 1; i >= 0; --i)
if (removed_meshes[i])
meshes.erase(meshes.begin() + i);
erase(meshes, removed_meshes);
// All the time meshes must be sorted by volume id - for faster search
if (need_sort) {
@ -277,47 +272,73 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu
}
}
const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id)
const Slic3r::AABBMesh *get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id)
{
auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; };
auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) { return m.first < i; };
auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index);
if (it == meshes.end() || it->first != volume_id)
return nullptr;
return &(*(it->second));
}
RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) {
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool {
return priv::is_lower_key(it.first, key);
};
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) {
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); };
auto it = std::lower_bound(items.begin(), items.end(), key, fnc);
if (it == items.end() || it->first != key)
if (it != items.end() && it->first != key)
return items.end();
return it;
}
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key)
{
auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); };
auto it = std::lower_bound(items.begin(), items.end(), key, fnc);
if (it != items.end() && it->first != key)
return items.end();
return it;
}
template<typename VecType> inline void erase(std::vector<VecType> &vec, const std::vector<bool> &flags)
{
if (vec.size() < flags.size() || flags.empty())
return;
// reverse iteration over flags to erase indices from back to front.
for (int i = static_cast<int>(flags.size()) - 1; i >= 0; --i)
if (flags[i])
vec.erase(vec.begin() + i);
}
} // namespace
namespace Slic3r::GUI{
RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition)
{
SceneRaycaster::EType type = SceneRaycaster::EType::Volume;
auto scene_casters = canvas.get_raycasters_for_picking(type);
const std::vector<std::shared_ptr<SceneRaycasterItem>> &casters = *scene_casters;
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
const ModelObjectPtrs &objects = canvas.get_model()->objects;
SceneRaycaster::EType type = SceneRaycaster::EType::Volume;
auto scene_casters = canvas.get_raycasters_for_picking(type);
if (scene_casters == nullptr)
return {};
const std::vector<std::shared_ptr<SceneRaycasterItem>> &casters = *scene_casters;
const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes;
const ModelObjectPtrs &objects = canvas.get_model()->objects;
RaycastManager::Meshes meshes;
for (const std::shared_ptr<SceneRaycasterItem> &caster : casters) {
int index = SceneRaycaster::decode_id(type, caster->get_id());
if (index < 0 || index >= gl_volumes.size())
if (index < 0)
continue;
const GLVolume *gl_volume = gl_volumes[index];
const ModelVolume *volume = get_model_volume(*gl_volume, objects);
size_t id = volume->id().id;
auto index_ = static_cast<size_t>(index);
if(index_ >= gl_volumes.size())
continue;
const GLVolume *gl_volume = gl_volumes[index_];
if (gl_volume == nullptr)
continue;
const ModelVolume *volume = get_model_volume(*gl_volume, objects);
if (volume == nullptr)
continue;
size_t id = volume->id().id;
if (condition.skip(id))
continue;
auto mesh = std::make_unique<AABBMesh>(caster->get_raycaster()->get_aabb_mesh());

View File

@ -113,8 +113,33 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); }
SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); }
SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); }
SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); }
SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); }
SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); }
SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); }
SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); }
SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); }
SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); }
SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); }
SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); }
SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }
SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); }
SECTION("write to a scalar variable") {
DynamicConfig config_outputs;
config_outputs.set_key_value("writable_string", new ConfigOptionString());
parser.process("{writable_string = \"Written\"}", 0, nullptr, &config_outputs, nullptr);
REQUIRE(parser.process("{writable_string}", 0, nullptr, &config_outputs, nullptr) == "Written");
}
SECTION("write to a vector variable") {
DynamicConfig config_outputs;
config_outputs.set_key_value("writable_floats", new ConfigOptionFloats({ 0., 0., 0. }));
parser.process("{writable_floats[1] = 33}", 0, nullptr, &config_outputs, nullptr);
REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
}
}