Merge branch 'master' into fs_emboss

# Conflicts:
#	src/slic3r/GUI/GLCanvas3D.cpp
This commit is contained in:
Filip Sykala - NTB T15p 2022-11-21 10:16:25 +01:00
commit 7c1a513e5a
16 changed files with 924 additions and 414 deletions

View File

@ -1,9 +1,13 @@
min_slic3r_version = 2.5.0-alpha0
1.5.4 Added material profiles for Prusament Resin BioBased60.
1.5.3 Added filament profiles for ColorFabb VarioShore TPU, FormFutura PP, NinjaTek NinjaFlex/Cheetah TPU and for multiple Eolas Prints filaments. Updated bridging settings in 50um and 70um profiles.
1.5.2 Added SLA material profiles.
1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up).
1.5.0 Updated arachne parameters. Added profiles for Jessie filaments.
1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments.
1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format.
min_slic3r_version = 2.4.0-rc
1.4.8 Added filament and SLA material profiles. Updated settings.
1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments.
1.4.6 Added SLA materials. Updated filament profiles.
1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code.

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.5.1
config_version = 1.5.4
# 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%
@ -441,8 +441,8 @@ inherits = *common*
layer_height = 0.05
bottom_solid_layers = 10
bridge_acceleration = 300
bridge_flow_ratio = 1.15
bridge_speed = 15
bridge_flow_ratio = 0.6
bridge_speed = 25
default_acceleration = 1000
external_perimeter_speed = 20
fill_density = 20%
@ -463,12 +463,13 @@ perimeters = 3
support_material_speed = 30
top_solid_infill_speed = 20
top_solid_layers = 15
thick_bridges = 1
[print:*0.07mm*]
inherits = *0.05mm*
layer_height = 0.07
bottom_solid_layers = 8
bridge_flow_ratio = 1
bridge_flow_ratio = 0.6
fill_density = 15%
infill_speed = 40
solid_infill_speed = 40
@ -583,6 +584,8 @@ top_solid_layers = 4
inherits = *0.05mm*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
infill_extrusion_width = 0.5
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL]
inherits = *0.10mm*
@ -892,12 +895,16 @@ fill_pattern = gyroid
fill_density = 15%
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
top_infill_extrusion_width = 0.4
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.07mm ULTRADETAIL @MK3]
inherits = *0.07mm*; *MK3*
fill_pattern = gyroid
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
top_infill_extrusion_width = 0.4
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL @MK3]
inherits = *0.10mm*; *MK3*
@ -1269,6 +1276,8 @@ support_material_xy_spacing = 60%
support_material_speed = 30
support_material_extrusion_width = 0.35
bridge_acceleration = 300
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.07mm ULTRADETAIL @MINI]
inherits = *0.07mm*; *MINI*
@ -1282,6 +1291,8 @@ external_perimeter_extrusion_width = 0.4
support_material_xy_spacing = 60%
support_material_extrusion_width = 0.35
bridge_acceleration = 300
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL @MINI]
inherits = *0.10mm*; *MINI*
@ -1652,7 +1663,7 @@ filament_retract_length = 4
filament_retract_speed = 40
filament_deretract_speed = 15
filament_retract_lift = 0
filament_retract_before_travel = 7
filament_retract_before_travel = 6
filament_wipe = 0
bridge_fan_speed = 80
fan_always_on = 1
@ -2879,6 +2890,55 @@ min_print_speed = 15
slowdown_below_layer_time = 10
cooling = 1
[filament:NinjaTek NinjaFlex TPU]
inherits = *FLEX*
filament_vendor = NinjaTek
fan_always_on = 1
filament_max_volumetric_speed = 1.2
extrusion_multiplier = 1.2
first_layer_temperature = 238
first_layer_bed_temperature = 50
temperature = 238
bed_temperature = 50
bridge_fan_speed = 75
max_fan_speed = 60
min_fan_speed = 60
filament_retract_before_travel = 3
filament_cost = 85
filament_density = 1.19
filament_retract_length = 2.5
filament_retract_speed = 60
filament_deretract_speed = 25
filament_retract_lift = 0
filament_wipe = 0
disable_fan_first_layers = 1
full_fan_speed_layer = 3
min_print_speed = 10
slowdown_below_layer_time = 10
cooling = 1
[filament:NinjaTek Cheetah TPU]
inherits = NinjaTek NinjaFlex TPU
filament_retract_length = 1.5
filament_density = 1.22
filament_max_volumetric_speed = 4
extrusion_multiplier = 1.05
filament_retract_speed = 45
filament_deretract_speed = 25
first_layer_temperature = 240
temperature = 240
[filament:NinjaTek Cheetah TPU @MINI]
inherits = NinjaTek NinjaFlex TPU; *FLEXMINI*
filament_density = 1.22
filament_max_volumetric_speed = 3.5
extrusion_multiplier = 1.05
first_layer_temperature = 240
temperature = 240
filament_retract_speed = 50
filament_deretract_speed = 25
filament_retract_length = 4.8
[filament:Filatech FilaFlex40]
inherits = *FLEX*
filament_vendor = Filatech
@ -3174,7 +3234,8 @@ filament_retract_layer_change = nil
inherits = Ultrafuse ABS
filament_density = 1.08
first_layer_bed_temperature = 105
temperature = 250
first_layer_temperature = 270
temperature = 270
filament_colour = #FFF8D9
filament_notes = "Material Description\nABS Fusion+ made with Polyscope XILOY™ 3D is an engineering filament which has been optimized for 3D-printing. This special grade has been developed in collaboration with Polyscope Polymers - renowned for its material solutions in the automotive industry. ABS is a thermoplastic which is used in many applications. Although ABS has been classified as a standard material in 3D-printing it is known to be quite challenging to process. ABS Fusion+ combines the properties of ABS with an improved processability. The filament is based on an ABS grade which can be directly printed on glass without any adhesives or tape and has a higher success rate of prints due to extreme low warping."
filament_retract_before_travel = 2
@ -3943,6 +4004,76 @@ filament_density = 1.24
filament_spool_weight = 230
compatible_printers_condition = nozzle_diameter[0]!=0.8 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Eolas Prints PLA]
inherits = *PLA*
filament_vendor = Eolas Prints
filament_cost = 23.50
filament_density = 1.24
filament_spool_weight = 0
filament_colour = #4D9398
filament_max_volumetric_speed = 15
temperature = 208
[filament:Eolas Prints PLA Matte]
inherits = Eolas Prints PLA
filament_cost = 25.50
filament_max_volumetric_speed = 14
temperature = 212
[filament:Eolas Prints INGEO 850]
inherits = Eolas Prints PLA
filament_cost = 25.90
temperature = 210
[filament:Eolas Prints INGEO 870]
inherits = Eolas Prints PLA
filament_cost = 25.90
temperature = 215
first_layer_bed_temperature = 68
first_layer_temperature = 220
bed_temperature = 65
[filament:Eolas Prints PETG]
inherits = *PET*
filament_vendor = Eolas Prints
filament_cost = 29.90
filament_density = 1.27
filament_spool_weight = 0
filament_colour = #4D9398
filament_max_volumetric_speed = 8
temperature = 240
first_layer_bed_temperature = 85
first_layer_temperature = 235
bed_temperature = 90
filament_retract_length = 1.1
filament_retract_lift = 0.22
[filament:Eolas Prints PETG @MINI]
inherits = Eolas Prints PETG; *PETMINI*
[filament:Eolas Prints PETG - UV Resistant]
inherits = Eolas Prints PETG
filament_cost = 35.90
temperature = 237
first_layer_temperature = 232
[filament:Eolas Prints PETG - UV Resistant @MINI]
inherits = Eolas Prints PETG - UV Resistant; *PETMINI*
[filament:Eolas Prints TPU 93A]
inherits = *FLEX*
filament_vendor = Eolas Prints
filament_cost = 34.99
filament_density = 1.21
filament_spool_weight = 1000
filament_colour = #4D9398
filament_max_volumetric_speed = 1.2
temperature = 235
first_layer_bed_temperature = 30
bed_temperature = 30
filament_retract_length = 0
extrusion_multiplier = 1.16
[filament:Fiberlogy Easy PLA]
inherits = *PLA*
renamed_from = Fiberlogy PLA
@ -4041,22 +4172,19 @@ disable_fan_first_layers = 5
[filament:Fiberlogy PCTG]
inherits = Fiberlogy CPE HT
filament_vendor = Fiberlogy
filament_cost = 29.41
filament_density = 1.23
extrusion_multiplier = 0.98
min_fan_speed = 10
max_fan_speed = 15
bridge_fan_speed = 50
min_print_speed = 15
first_layer_temperature = 265
temperature = 265
first_layer_bed_temperature = 90
bed_temperature = 90
filament_type = CPE
fan_below_layer_time = 20
slowdown_below_layer_time = 15
disable_fan_first_layers = 5
filament_type = PCTG
[filament:Fiberlogy PCTG @MINI]
inherits = Fiberlogy PCTG; *PETMINI*
[filament:Fiberlogy FiberFlex 40D]
inherits = *FLEX*
@ -4331,7 +4459,7 @@ filament_loading_speed_start = 19
filament_minimal_purge_on_wipe_tower = 15
filament_unloading_speed_start = 100
full_fan_speed_layer = 4
filament_max_volumetric_speed = 13
filament_max_volumetric_speed = 12
[filament:Generic PLA @MMU2]
inherits = *PLA MMU2*
@ -4350,6 +4478,13 @@ filament_cost = 36.29
filament_density = 1.24
filament_spool_weight = 201
[filament:Jessie PLA @MMU2]
inherits = *PLA MMU2*
filament_vendor = Printed Solid
filament_cost = 21
filament_density = 1.24
filament_max_volumetric_speed = 10
[filament:Prusament PVB @MMU2]
inherits = *PLA MMU2*
filament_vendor = Prusa Polymers
@ -4402,6 +4537,17 @@ full_fan_speed_layer = 6
filament_retract_length = 1.2
filament_deretract_speed = 20
[filament:ColorFabb VarioShore TPU]
inherits = Fillamentum Flexfill 98A
filament_vendor = ColorFabb
filament_colour = #BBBBBB
filament_cost = 71.35
filament_density = 1.22
filament_spool_weight = 0
extrusion_multiplier = 0.85
first_layer_temperature = 220
temperature = 220
[filament:Taulman Bridge]
inherits = *common*
filament_vendor = Taulman
@ -4653,6 +4799,42 @@ min_fan_speed = 100
start_filament_gcode = "M900 K0 ; Filament gcode"
temperature = 220
[filament:FormFutura Centaur PP]
inherits = *common*
filament_vendor = FormFutura
filament_cost = 70
filament_density = 0.89
filament_spool_weight = 212
bridge_fan_speed = 100
cooling = 1
disable_fan_first_layers = 2
extrusion_multiplier = 1.05
fan_always_on = 1
fan_below_layer_time = 100
filament_colour = #DEE0E6
filament_max_volumetric_speed = 4
filament_type = PP
first_layer_bed_temperature = 85
bed_temperature = 85
first_layer_temperature = 235
max_fan_speed = 70
min_fan_speed = 70
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.1{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K28{elsif nozzle_diameter[0]==0.8};{else}M900 K48{endif} ; Filament gcode LA 1.0"
temperature = 235
filament_wipe = 0
filament_retract_lift = 0
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:FormFutura Centaur PP @MINI]
inherits = FormFutura Centaur PP
filament_max_volumetric_speed = 3
filament_retract_length = 3.5
filament_retract_speed = 45
filament_deretract_speed = 20
filament_retract_lift = 0
filament_retract_before_travel = 4
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model=="MINI"
## Filaments MMU1
[filament:ColorFabb HT @MMU1]
@ -5775,6 +5957,30 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.025]
inherits = *common 0.025*
exposure_time = 7
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.025]
inherits = *common 0.025*
exposure_time = 7
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.025]
inherits = *common 0.025*
exposure_time = 6
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.025
[sla_material:Prusa Orange Tough @0.025]
@ -6732,6 +6938,30 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.05
[sla_material:Prusa Beige Tough @0.05]
@ -7120,6 +7350,30 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.1]
inherits = *common 0.1*
exposure_time = 13
initial_exposure_time = 45
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.1]
inherits = *common 0.1*
exposure_time = 13
initial_exposure_time = 45
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.1]
inherits = *common 0.1*
exposure_time = 8
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.1
[sla_material:Prusa Orange Tough @0.1]
@ -7336,6 +7590,31 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 3.5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 3.5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 2.8
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.025
[sla_material:Prusa Orange Tough @0.025 SL1S]
@ -7531,6 +7810,15 @@ material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:BlueCast X-One @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 6
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:PrimaCreator Tough Light Grey @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 1.8
@ -7754,6 +8042,31 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 3
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.05
[sla_material:Prusa Orange Tough @0.05 SL1S]
@ -8128,6 +8441,15 @@ material_type = Tough
material_vendor = BlueCast
material_colour = #007EFD
[sla_material:BlueCast X-One @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 7
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:NextDent Model 2.0 Grey @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4
@ -8476,6 +8798,31 @@ material_type = Tough
material_vendor = Prusa Polymers
material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.1
[sla_material:Prusa Orange Tough @0.1 SL1S]
@ -8671,6 +9018,15 @@ material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:BlueCast X-One @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 8.5
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:PrimaCreator Tough Light Grey @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 3

View File

@ -25,6 +25,7 @@
namespace Slic3r {
// execute fn for each pixel on the line. If fn returns false, terminate the iteration
template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
{
coord_t dx = abs(x1 - x0);
@ -39,7 +40,7 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
dy *= 2;
for (; n > 0; --n) {
fn(x, y);
if (!fn(x, y)) return;
if (error > 0) {
x += x_inc;
@ -58,8 +59,8 @@ template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, c
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
normal.x() = ceil(normal.x());
normal.y() = ceil(normal.y());
Point start_offset = Point(x0,y0) + (normal).cast<coord_t>();
Point end_offset = Point(x1,y1) + (normal).cast<coord_t>();
Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
dda(x0, y0, x1, y1, fn);
dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn);
@ -169,7 +170,10 @@ public:
float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); }
size_t unique_id(Node n) const { return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y())); }
size_t unique_id(Node n) const
{
return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y()));
}
const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
};
@ -189,6 +193,7 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
obstacle_min.x() = std::min(obstacle_min.x(), x);
obstacle_min.y() = std::min(obstacle_min.y(), y);
inpassable.insert(Pixel{x, y});
return true;
};
for (const Line &l : obstacles) {
@ -200,36 +205,13 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin)
{
if (layer != nullptr) { this->print_z = layer->print_z; }
if (layer == nullptr) return;
auto store_obstacle = [&](coord_t x, coord_t y) {
obstacle_max.x() = std::max(obstacle_max.x(), x);
obstacle_max.y() = std::max(obstacle_max.y(), y);
obstacle_min.x() = std::min(obstacle_min.x(), x);
obstacle_min.y() = std::min(obstacle_min.y(), y);
inpassable.insert(Pixel{x, y});
};
this->print_z = layer->print_z;
Lines obstacles;
for (size_t step = 0; step < 3; step++) {
if (layer != nullptr) {
obstacles.insert(obstacles.end(), layer->malformed_lines.begin(), layer->malformed_lines.end());
layer = layer->lower_layer;
} else {
break;
}
}
for (const Line &l : obstacles) {
Pixel start = pixelize(l.a + global_origin);
Pixel end = pixelize(l.b + global_origin);
double_dda_with_offset(start.x(), start.y(), end.x(), end.y(), store_obstacle);
}
#ifdef DEBUG_FILES
::Slic3r::SVG svg(debug_out_path(("obstacles_jps" + std::to_string(print_z) + "_" + std::to_string(rand() % 1000)).c_str()).c_str(),
get_extents(obstacles));
svg.draw(obstacles);
svg.Close();
#endif
obstacles.reserve(layer->malformed_lines.size());
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
add_obstacles(obstacles);
}
Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
@ -238,15 +220,34 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
Pixel end = pixelize(p1);
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
BoundingBox search_box({start,end,obstacle_max,obstacle_min});
search_box.max += Pixel(1,1);
search_box.min -= Pixel(1,1);
if (inpassable.find(start) != inpassable.end()) {
dda(start.x(), start.y(), end.x(), end.y(), [&](coord_t x, coord_t y) {
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
start = Pixel(x, y);
return false;
}
return true;
});
}
if (inpassable.find(end) != inpassable.end()) {
dda(end.x(), end.y(), start.x(), start.y(), [&](coord_t x, coord_t y) {
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
end = Pixel(x, y);
return false;
}
return true;
});
}
BoundingBox bounding_square(Points{start,end});
bounding_square.max += Pixel(5,5);
bounding_square.min -= Pixel(5,5);
coord_t bounding_square_size = 2*std::max(bounding_square.size().x(),bounding_square.size().y());
BoundingBox search_box({start, end, obstacle_max, obstacle_min});
search_box.max += Pixel(1, 1);
search_box.min -= Pixel(1, 1);
BoundingBox bounding_square(Points{start, end});
bounding_square.max += Pixel(5, 5);
bounding_square.min -= Pixel(5, 5);
coord_t bounding_square_size = 2 * std::max(bounding_square.size().x(), bounding_square.size().y());
bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2;
@ -283,9 +284,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
closest_qnode = astar_cache[closest_qnode].parent;
}
} else {
for (const auto& node : out_nodes) {
out_path.push_back(node.position);
}
for (const auto &node : out_nodes) { out_path.push_back(node.position); }
out_path.push_back(start);
}
@ -335,7 +334,11 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
if (i - index_of_last_stored_point < 2) continue;
bool passable = true;
auto store_obstacle = [&](coord_t x, coord_t y) {
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) { passable = false; };
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) {
passable = false;
return false;
}
return true;
};
dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle);
if (!passable) {

View File

@ -339,32 +339,9 @@ void MeasuringImpl::extract_features()
}
circles_lengths.clear(); // no longer needed, make it obvious
// Some of the "circles" may actually be polygons (5-8 vertices). We want them
// detected as edges, but also to remember the center and save it into those edges.
// We will add all such edges manually and delete the detected circles, leaving it
// in circles_idxs so they are not picked again.
assert(circles.size() == circles_idxs.size());
for (int i=circles.size()-1; i>=0; --i) {
if (circles_idxs[i].first == 0 && circles_idxs[i].second == border.size()-1) {
int N = circles_idxs[i].second - circles_idxs[i].first;
if (N <= 8) {
if (N >= 5) { // polygon = 5,6,7,8 vertices
const Vec3d center = std::get<0>(circles[i].get_circle());
for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j)
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
border[j - 1], border[j], std::make_optional(center)));
} else {
// This will be handled just like a regular edge (squares, triangles).
circles_idxs.erase(circles_idxs.begin() + i);
}
circles.erase(circles.begin() + i);
}
}
}
// Anything under 5 vertices shall not be considered a circle.
assert(circles_idxs.size() == circles.size());
for (int i=0; i<int(circles_idxs.size()); ++i) {
for (int i=int(circles_idxs.size())-1; i>=0; --i) {
const auto& [start, end] = circles_idxs[i];
int N = start >= 0
? end - start + (start == 0 && end == border.size()-1 ? 0 : 1) // last point is the same as first
@ -372,7 +349,15 @@ void MeasuringImpl::extract_features()
if (N < 5) {
circles.erase(circles.begin() + i);
circles_idxs.erase(circles_idxs.begin() + i);
--i;
} else if (N <= 8 && start == 0 && end == border.size()-1) {
// This is a regular 5-8 polygon. Add the edges as edges with a special
// point and remove the circle. Leave the indices in circles_idxs, so
// the edges are not picked up again later.
const Vec3d center = std::get<0>(circles[i].get_circle());
for (int j=1; j<=end; ++j)
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
border[j - 1], border[j], std::make_optional(center)));
circles.erase(circles.begin() + i);
}
}

View File

@ -768,54 +768,47 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
}
#define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
//#define EXTRA_PERIM_DEBUG_FILES
// #define EXTRA_PERIM_DEBUG_FILES
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
// Polygons filled by those clipped perimeters
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(const Surface &surface,
ExPolygons infill_area,
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
const Polygons &lower_slices_polygons,
const Flow &overhang_flow,
size_t standard_perimeters_count,
double scaled_resolution,
const PrintObjectConfig &object_config,
const PrintConfig &print_config)
{
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
ExPolygons local_area = intersection_ex({surface.expolygon}, infill_area);
ExPolygons anchors = intersection_ex(local_area, lower_slices_polygons);
ExPolygons overhangs = diff_ex(local_area, lower_slices_polygons);
if (overhangs.empty()) {
return {};
}
Polygons anchors = intersection(infill_area, lower_slices_polygons);
Polygons overhangs = diff(infill_area, lower_slices_polygons);
if (overhangs.empty()) { return {}; }
ExPolygons inset_anchors; // anchored area inset by the anchor length
Polygons inset_anchors; // anchored area inset by the anchor length
{
std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(),
anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(),
anchors_size * 0.66 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 1.00};
std::vector<ExPolygons> anchor_areas_w_delta_anchor_size{};
std::vector<Polygons> anchor_areas_w_delta_anchor_size{};
for (double delta : deltas) {
anchor_areas_w_delta_anchor_size.push_back(diff_ex(anchors, offset_ex(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
anchor_areas_w_delta_anchor_size.push_back(diff(anchors, expand(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
}
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) {
ExPolygons clipped = diff_ex(anchor_areas_w_delta_anchor_size[i], offset_ex(anchor_areas_w_delta_anchor_size[i + 1],
Polygons clipped = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS));
anchor_areas_w_delta_anchor_size[i] = intersection_ex(anchor_areas_w_delta_anchor_size[i],
offset_ex(clipped, deltas[i] + deltas[i + 1],
anchor_areas_w_delta_anchor_size[i] = intersection(anchor_areas_w_delta_anchor_size[i],
expand(clipped, deltas[i+1] + 0.1*overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS));
}
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
inset_anchors.insert(inset_anchors.end(), anchor_areas_w_delta_anchor_size[i].begin(),
anchor_areas_w_delta_anchor_size[i].end());
inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
}
inset_anchors = union_ex(inset_anchors);
inset_anchors = closing_ex(to_polygons(inset_anchors), 1.0 * overhang_flow.scaled_spacing(), 1.0 * overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS);
inset_anchors = opening(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
inset_anchors = closing(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
#ifdef EXTRA_PERIM_DEBUG_FILES
{
@ -832,58 +825,57 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
#endif
}
ExPolygons inset_overhang_area = diff_ex(local_area, inset_anchors);
ExPolygons repeated_area = offset_ex(intersection_ex(offset_ex(inset_overhang_area, 1.10 * overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS),
inset_anchors),
0.1 * overhang_flow.scaled_spacing(), EXTRA_PERIMETER_OFFSET_PARAMETERS);
Polygons inset_overhang_area = diff(infill_area, inset_anchors);
#ifdef EXTRA_PERIM_DEBUG_FILES
{
BoundingBox bbox = get_extents(inset_overhangs_w_anchors);
BoundingBox bbox = get_extents(inset_overhang_area);
bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path("inset_overhangs_w_anchors").c_str(), bbox);
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "purple", scale_(0.25));
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.20));
for (const Line &line : to_lines(repeated_area)) svg.draw(line, "green", scale_(0.15));
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "yellow", scale_(0.10));
::Slic3r::SVG svg(debug_out_path("inset_overhang_area").c_str(), bbox);
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "purple", scale_(0.25));
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.15));
svg.Close();
}
#endif
Polygons area_left_unfilled;
Polygons inset_overhang_area_left_unfilled;
std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts
for (const ExPolygon &overhang : inset_overhang_area) {
ExPolygons overhang_to_cover = {overhang};
overhang_to_cover.insert(overhang_to_cover.end(), repeated_area.begin(), repeated_area.end());
overhang_to_cover = union_ex(overhang_to_cover);
for (const ExPolygon &overhang : union_ex(to_expolygons(inset_overhang_area))) {
Polygons overhang_to_cover = to_polygons(overhang);
Polygons expanded_overhang_to_cover = expand(overhang_to_cover, 1.1 * overhang_flow.scaled_spacing());
Polygons shrinked_overhang_to_cover = shrink(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing());
ExPolygons real_overhang = intersection_ex({overhang_to_cover}, overhangs);
Polygons real_overhang = intersection(overhang_to_cover, overhangs);
if (real_overhang.empty()) {
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover));
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), overhang_to_cover.begin(),
overhang_to_cover.end());
continue;
}
extra_perims.emplace_back();
std::vector<ExtrusionPaths> &overhang_region = extra_perims.back();
ExPolygons anchoring = intersection_ex({overhang_to_cover}, inset_anchors);
ExPolygons perimeter_polygon = offset_ex(overhang_to_cover, -overhang_flow.scaled_spacing() * 0.6);
Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors);
Polygons perimeter_polygon = offset(union_(expand(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing()), anchoring),
-overhang_flow.scaled_spacing() * 0.6);
double perimeter_polygon_area = area(perimeter_polygon);
Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring);
double unbridgeable_area = area(diff_ex(perimeter_polygon, {anchoring_convex_hull}));
for (const ExPolygon &exp : perimeter_polygon) {
for (const Polygon &hole : exp.holes) { unbridgeable_area += std::abs(area(hole)); }
double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull}));
// penalize also holes
for (const Polygon &poly : perimeter_polygon) {
if (poly.is_clockwise()) { // hole, penalize bridges.
unbridgeable_area += std::abs(area(poly));
}
auto [dir, unsupp_dist] = detect_bridging_direction(to_polygons(real_overhang), to_polygons(anchors));
}
auto [dir, unsupp_dist] = detect_bridging_direction(real_overhang, anchors);
#ifdef EXTRA_PERIM_DEBUG_FILES
{
BoundingBox bbox = get_extents(inset_overhang_area);
BoundingBox bbox = get_extents(anchoring_convex_hull);
bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path(("bridge_check").c_str()).c_str(), bbox);
::Slic3r::SVG svg(debug_out_path("bridge_check").c_str(), bbox);
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "purple", scale_(0.25));
for (const Line &line : to_lines(real_overhang)) svg.draw(line, "red", scale_(0.20));
for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15));
@ -892,10 +884,8 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
svg.Close();
}
#endif
auto total_length_of_real_overhang = total_length(to_polygons(real_overhang));
if (unbridgeable_area < 0.2 * perimeter_polygon_area && unsupp_dist < total_length_of_real_overhang * 0.5) {
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover));
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.125) {
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end());
perimeter_polygon.clear();
} else {
// fill the overhang with perimeters
@ -903,16 +893,15 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
while (continuation_loops > 0) {
auto prev = perimeter_polygon;
// prepare next perimeter lines
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area);
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
// do not add the perimeter to result yet, first check if perimeter_polygon is not empty after shrinking - this would mean
// that the polygon was possibly too small for full perimeter loop and in that case try gap fill first
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end());
perimeter_polygon = union_ex(perimeter_polygon);
perimeter_polygon = intersection_ex({overhang_to_cover}, offset_ex(perimeter_polygon, -overhang_flow.scaled_spacing()));
perimeter_polygon = union_(perimeter_polygon, anchoring);
perimeter_polygon = intersection(offset(perimeter_polygon, -overhang_flow.scaled_spacing()), expanded_overhang_to_cover);
if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width
ExPolygons shrinked = offset_ex(prev, -0.4 * overhang_flow.scaled_spacing());
Polygons shrinked = offset(prev, -0.4 * overhang_flow.scaled_spacing());
if (!shrinked.empty()) {
overhang_region.emplace_back();
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
@ -922,8 +911,10 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
Polylines fills;
ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) :
offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5);
//gap = expolygons_simplify(gap, overhang_flow.scaled_spacing());
for (const ExPolygon &ep : gap) {
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.35 * overhang_flow.scaled_width(), &fills);
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.3 * overhang_flow.scaled_width(), &fills);
}
if (!fills.empty()) {
fills = intersection_pl(fills, inset_overhang_area);
@ -938,10 +929,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
overhang_flow.width(), overhang_flow.height());
}
if (intersection(perimeter_polygon, real_overhang).empty()) {
continuation_loops--;
}
if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; }
if (prev == perimeter_polygon) {
#ifdef EXTRA_PERIM_DEBUG_FILES
@ -957,15 +945,14 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
break;
}
}
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area);
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
overhang_region.emplace_back();
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
overhang_flow.width(), overhang_flow.height());
perimeter_polygon = offset_ex(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end());
perimeter_polygon = union_ex(perimeter_polygon);
area_left_unfilled = union_(area_left_unfilled, to_polygons(perimeter_polygon));
perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
perimeter_polygon = union_(perimeter_polygon, anchoring);
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), perimeter_polygon.begin(),perimeter_polygon.end());
#ifdef EXTRA_PERIM_DEBUG_FILES
BoundingBox bbox = get_extents(inset_overhang_area);
@ -974,30 +961,32 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "blue", scale_(0.05));
for (const Line &line : to_lines(anchoring)) svg.draw(line, "green", scale_(0.05));
for (const Line &line : to_lines(overhang_to_cover)) svg.draw(line, "yellow", scale_(0.05));
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
svg.Close();
#endif
std::reverse(overhang_region.begin(), overhang_region.end()); //reverse the order, It shall be printed from inside out
std::reverse(overhang_region.begin(), overhang_region.end()); // reverse the order, It shall be printed from inside out
}
}
std::vector<ExtrusionPaths> result{};
for (const std::vector<ExtrusionPaths> &paths : extra_perims) {
result.push_back(sort_and_connect_extra_perimeters(paths, 1.5 * overhang_flow.scaled_spacing()));
result.push_back(sort_and_connect_extra_perimeters(paths, 2.0 * overhang_flow.scaled_spacing()));
}
#ifdef EXTRA_PERIM_DEBUG_FILES
BoundingBox bbox = get_extents(inset_overhang_area);
bbox.offset(scale_(2.));
::Slic3r::SVG svg(debug_out_path(("final" + std::to_string(rand())).c_str()).c_str(), bbox);
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "blue", scale_(0.05));
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "green", scale_(0.05));
for (const Line &line : to_lines(diff(inset_overhangs_w_anchors, area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "blue", scale_(0.05));
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "green", scale_(0.05));
for (const Line &line : to_lines(diff(inset_overhang_area, inset_overhang_area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
svg.Close();
#endif
return {result, diff(inset_overhang_area, area_left_unfilled)};
inset_overhang_area_left_unfilled = union_(inset_overhang_area_left_unfilled);
return {result, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
}
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
@ -1215,31 +1204,23 @@ void PerimeterGenerator::process_arachne()
// collapse too narrow infill areas
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces
this->fill_surfaces->append(
offset2_ex(
union_ex(pp),
float(- min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.)),
stInternal);
// append infill areas to fill_surfaces
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.));
this->fill_surfaces->append(infill_areas, stInternal);
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
ExPolygons infill_area;
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); }
auto [extra_perimeters,
filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area,
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
this->lower_slices_polygons(),
this->overhang_flow, this->config->perimeters,
this->m_scaled_resolution,
this->overhang_flow, this->m_scaled_resolution,
*this->object_config, *this->print_config);
if (!extra_perimeters.empty()) {
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
ExtrusionEntityCollection new_perimeters{};
new_perimeters.no_sort = this_islands_perimeters->no_sort;
for (const ExtrusionPaths& paths : extra_perimeters) {
new_perimeters.append(paths);
}
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
new_perimeters.append(this_islands_perimeters->entities);
this_islands_perimeters->swap(new_perimeters);
@ -1516,34 +1497,25 @@ void PerimeterGenerator::process_classic()
// collapse too narrow infill areas
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces
this->fill_surfaces->append(
offset2_ex(
union_ex(pp),
float(- inset - min_perimeter_infill_spacing / 2.),
float(min_perimeter_infill_spacing / 2.)),
stInternal);
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-inset - min_perimeter_infill_spacing / 2.),
float(min_perimeter_infill_spacing / 2.));
this->fill_surfaces->append(infill_areas, stInternal);
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
ExPolygons infill_area;
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); }
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area,
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
this->lower_slices_polygons(),
this->overhang_flow, this->config->perimeters,
this->m_scaled_resolution,
this->overhang_flow, this->m_scaled_resolution,
*this->object_config, *this->print_config);
if (!extra_perimeters.empty()) {
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
ExtrusionEntityCollection new_perimeters{};
new_perimeters.no_sort = this_islands_perimeters->no_sort;
for (const ExtrusionPaths& paths : extra_perimeters) {
new_perimeters.append(paths);
}
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
new_perimeters.append(this_islands_perimeters->entities);
this_islands_perimeters->swap(new_perimeters);
SurfaceCollection orig_surfaces = *this->fill_surfaces;
this->fill_surfaces->clear();
for (const auto &surface : orig_surfaces.surfaces) {

View File

@ -58,7 +58,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
// Cache the plenty of parameters, which influence the G-code generator only,
// or they are only notes not influencing the generated G-code.
static std::unordered_set<std::string> steps_gcode = {
"avoid_curled_filament_during_travels",
"avoid_crossing_perimeters",
"avoid_crossing_perimeters_max_detour",
"bed_shape",
@ -223,6 +222,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
osteps.emplace_back(posInfill);
osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirtBrim);
} else if (opt_key == "avoid_curled_filament_during_travels") {
osteps.emplace_back(posEstimateCurledExtrusions);
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?

View File

@ -1231,13 +1231,13 @@ struct LayerCurlingEstimator
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx);
current_line.malformation += 0.85 * nearest_line.malformation;
current_line.malformation += 0.9 * nearest_line.malformation;
}
if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) {
float factor = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
float factor = 0.5f + 0.5f * std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
(max_malformation_dist - min_malformation_dist);
malformation_acc.add_distance(current_line.len);
current_line.malformation += l->height * factor * (2.0f + 3.0f * (malformation_acc.max_curvature / PI));
current_line.malformation += l->height * factor * (1.5f + 3.0f * (malformation_acc.max_curvature / PI));
current_line.malformation = std::min(current_line.malformation, float(l->height * params.max_malformation_factor));
} else {
malformation_acc.reset();

View File

@ -26,7 +26,7 @@ struct Params {
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
const float bridge_distance = 12.0f; //mm
const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) )
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.45, -0.1 };
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.50, -0.1 };
const float max_malformation_factor = 10.0f;
const float min_distance_between_support_points = 3.0f; //mm

View File

@ -5174,6 +5174,8 @@ bool GLCanvas3D::_init_main_toolbar()
item.left.toggable = true;
item.left.render_callback = [this](float left, float right, float, float) {
if (m_canvas != nullptr) {
if (!m_canvas->HasFocus())
m_canvas->SetFocus();
if (_render_search_list(0.5f * (left + right)))
_deactivate_search_toolbar_item();
}

View File

@ -217,9 +217,7 @@ ObjectList::ObjectList(wxWindow* parent) :
Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this);
Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this);
#ifdef __WXMSW__
Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &ObjectList::OnEditingStarted, this);
#endif /* __WXMSW__ */
Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this);
Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this);
@ -1897,11 +1895,9 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
// If last volume item with warning was deleted, unmark object item
if (type & itVolume) {
add_volumes_to_object_in_list(obj_idx);
const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats());
m_objects_model->UpdateWarningIcon(parent, icon_name);
}
else
m_objects_model->Delete(item);
update_info_items(obj_idx);
@ -3065,7 +3061,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue;
if (item->type&itVolume) {
add_volumes_to_object_in_list(item->obj_idx);
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
ModelObject* obj = object(item->obj_idx);
if (obj->volumes.size() == 1) {
wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx);
@ -4653,17 +4649,19 @@ void ObjectList::ItemValueChanged(wxDataViewEvent &event)
}
}
void ObjectList::OnEditingStarted(wxDataViewEvent &event)
{
m_is_editing_started = true;
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
// Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column.
void ObjectList::OnEditingStarted(wxDataViewEvent &event)
{
m_last_selected_column = -1;
}
#endif //__WXMSW__
}
void ObjectList::OnEditingDone(wxDataViewEvent &event)
{
m_is_editing_started = false;
if (event.GetColumn() != colName)
return;

View File

@ -176,6 +176,7 @@ private:
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
int m_last_selected_column = -1;
#endif /* __MSW__ */
bool m_is_editing_started{ false };
#if 0
SettingsFactory::Bundle m_freq_settings_fff;
@ -406,6 +407,8 @@ public:
void apply_volumes_order();
bool has_paint_on_segmentation();
bool is_editing() const { return m_is_editing_started; }
private:
#ifdef __WXOSX__
// void OnChar(wxKeyEvent& event);
@ -419,10 +422,8 @@ private:
bool can_drop(const wxDataViewItem& item) const ;
void ItemValueChanged(wxDataViewEvent &event);
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
void OnEditingStarted(wxDataViewEvent &event);
#endif /* __WXMSW__ */
void OnEditingDone(wxDataViewEvent &event);
};

View File

@ -75,14 +75,25 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t
std::string ret;
switch (type) {
case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; }
case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; }
case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; }
case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; }
case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Point on circle"); break; }
case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; }
default: { assert(false); break; }
}
return ret;
}
static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type)
{
std::string ret;
switch (type) {
case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Center of edge"); break; }
case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Center of circle"); break; }
default: { assert(false); break; }
}
return ret;
}
static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector<std::vector<int>>& planes_triangles, int idx)
{
assert(0 <= idx && idx < (int)planes_triangles.size());
@ -251,7 +262,21 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
m_mouse_left_down = false;
return false;
}
else if (mouse_event.Dragging()) {
// Enable/Disable panning/rotating the 3D scene
// Ctrl is pressed or the mouse is not hovering a selected volume
bool unlock_dragging = mouse_event.CmdDown() || (m_hover_id == -1 && !m_parent.get_selection().contains_volume(m_parent.get_first_hover_volume_idx()));
// mode is not center selection or mouse is not hovering a center
unlock_dragging &= !mouse_event.ShiftDown() || (m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID && m_hover_id != POINT_ID);
return !unlock_dragging;
}
else if (mouse_event.LeftDown()) {
// let the event pass through to allow panning/rotating the 3D scene
if ((m_mode != EMode::CenterSelection && mouse_event.CmdDown()) ||
(m_mode == EMode::CenterSelection && m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID && m_hover_id != POINT_ID)) {
return false;
}
if (m_hover_id != -1) {
SelectedFeatures selected_features_old = m_selected_features;
m_mouse_left_down = true;
@ -263,10 +288,41 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value())
item = m_selected_features.second;
else {
item = {
(m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()),
(m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature
};
switch (m_mode)
{
case EMode::FeatureSelection:
{
item = { surface_feature_type_as_string(m_curr_feature->get_type()), m_curr_feature };
break;
}
case EMode::PointSelection:
{
item = { point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id), Measure::SurfaceFeature(*m_curr_point_on_feature_position) };
break;
}
case EMode::CenterSelection:
{
Vec3d position;
switch (m_curr_feature->get_type())
{
case Measure::SurfaceFeatureType::Circle:
{
position = std::get<0>(m_curr_feature->get_circle());
break;
}
case Measure::SurfaceFeatureType::Edge:
{
assert(m_curr_feature->get_extra_point().has_value());
position = *m_curr_feature->get_extra_point();
break;
}
default: { assert(false); break; }
}
item = { center_on_feature_type_as_string(m_curr_feature->get_type()), Measure::SurfaceFeature(position) };
break;
}
}
}
return item;
};
@ -284,8 +340,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
m_selected_features.second.reset();
else {
m_selected_features.second = item;
if (m_mode == EMode::ExtendedSelection)
if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection)
m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster));
if (m_mode == EMode::CenterSelection) {
// Fake ctrl up event to exit the center selection mode
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), true, false, false);
// increase counter to avoid that keeping the ctrl key pressed triggers a ctrl down event
m_ctrl_kar_filter.increase_count();
}
}
}
else {
@ -296,8 +358,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
else {
const SelectedFeatures::Item item = item_from_feature();
m_selected_features.first = item;
if (m_mode == EMode::ExtendedSelection)
if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection)
m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster));
if (m_mode == EMode::CenterSelection) {
// Fake ctrl up event to exit the center selection mode
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), true, false, false);
// increase counter to avoid that keeping the ctrl key pressed triggers a ctrl down event
m_ctrl_kar_filter.increase_count();
}
}
if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) {
@ -330,11 +398,19 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
// avoid closing the gizmo if the user clicks outside of any volume
return true;
}
else if (mouse_event.RightDown() && mouse_event.CmdDown()) {
else if (mouse_event.RightDown()) {
// let the event pass through to allow panning/rotating the 3D scene
if ((m_mode != EMode::CenterSelection && mouse_event.CmdDown()) || (m_mode == EMode::CenterSelection && m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID)) {
std::cout << "RightDown -> false\n";
return false;
}
if (mouse_event.ShiftDown()) {
m_selected_features.reset();
m_selection_raycasters.clear();
m_parent.request_extra_frame();
}
}
else if (mouse_event.Leaving())
m_mouse_left_down = false;
@ -369,21 +445,50 @@ void GLGizmoMeasure::data_changed()
m_is_editing_distance_first_frame = true;
}
static bool feature_has_center(std::optional<Measure::SurfaceFeature> feature)
{
return feature.has_value() ?
(feature->get_type() == Measure::SurfaceFeatureType::Circle || (feature->get_type() == Measure::SurfaceFeatureType::Edge && feature->get_extra_point().has_value()))
: false;
}
bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
if (action == SLAGizmoEventType::CtrlDown) {
auto activate_center_selection = [this, shift_down, control_down](SLAGizmoEventType action) {
bool ret = false;
switch (action)
{
case SLAGizmoEventType::CtrlDown: { ret = shift_down && feature_has_center(m_curr_feature); break; }
case SLAGizmoEventType::ShiftDown: { ret = control_down && feature_has_center(m_curr_feature); break; }
default: { break; }
}
return ret;
};
if (action == SLAGizmoEventType::ShiftDown) {
if (m_shift_kar_filter.is_first()) {
m_mode = activate_center_selection(SLAGizmoEventType::ShiftDown) ? EMode::CenterSelection : EMode::PointSelection;
disable_scene_raycasters();
}
m_shift_kar_filter.increase_count();
}
else if (action == SLAGizmoEventType::ShiftUp) {
m_shift_kar_filter.reset_count();
m_mode = EMode::FeatureSelection;
restore_scene_raycasters_state();
}
else if (action == SLAGizmoEventType::CtrlDown) {
if (m_ctrl_kar_filter.is_first()) {
if (m_curr_feature.has_value()) {
m_mode = EMode::ExtendedSelection;
if (activate_center_selection(SLAGizmoEventType::CtrlDown)) {
m_mode = EMode::CenterSelection;
disable_scene_raycasters();
}
}
m_ctrl_kar_filter.increase_count();
}
else if (action == SLAGizmoEventType::CtrlUp) {
m_ctrl_kar_filter.reset_count();
m_mode = EMode::BasicSelection;
m_mode = control_down ? EMode::PointSelection : EMode::FeatureSelection;
restore_scene_raycasters_state();
}
@ -400,6 +505,7 @@ void GLGizmoMeasure::on_set_state()
{
if (m_state == Off) {
m_ctrl_kar_filter.reset_count();
m_shift_kar_filter.reset_count();
m_curr_feature.reset();
m_curr_point_on_feature_position.reset();
restore_scene_raycasters_state();
@ -408,7 +514,7 @@ void GLGizmoMeasure::on_set_state()
m_measuring.reset();
}
else {
m_mode = EMode::BasicSelection;
m_mode = EMode::FeatureSelection;
// store current state of scene raycaster for later use
m_scene_raycasters.clear();
auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume);
@ -467,7 +573,7 @@ void GLGizmoMeasure::on_render()
Vec3f normal_on_model;
size_t model_facet_idx;
const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx);
const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1;
const bool is_hovering_on_feature = (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection) && m_hover_id != -1;
auto update_circle = [this, inv_zoom]() {
if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) {
@ -483,9 +589,19 @@ void GLGizmoMeasure::on_render()
return false;
};
if (m_mode == EMode::BasicSelection) {
std::optional<Measure::SurfaceFeature> curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast<double>()) : std::nullopt;
if (m_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) {
if ((m_hover_id == SELECTION_1_ID && boost::algorithm::istarts_with(m_selected_features.first.source, _u8L("Center"))) ||
(m_hover_id == SELECTION_2_ID && boost::algorithm::istarts_with(m_selected_features.second.source, _u8L("Center")))) {
// Skip feature detection if hovering on a selected center
m_curr_feature.reset();
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID);
m_curr_point_on_feature_position.reset();
}
else {
std::optional<Measure::SurfaceFeature> curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast<double>()) : std::nullopt;
if (m_curr_feature != curr_feature ||
(curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) {
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID);
@ -537,7 +653,11 @@ void GLGizmoMeasure::on_render()
}
}
}
else if (is_hovering_on_locked_feature) {
}
if (m_mode != EMode::PointSelection)
m_curr_point_on_feature_position.reset();
else if (is_hovering_on_feature) {
auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function<Vec3f(const Vec3f&)> callback = nullptr) -> Vec3d {
auto it = m_raycasters.find(feature_type_id);
if (it != m_raycasters.end() && it->second != nullptr) {
@ -554,6 +674,7 @@ void GLGizmoMeasure::on_render()
return Vec3d::Zero();
};
if (m_curr_feature.has_value()) {
switch (m_curr_feature->get_type())
{
default: { assert(false); break; }
@ -583,7 +704,7 @@ void GLGizmoMeasure::on_render()
m_curr_point_on_feature_position = center;
else {
const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; });
const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()* normal, m_volume_matrix * center);
const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal, m_volume_matrix * center);
const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), normal);
const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof);
double angle = std::atan2(local_proj.y(), local_proj.x());
@ -597,6 +718,7 @@ void GLGizmoMeasure::on_render()
}
}
}
}
else {
if (m_curr_feature.has_value() && m_curr_feature->get_type() == Measure::SurfaceFeatureType::Circle) {
if (update_circle()) {
@ -617,7 +739,6 @@ void GLGizmoMeasure::on_render()
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.25f);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
@ -634,8 +755,13 @@ void GLGizmoMeasure::on_render()
shader->set_uniform("view_normal_matrix", view_normal_matrix);
};
auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector<ColorRGBA>& colors,
float inv_zoom, bool update_raycasters_transform) {
auto set_emission_uniform = [this, shader](const ColorRGBA& color, bool hover) {
shader->set_uniform("emission_factor", (color == m_parent.get_selection().get_first_volume()->render_color) ? 0.0f :
hover ? 0.5f : 0.25f);
};
auto render_feature = [this, set_matrix_uniforms, set_emission_uniform](const Measure::SurfaceFeature& feature, const std::vector<ColorRGBA>& colors,
float inv_zoom, bool hover, bool update_raycasters_transform) {
switch (feature.get_type())
{
default: { assert(false); break; }
@ -644,6 +770,7 @@ void GLGizmoMeasure::on_render()
const Vec3d position = TransformHelper::model_to_world(feature.get_point(), m_volume_matrix);
const Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(feature_matrix);
set_emission_uniform(colors.front(), hover);
m_sphere.model.set_color(colors.front());
m_sphere.model.render();
if (update_raycasters_transform) {
@ -657,20 +784,23 @@ void GLGizmoMeasure::on_render()
{
const auto& [center, radius, normal] = feature.get_circle();
// render center
if (update_raycasters_transform) {
const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix);
const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(center_matrix);
set_emission_uniform(colors.front(), hover);
m_sphere.model.set_color(colors.front());
m_sphere.model.render();
if (update_raycasters_transform) {
auto it = m_raycasters.find(POINT_ID);
if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(center_matrix);
}
// render circle
if (m_mode != EMode::CenterSelection) {
const Transform3d circle_matrix = Transform3d::Identity();
set_matrix_uniforms(circle_matrix);
if (update_raycasters_transform) {
set_emission_uniform(colors.back(), hover);
m_circle.model.set_color(colors.back());
m_circle.model.render();
auto it = m_raycasters.find(CIRCLE_ID);
@ -681,35 +811,40 @@ void GLGizmoMeasure::on_render()
GLModel circle;
GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast<float>(), float(radius), 5.0f * inv_zoom, normal.cast<float>(), m_volume_matrix.cast<float>());
circle.init_from(std::move(circle_geometry));
set_emission_uniform(colors.back(), hover);
circle.set_color(colors.back());
circle.render();
}
}
break;
}
case Measure::SurfaceFeatureType::Edge:
{
const auto& [from, to] = feature.get_edge();
// render extra point
if (update_raycasters_transform) {
const std::optional<Vec3d> extra = feature.get_extra_point();
if (extra.has_value()) {
const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix);
const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(point_matrix);
set_emission_uniform(colors.front(), hover);
m_sphere.model.set_color(colors.front());
m_sphere.model.render();
if (update_raycasters_transform) {
auto it = m_raycasters.find(POINT_ID);
if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(point_matrix);
}
}
// render edge
if (m_mode != EMode::CenterSelection) {
const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix);
const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix);
const Transform3d edge_matrix = Geometry::translation_transform(from_world) *
Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) *
Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() });
set_matrix_uniforms(edge_matrix);
set_emission_uniform(colors.back(), hover);
m_cylinder.model.set_color(colors.back());
m_cylinder.model.render();
if (update_raycasters_transform) {
@ -717,6 +852,7 @@ void GLGizmoMeasure::on_render()
if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(edge_matrix);
}
}
break;
}
case Measure::SurfaceFeatureType::Plane:
@ -724,6 +860,7 @@ void GLGizmoMeasure::on_render()
const auto& [idx, normal, pt] = feature.get_plane();
assert(idx < m_plane_models_cache.size());
set_matrix_uniforms(m_volume_matrix);
set_emission_uniform(colors.front(), hover);
m_plane_models_cache[idx].set_color(colors.front());
m_plane_models_cache[idx].render();
if (update_raycasters_transform) {
@ -737,19 +874,19 @@ void GLGizmoMeasure::on_render()
};
auto hover_selection_color = [this]() {
return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f);
return !m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR;
};
auto hovering_color = [this, hover_selection_color, &selection]() {
return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color();
return (m_mode == EMode::PointSelection) ? selection.get_first_volume()->render_color : hover_selection_color();
};
if (m_curr_feature.has_value()) {
std::vector<ColorRGBA> colors;
if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature)
colors.emplace_back(SELECTED_1ST_COLOR);
colors.emplace_back(hovering_color());
else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature)
colors.emplace_back(SELECTED_2ND_COLOR);
colors.emplace_back(hovering_color());
else {
switch (m_curr_feature->get_type())
{
@ -774,12 +911,12 @@ void GLGizmoMeasure::on_render()
}
}
render_feature(*m_curr_feature, colors, inv_zoom, true);
render_feature(*m_curr_feature, colors, inv_zoom, true, true);
}
if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) {
const std::vector<ColorRGBA> colors = { SELECTED_1ST_COLOR };
render_feature(*m_selected_features.first.feature, colors, inv_zoom, false);
render_feature(*m_selected_features.first.feature, colors, inv_zoom, m_hover_id == SELECTION_1_ID, false);
if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) {
auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(),
[](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; });
@ -789,7 +926,7 @@ void GLGizmoMeasure::on_render()
}
if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) {
const std::vector<ColorRGBA> colors = { SELECTED_2ND_COLOR };
render_feature(*m_selected_features.second.feature, colors, inv_zoom, false);
render_feature(*m_selected_features.second.feature, colors, inv_zoom, m_hover_id == SELECTION_2_ID, false);
if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) {
auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(),
[](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; });
@ -798,12 +935,14 @@ void GLGizmoMeasure::on_render()
}
}
if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) {
if (is_hovering_on_feature && m_curr_point_on_feature_position.has_value()) {
if (m_hover_id != POINT_ID) {
const Vec3d position = TransformHelper::model_to_world(*m_curr_point_on_feature_position, m_volume_matrix);
const Transform3d matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(matrix);
m_sphere.model.set_color(hover_selection_color());
const ColorRGBA color = hover_selection_color();
set_emission_uniform(color, true);
m_sphere.model.set_color(color);
m_sphere.model.render();
}
}
@ -1343,12 +1482,15 @@ void GLGizmoMeasure::render_debug_dialog()
{
case Measure::SurfaceFeatureType::Point:
{
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
const Vec3d position = m_volume_matrix * item.feature->get_point();
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text));
break;
}
case Measure::SurfaceFeatureType::Edge:
{
auto [from, to] = item.feature->get_edge();
from = m_volume_matrix * from;
to = m_volume_matrix * to;
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text));
break;
@ -1356,6 +1498,8 @@ void GLGizmoMeasure::render_debug_dialog()
case Measure::SurfaceFeatureType::Plane:
{
auto [idx, normal, origin] = item.feature->get_plane();
origin = m_volume_matrix * origin;
normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal;
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text));
@ -1364,6 +1508,10 @@ void GLGizmoMeasure::render_debug_dialog()
case Measure::SurfaceFeatureType::Circle:
{
auto [center, radius, normal] = item.feature->get_circle();
const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true));
center = m_volume_matrix * center;
normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized();
radius = (on_circle - center).norm();
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text));
@ -1376,6 +1524,28 @@ void GLGizmoMeasure::render_debug_dialog()
};
m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
if (ImGui::BeginTable("Mode", 2)) {
std::string txt;
switch (m_mode)
{
case EMode::FeatureSelection: { txt = "Feature selection"; break; }
case EMode::PointSelection: { txt = "Point selection"; break; }
case EMode::CenterSelection: { txt = "Center selection"; break; }
default: { assert(false); break; }
}
add_strings_row_to_table(*m_imgui, "Mode", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text));
ImGui::EndTable();
}
ImGui::Separator();
if (ImGui::BeginTable("Hover", 2)) {
add_strings_row_to_table(*m_imgui, "Hover id", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(m_hover_id), ImGui::GetStyleColorVec4(ImGuiCol_Text));
const std::string txt = m_curr_feature.has_value() ? surface_feature_type_as_string(m_curr_feature->get_type()) : "None";
add_strings_row_to_table(*m_imgui, "Current feature", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text));
ImGui::EndTable();
}
ImGui::Separator();
if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value())
m_imgui->text("Empty selection");
else {
@ -1402,7 +1572,7 @@ void GLGizmoMeasure::render_debug_dialog()
void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit)
{
static std::optional<Measure::SurfaceFeature> last_feature;
static EMode last_mode = EMode::BasicSelection;
static EMode last_mode = EMode::FeatureSelection;
static SelectedFeatures last_selected_features;
static float last_y = 0.0f;
@ -1436,12 +1606,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
std::string text;
ColorRGBA color;
if (m_selected_features.second.feature.has_value()) {
if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::BasicSelection)
if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::FeatureSelection)
text = _u8L("Unselect feature");
else if (m_hover_id == SELECTION_2_ID)
text = _u8L("Unselect point");
text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point");
else
text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point");
text = (m_mode == EMode::PointSelection) ? _u8L("Select point") :
(m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature");
color = SELECTED_2ND_COLOR;
}
else {
@ -1449,11 +1620,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
if (m_selected_features.first.feature == m_curr_feature)
text = _u8L("Unselect feature");
else if (m_hover_id == SELECTION_1_ID)
text = _u8L("Unselect point");
text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point");
color = SELECTED_1ST_COLOR;
}
if (text.empty()) {
text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point");
text = (m_mode == EMode::PointSelection) ? _u8L("Select point") :
(m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature");
color = m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR;
}
}
@ -1467,18 +1639,23 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
}
);
if (m_selected_features.first.feature.has_value()) {
add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
if (m_mode == EMode::FeatureSelection && m_hover_id != -1) {
add_strings_row_to_table(*m_imgui, _u8L("Shift"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count;
}
if (m_mode == EMode::BasicSelection && m_hover_id != -1) {
add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
if (m_mode != EMode::CenterSelection && feature_has_center(m_curr_feature)) {
add_strings_row_to_table(*m_imgui, _u8L("Shift") + "+" + CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable center selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count;
}
if (m_selected_features.first.feature.has_value()) {
add_strings_row_to_table(*m_imgui, _u8L("Shift") + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count;
}
// add dummy rows to keep dialog size fixed
for (unsigned int i = row_count; i < 3; ++i) {
for (unsigned int i = row_count; i < 4; ++i) {
add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text));
}
@ -1492,16 +1669,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
//bool data_text_set = false;
//ImGui::Separator();
//if (feature_type != Measure::SurfaceFeatureType::Undef) {
// if (m_mode == EMode::BasicSelection) {
// if (m_mode == EMode::FeatureSelection) {
// m_imgui->text(surface_feature_type_as_string(feature_type));
// data_text_set = true;
// }
// else if (m_mode == EMode::ExtendedSelection) {
// else if (m_mode == EMode::PointSelection) {
// if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) {
// m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id));
// data_text_set = true;
// }
// }
// else if (m_mode == EMode::CenterSelection) {
// if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) {
// m_imgui->text(center_on_feature_type_as_string(feature_type));
// data_text_set = true;
// }
// }
//}
//if (!data_text_set)
// m_imgui->text(_u8L("No feature"));
@ -1509,7 +1692,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
//const unsigned int max_data_row_count = 3;
//unsigned int data_row_count = 0;
//if (ImGui::BeginTable("Data", 2)) {
// if (m_mode == EMode::BasicSelection) {
// if (m_mode == EMode::FeatureSelection) {
// switch (feature_type)
// {
// default: { break; }

View File

@ -23,8 +23,9 @@ class GLGizmoMeasure : public GLGizmoBase
{
enum class EMode : unsigned char
{
BasicSelection,
ExtendedSelection
FeatureSelection,
PointSelection,
CenterSelection
};
struct SelectedFeatures
@ -67,7 +68,7 @@ class GLGizmoMeasure : public GLGizmoBase
}
};
EMode m_mode{ EMode::BasicSelection };
EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result;
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
@ -114,6 +115,7 @@ class GLGizmoMeasure : public GLGizmoBase
Vec2d m_mouse_pos{ Vec2d::Zero() };
KeyAutoRepeatFilter m_ctrl_kar_filter;
KeyAutoRepeatFilter m_shift_kar_filter;
SelectedFeatures m_selected_features;
bool m_pending_scale{ false };

View File

@ -26,6 +26,7 @@ enum class SLAGizmoEventType : unsigned char {
SelectAll,
CtrlDown,
CtrlUp,
ShiftDown,
ShiftUp,
AltUp,
ApplyChanges,

View File

@ -608,10 +608,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
const int keyCode = evt.GetKeyCode();
bool processed = false;
if (evt.GetEventType() == wxEVT_KEY_UP)
{
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut)
{
if (evt.GetEventType() == wxEVT_KEY_UP) {
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) {
GLGizmoBase* gizmo = get_current();
const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode();
const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
@ -627,18 +625,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
processed = true;
}
}
else if (m_current == Measure && keyCode == WXK_CONTROL) {
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false);
else if (m_current == Measure) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
}
// if (processed)
// m_parent.set_cursor(GLCanvas3D::Standard);
}
else if (evt.GetEventType() == wxEVT_KEY_DOWN)
{
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT))
&& get_current()->is_in_editing_mode())
{
else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
if (m_current == SlaSupports && (keyCode == WXK_SHIFT || keyCode == WXK_ALT)
&& get_current()->is_in_editing_mode()) {
// m_parent.set_cursor(GLCanvas3D::Cross);
processed = true;
}
@ -664,8 +663,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
if (simplify != nullptr)
processed = simplify->on_esc_key_down();
}
else if (m_current == Measure && keyCode == WXK_CONTROL) {
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true);
else if (m_current == Measure) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
}
}

View File

@ -4890,12 +4890,12 @@ void Plater::priv::set_bed_shape(const Pointfs& shape, const double max_print_he
bool Plater::priv::can_delete() const
{
return !get_selection().is_empty() && !get_selection().is_wipe_tower();
return !get_selection().is_empty() && !get_selection().is_wipe_tower() && !sidebar->obj_list()->is_editing();
}
bool Plater::priv::can_delete_all() const
{
return !model.objects.empty();
return !model.objects.empty() && !sidebar->obj_list()->is_editing();
}
bool Plater::priv::can_fix_through_netfabb() const