Merge remote-tracking branch 'origin/master' into ys_color_print_extension

This commit is contained in:
YuSanka 2019-10-09 11:34:57 +02:00
commit 09a7b348f4
46 changed files with 2030 additions and 1030 deletions

View File

@ -82,6 +82,29 @@ variants = default
technology = SLA
family = SL1
[default_filaments]
Generic PLA = 1
Generic PLA MMU2 = 1
Prusa PLA = 1
Prusa PLA MMU2 = 1
Prusament PLA = 1
Prusament PLA MMU2 = 1
[default_sla_materials]
Prusa Azure Blue Tough 0.05 = 1
Prusa Black Tough 0.05 = 1
Prusa Green Casting 0.05 = 1
Prusa Grey Tough 0.05 = 1
Prusa Maroon Tough 0.05 = 1
Prusa Orange Tough 0.025 = 1
Prusa Orange Tough 0.035 = 1
Prusa Orange Tough 0.05 = 1
Prusa Orange Tough 0.1 = 1
Prusa Pink Tough 0.05 = 1
Prusa Skin Tough 0.05 = 1
Prusa Transparent Red Tough 0.05 = 1
Prusa White Tough 0.05 = 1
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@ -1128,6 +1151,7 @@ filament_density = 3.9
filament_colour = #804040
filament_max_volumetric_speed = 9
filament_notes = "List of materials tested with standard print settings:\n\nColorFabb bronzeFill\nColorFabb brassFill\nColorFabb steelFill\nColorFabb copperFill"
filament_vendor = ColorFabb
[filament:ColorFabb HT]
inherits = *PET*
@ -1145,11 +1169,13 @@ max_fan_speed = 20
min_fan_speed = 10
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode"
temperature = 270
filament_vendor = ColorFabb
[filament:ColorFabb PLA-PHA]
inherits = *PLA*
filament_cost = 55.5
filament_density = 1.24
filament_vendor = ColorFabb
[filament:ColorFabb woodFill]
inherits = *PLA*
@ -1163,6 +1189,7 @@ filament_max_volumetric_speed = 10
first_layer_temperature = 200
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 200
filament_vendor = ColorFabb
[filament:ColorFabb corkFill]
inherits = *PLA*
@ -1175,6 +1202,7 @@ filament_max_volumetric_speed = 6
first_layer_temperature = 220
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 220
filament_vendor = ColorFabb
[filament:ColorFabb XT]
inherits = *PET*
@ -1184,6 +1212,7 @@ filament_density = 1.27
first_layer_bed_temperature = 90
first_layer_temperature = 260
temperature = 270
filament_vendor = ColorFabb
[filament:ColorFabb XT-CF20]
inherits = *PET*
@ -1199,6 +1228,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el
temperature = 260
filament_retract_length = nil
filament_retract_lift = 0.2
filament_vendor = ColorFabb
[filament:ColorFabb nGen]
inherits = *PET*
@ -1211,6 +1241,7 @@ filament_type = NGEN
first_layer_temperature = 240
max_fan_speed = 35
min_fan_speed = 20
filament_vendor = ColorFabb
[filament:ColorFabb nGen flex]
inherits = *FLEX*
@ -1231,12 +1262,14 @@ temperature = 260
filament_retract_length = nil
filament_retract_lift = 0
compatible_printers_condition = nozzle_diameter[0]>0.35 and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
filament_vendor = ColorFabb
[filament:E3D Edge]
inherits = *PET*
filament_cost = 56.9
filament_density = 1.26
filament_type = EDGE
filament_vendor = E3D
[filament:E3D PC-ABS]
inherits = *ABS*
@ -1245,6 +1278,7 @@ filament_type = PC
filament_density = 1.05
first_layer_temperature = 270
temperature = 270
filament_vendor = E3D
[filament:Fillamentum ABS]
inherits = *ABS*
@ -1252,6 +1286,7 @@ filament_cost = 32.4
filament_density = 1.04
first_layer_temperature = 240
temperature = 240
filament_vendor = Fillamentum
[filament:Fillamentum ASA]
inherits = *ABS*
@ -1266,6 +1301,7 @@ slowdown_below_layer_time = 15
first_layer_temperature = 265
temperature = 265
filament_type = ASA
filament_vendor = Fillamentum
[filament:Prusament ASA]
inherits = *ABS*
@ -1296,6 +1332,7 @@ first_layer_temperature = 275
max_fan_speed = 50
min_fan_speed = 50
temperature = 275
filament_vendor = Fillamentum
[filament:Fillamentum Timberfill]
inherits = *PLA*
@ -1309,24 +1346,28 @@ filament_max_volumetric_speed = 10
first_layer_temperature = 190
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 190
filament_vendor = Fillamentum
[filament:Generic ABS]
inherits = *ABS*
filament_cost = 27.82
filament_density = 1.04
filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
filament_vendor = Generic
[filament:Generic PET]
inherits = *PET*
filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG"
filament_vendor = Generic
[filament:Generic PLA]
inherits = *PLA*
filament_cost = 25.4
filament_density = 1.24
filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
filament_vendor = Generic
[filament:Generic FLEX]
inherits = *FLEX*
@ -1347,6 +1388,7 @@ filament_colour = #3A80CA
first_layer_bed_temperature = 100
first_layer_temperature = 270
temperature = 270
filament_vendor = Polymaker
[filament:PrimaSelect PVA+]
inherits = *PLA*
@ -1363,12 +1405,14 @@ filament_type = PVA
first_layer_temperature = 195
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 195
filament_vendor = PrimaSelect
[filament:Prusa ABS]
inherits = *ABS*
filament_cost = 27.82
filament_density = 1.08
filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS"
filament_vendor = Prusa
[filament:*ABS MMU2*]
inherits = Prusa ABS
@ -1385,6 +1429,7 @@ filament_unloading_speed = 20
[filament:Generic ABS MMU2]
inherits = *ABS MMU2*
filament_vendor = Generic
[filament:Prusament ASA MMU2]
inherits = *ABS MMU2*
@ -1410,6 +1455,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el
[filament:Prusa ABS MMU2]
inherits = *ABS MMU2*
filament_vendor = Prusa
[filament:Prusa HIPS]
inherits = *ABS*
@ -1428,6 +1474,7 @@ max_fan_speed = 20
min_fan_speed = 20
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 220
filament_vendor = Prusa
[filament:Prusa PET]
inherits = *PET*
@ -1435,6 +1482,7 @@ filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG"
compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
filament_vendor = Prusa
[filament:Prusament PETG]
inherits = *PET*
@ -1444,12 +1492,14 @@ filament_cost = 24.99
filament_density = 1.27
filament_type = PETG
compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
filament_vendor = Prusa
[filament:Prusa PET 0.6 nozzle]
inherits = *PET06*
filament_cost = 27.82
filament_density = 1.27
filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG"
filament_vendor = Prusa
[filament:Prusament PETG 0.6 nozzle]
inherits = *PET06*
@ -1458,6 +1508,7 @@ temperature = 250
filament_cost = 24.99
filament_density = 1.27
filament_type = PETG
filament_vendor = Prusa
[filament:*PET MMU2*]
inherits = Prusa PET
@ -1485,9 +1536,11 @@ filament_max_volumetric_speed = 13
[filament:Generic PET MMU2]
inherits = *PET MMU2*
filament_vendor = Generic
[filament:Prusa PET MMU2]
inherits = *PET MMU2*
filament_vendor = Prusa
[filament:Prusament PETG MMU2]
inherits = *PET MMU2*
@ -1498,16 +1551,19 @@ inherits = *PET MMU2 06*
[filament:Prusa PET MMU2 0.6 nozzle]
inherits = *PET MMU2 06*
filament_vendor = Prusa
[filament:Prusament PETG MMU2 0.6 nozzle]
inherits = *PET MMU2 06*
filament_type = PETG
filament_vendor = Prusa
[filament:Prusa PLA]
inherits = *PLA*
filament_cost = 25.4
filament_density = 1.24
filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFiberlogy PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nAmazonBasics PLA"
filament_vendor = Prusa
[filament:Prusament PLA]
inherits = *PLA*
@ -1515,6 +1571,7 @@ temperature = 215
filament_cost = 24.99
filament_density = 1.24
filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa"
filament_vendor = Prusa
[filament:*PLA MMU2*]
inherits = Prusa PLA
@ -1534,18 +1591,22 @@ filament_unloading_speed_start = 100
[filament:Generic PLA MMU2]
inherits = *PLA MMU2*
filament_vendor = Generic
[filament:Prusa PLA MMU2]
inherits = *PLA MMU2*
filament_vendor = Prusa
[filament:Prusament PLA MMU2]
inherits = *PLA MMU2*
filament_vendor = Prusa
[filament:SemiFlex or Flexfill 98A]
inherits = *FLEX*
filament_cost = 82
filament_density = 1.22
filament_max_volumetric_speed = 1.35
filament_vendor = Flexfill
[filament:Taulman Bridge]
inherits = *common*
@ -1567,6 +1628,7 @@ max_fan_speed = 5
min_fan_speed = 0
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 250
filament_vendor = Taulman
[filament:Taulman T-Glase]
inherits = *PET*
@ -1580,6 +1642,7 @@ first_layer_temperature = 240
max_fan_speed = 5
min_fan_speed = 0
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode"
filament_vendor = Taulman
[filament:Verbatim BVOH]
inherits = *common*
@ -1603,6 +1666,7 @@ max_fan_speed = 100
min_fan_speed = 100
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 210
filament_vendor = Verbatim
[filament:Verbatim BVOH MMU2]
inherits = Verbatim BVOH
@ -1622,6 +1686,7 @@ filament_unload_time = 12
filament_unloading_speed = 20
filament_unloading_speed_start = 100
filament_loading_speed_start = 19
filament_vendor = Verbatim
[filament:PrimaSelect PVA+ MMU2]
inherits = *common*
@ -1660,6 +1725,7 @@ min_print_speed = 15
slowdown_below_layer_time = 20
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode"
temperature = 195
filament_vendor = PrimaSelect
[filament:Verbatim PP]
inherits = *common*
@ -1682,6 +1748,7 @@ max_fan_speed = 100
min_fan_speed = 100
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 220
filament_vendor = Verbatim
## Filaments MMU1
@ -1899,9 +1966,11 @@ exposure_time = 6
initial_exposure_time = 40
[sla_material:BlueCast Keramaster Dental 0.025]
material_type = Dental
inherits = *common 0.025*
exposure_time = 6
initial_exposure_time = 45
material_vendor = Bluecast
[sla_material:BlueCast X10 0.025]
inherits = *common 0.025*
@ -1912,6 +1981,7 @@ initial_exposure_time = 100
inherits = *common 0.025*
exposure_time = 6
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Grey Tough 0.025]
inherits = *common 0.025*
@ -1964,31 +2034,38 @@ initial_exposure_time = 35
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 35
material_vendor = Bluecast
[sla_material:BlueCast Keramaster 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 45
material_vendor = Bluecast
[sla_material:BlueCast Keramaster Dental 0.05]
material_type = Dental
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 50
material_vendor = Bluecast
[sla_material:BlueCast LCD-DLP Original 0.05]
inherits = *common 0.05*
exposure_time = 10
initial_exposure_time = 60
material_vendor = Bluecast
[sla_material:BlueCast Phrozen Wax 0.05]
inherits = *common 0.05*
exposure_time = 16
initial_exposure_time = 50
material_vendor = Bluecast
[sla_material:BlueCast S+ 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 45
material_vendor = Bluecast
[sla_material:BlueCast X10 0.05]
inherits = *common 0.05*
@ -1999,26 +2076,31 @@ initial_exposure_time = 100
inherits = *common 0.05*
exposure_time = 6
initial_exposure_time = 40
material_vendor = Monocure
[sla_material:Monocure 3D Blue Rapid Resin 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
material_vendor = Monocure
[sla_material:Monocure 3D Clear Rapid Resin 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 40
material_vendor = Monocure
[sla_material:Monocure 3D Grey Rapid Resin 0.05]
inherits = *common 0.05*
exposure_time = 10
initial_exposure_time = 30
material_vendor = Monocure
[sla_material:Monocure 3D White Rapid Resin 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 40
material_vendor = Monocure
[sla_material:3DM-HTR140 (high temperature) 0.05]
inherits = *common 0.05*
@ -2034,36 +2116,43 @@ initial_exposure_time = 25
inherits = *common 0.05*
exposure_time = 20
initial_exposure_time = 40
material_vendor = 3DM
[sla_material:3DM-DENT 0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 45
material_vendor = 3DM
[sla_material:3DM-HR Green 0.05]
inherits = *common 0.05*
exposure_time = 15
initial_exposure_time = 40
material_vendor = 3DM
[sla_material:3DM-HR Red Wine 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 35
material_vendor = 3DM
[sla_material:3DM-XPRO White 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 35
material_vendor = 3DM
[sla_material:FTD Ash Grey 0.05]
inherits = *common 0.05*
exposure_time = 9
initial_exposure_time = 40
material_vendor = FTD
[sla_material:Harz Labs Model Resin Cherry 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 45
material_vendor = Harz Labs
[sla_material:Photocentric Hard Grey 0.05]
inherits = *common 0.05*
@ -2116,6 +2205,7 @@ initial_exposure_time = 35
inherits = *common 0.05*
exposure_time = 13
initial_exposure_time = 40
material_vendor = Prusa
## [sla_material:Prusa Yellow Solid 0.05]
## inherits = *common 0.05*
@ -2126,6 +2216,7 @@ initial_exposure_time = 40
inherits = *common 0.05*
exposure_time = 7.5
initial_exposure_time = 35
material_vendor = Prusa
## [sla_material:Prusa Transparent Green Tough 0.05]
## inherits = *common 0.05*
@ -2136,21 +2227,25 @@ initial_exposure_time = 35
inherits = *common 0.05*
exposure_time = 6
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Maroon Tough 0.05]
inherits = *common 0.05*
exposure_time = 7.5
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Pink Tough 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Azure Blue Tough 0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Transparent Tough 0.05]
inherits = *common 0.05*
@ -2193,6 +2288,7 @@ initial_exposure_time = 15
inherits = *common 0.035*
exposure_time = 6
initial_exposure_time = 35
material_vendor = Prusa
########### Materials 0.1
@ -2235,6 +2331,7 @@ initial_exposure_time = 55
inherits = *common 0.1*
exposure_time = 8
initial_exposure_time = 35
material_vendor = Prusa
[sla_material:Prusa Green Casting 0.1]
inherits = *common 0.1*

View File

@ -49,91 +49,84 @@ using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
extern template class Nester<NfpPlacer, FirstFitSelection>;
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
extern template PackGroup Nester<NfpPlacer, FirstFitSelection>::execute(
extern template std::size_t Nester<NfpPlacer, FirstFitSelection>::execute(
std::vector<Item>::iterator, std::vector<Item>::iterator);
extern template PackGroup Nester<BottomLeftPlacer, FirstFitSelection>::execute(
extern template std::size_t Nester<BottomLeftPlacer, FirstFitSelection>::execute(
std::vector<Item>::iterator, std::vector<Item>::iterator);
#endif
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Iterator = std::vector<Item>::iterator>
void nest(Iterator from, Iterator to,
const typename Placer::BinType& bin,
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
{
_Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
nester.execute(from, to);
}
template<class Placer = NfpPlacer, class Selector = FirstFitSelection>
struct NestConfig {
typename Placer::Config placer_config;
typename Selector::Config selector_config;
using Placement = typename Placer::Config;
using Selection = typename Selector::Config;
NestConfig() = default;
NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {}
NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {}
NestConfig(const typename Placer::Config & pcfg,
const typename Selector::Config &scfg)
: placer_config{pcfg}, selector_config{scfg} {}
};
struct NestControl {
ProgressFunction progressfn;
StopCondition stopcond = []{ return false; };
NestControl() = default;
NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {}
NestControl(StopCondition sc) : stopcond{std::move(sc)} {}
NestControl(ProgressFunction pr, StopCondition sc)
: progressfn{std::move(pr)}, stopcond{std::move(sc)}
{}
};
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Iterator = std::vector<Item>::iterator>
void nest(Iterator from, Iterator to,
const typename Placer::BinType& bin,
ProgressFunction prg,
StopCondition scond = []() { return false; },
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
std::size_t nest(Iterator from, Iterator to,
const typename Placer::BinType & bin,
Coord dist = 0,
const NestConfig<Placer, Selector> &cfg = {},
NestControl ctl = {})
{
_Nester<Placer, Selector> nester(bin, dist, pconf, sconf);
if(prg) nester.progressIndicator(prg);
if(scond) nester.stopCondition(scond);
nester.execute(from, to);
_Nester<Placer, Selector> nester{bin, dist, cfg.placer_config, cfg.selector_config};
if(ctl.progressfn) nester.progressIndicator(ctl.progressfn);
if(ctl.stopcond) nester.stopCondition(ctl.stopcond);
return nester.execute(from, to);
}
#ifdef LIBNEST2D_STATIC
extern template class Nester<NfpPlacer, FirstFitSelection>;
extern template class Nester<BottomLeftPlacer, FirstFitSelection>;
extern template void nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
const Box& bin,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
extern template void nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
const Box& bin,
ProgressFunction prg,
StopCondition scond,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
extern template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist,
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
NestControl ctl);
extern template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist,
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
NestControl ctl);
#endif
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Container = std::vector<Item>>
void nest(Container&& cont,
const typename Placer::BinType& bin,
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
std::size_t nest(Container&& cont,
const typename Placer::BinType & bin,
Coord dist = 0,
const NestConfig<Placer, Selector> &cfg = {},
NestControl ctl = {})
{
nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, pconf, sconf);
}
template<class Placer = NfpPlacer,
class Selector = FirstFitSelection,
class Container = std::vector<Item>>
void nest(Container&& cont,
const typename Placer::BinType& bin,
ProgressFunction prg,
StopCondition scond = []() { return false; },
Coord dist = 0,
const typename Placer::Config& pconf = {},
const typename Selector::Config& sconf = {})
{
nest<Placer, Selector>(cont.begin(), cont.end(), bin, prg, scond, dist,
pconf, sconf);
return nest<Placer, Selector>(cont.begin(), cont.end(), bin, dist, cfg, ctl);
}
}

View File

@ -129,8 +129,12 @@ public:
sh_(sl::create<RawShape>(std::move(contour), std::move(holes))) {}
inline bool isFixed() const noexcept { return fixed_; }
inline void markAsFixed(bool fixed = true) { fixed_ = fixed; }
inline void markAsFixedInBin(int binid)
{
fixed_ = binid >= 0;
binid_ = binid;
}
inline void binId(int idx) { binid_ = idx; }
inline int binId() const noexcept { return binid_; }
@ -748,6 +752,7 @@ template<class PlacementStrategy, class SelectionStrategy >
class _Nester {
using TSel = SelectionStrategyLike<SelectionStrategy>;
TSel selector_;
public:
using Item = typename PlacementStrategy::Item;
using ShapeType = typename Item::ShapeType;
@ -824,7 +829,7 @@ public:
* the selection algorithm.
*/
template<class It>
inline ItemIteratorOnly<It, void> execute(It from, It to)
inline ItemIteratorOnly<It, size_t> execute(It from, It to)
{
auto infl = static_cast<Coord>(std::ceil(min_obj_distance_/2.0));
if(infl > 0) std::for_each(from, to, [this, infl](Item& item) {
@ -837,6 +842,8 @@ public:
if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) {
item.inflate(-infl);
});
return selector_.getResult().size();
}
/// Set a progress indicator function object for the selector.

View File

@ -1122,8 +1122,6 @@ private:
sl::rotate(sh, item.rotation());
Box bb = sl::boundingBox(sh);
bb.minCorner() += item.translation();
bb.maxCorner() += item.translation();
Vertex ci, cb;
auto bbin = sl::boundingBox(bin_);

View File

@ -5,19 +5,17 @@ namespace libnest2d {
template class Nester<NfpPlacer, FirstFitSelection>;
template class Nester<BottomLeftPlacer, FirstFitSelection>;
template PackGroup nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
const Box& bin,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist,
const NestConfig<NfpPlacer, FirstFitSelection> &cfg,
NestControl ctl);
template PackGroup nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator to,
const Box& bin,
ProgressFunction prg,
StopCondition scond,
Coord dist = 0,
const NfpPlacer::Config& pconf,
const FirstFitSelection::Config& sconf);
template std::size_t nest(std::vector<Item>::iterator from,
std::vector<Item>::iterator from to,
const Box & bin,
Coord dist,
const NestConfig<BottomLeftPlacer, FirstFitSelection> &cfg,
NestControl ctl);
}

View File

@ -375,7 +375,7 @@ public:
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
Item& itm = fixeditems[idx];
itm.markAsFixed();
itm.markAsFixedInBin(0);
}
m_pck.configure(m_pconf);

View File

@ -31,7 +31,8 @@ namespace pt = boost::property_tree;
// VERSION NUMBERS
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
const unsigned int VERSION_3MF = 1;
// 2 : Meshes saved in their local system; Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file.
const unsigned int VERSION_3MF = 2;
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
const std::string MODEL_FOLDER = "3D/";
@ -87,6 +88,13 @@ const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
const char* VOLUME_TYPE_KEY = "volume_type";
const char* MATRIX_KEY = "matrix";
const char* SOURCE_FILE_KEY = "source_file";
const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@ -148,11 +156,15 @@ bool get_attribute_value_bool(const char** attributes, unsigned int attributes_s
return (text != nullptr) ? (bool)::atoi(text) : true;
}
Slic3r::Transform3d get_transform_from_string(const std::string& mat_str)
Slic3r::Transform3d get_transform_from_3mf_specs_string(const std::string& mat_str)
{
// check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md
// to see how matrices are stored inside 3mf according to specifications
Slic3r::Transform3d ret = Slic3r::Transform3d::Identity();
if (mat_str.empty())
// empty string means default identity matrix
return Slic3r::Transform3d::Identity();
return ret;
std::vector<std::string> mat_elements_str;
boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on);
@ -160,9 +172,8 @@ Slic3r::Transform3d get_transform_from_string(const std::string& mat_str)
unsigned int size = (unsigned int)mat_elements_str.size();
if (size != 12)
// invalid data, return identity matrix
return Slic3r::Transform3d::Identity();
return ret;
Slic3r::Transform3d ret = Slic3r::Transform3d::Identity();
unsigned int i = 0;
// matrices are stored into 3mf files as 4x3
// we need to transpose them
@ -1375,7 +1386,7 @@ namespace Slic3r {
bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
{
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
if (object_item == m_objects.end())
@ -1421,7 +1432,7 @@ namespace Slic3r {
// see specifications
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
return _create_object_instance(object_id, transform, printable, 1);
@ -1634,6 +1645,21 @@ namespace Slic3r {
return false;
}
Slic3r::Geometry::Transformation transform;
if (m_version > 1)
{
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata)
{
if (metadata.key == MATRIX_KEY)
{
transform.set_from_string(metadata.value);
break;
}
}
}
Transform3d inv_matrix = transform.get_matrix().inverse();
// splits volume out of imported geometry
TriangleMesh triangle_mesh;
stl_file &stl = triangle_mesh.stl;
@ -1651,7 +1677,12 @@ namespace Slic3r {
stl_facet& facet = stl.facet_start[i];
for (unsigned int v = 0; v < 3; ++v)
{
::memcpy(facet.vertex[v].data(), (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float));
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
if (m_version > 1)
// revert the vertices to the original mesh reference system
vertex = (inv_matrix * vertex.cast<double>()).cast<float>();
::memcpy(facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float));
}
}
@ -1659,10 +1690,12 @@ namespace Slic3r {
triangle_mesh.repair();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
volume->center_geometry_after_creation();
// apply the volume matrix taken from the metadata, if present
if (m_version > 1)
volume->set_transformation(transform);
volume->calculate_convex_hull();
// apply volume's name and config data
// apply the remaining volume's metadata
for (const Metadata& metadata : volume_data.metadata)
{
if (metadata.key == NAME_KEY)
@ -1671,6 +1704,18 @@ namespace Slic3r {
volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
else if (metadata.key == VOLUME_TYPE_KEY)
volume->set_type(ModelVolume::type_from_string(metadata.value));
else if (metadata.key == SOURCE_FILE_KEY)
volume->source.input_file = metadata.value;
else if (metadata.key == SOURCE_OBJECT_ID_KEY)
volume->source.object_idx = ::atoi(metadata.value.c_str());
else if (metadata.key == SOURCE_VOLUME_ID_KEY)
volume->source.volume_idx = ::atoi(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_X_KEY)
volume->source.mesh_offset(0) = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_Y_KEY)
volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_Z_KEY)
volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@ -2116,7 +2161,7 @@ namespace Slic3r {
for (const BuildItem& item : build_items)
{
stream << " <" << ITEM_TAG << " objectid=\"" << item.id << "\" transform =\"";
stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c)
{
for (unsigned r = 0; r < 3; ++r)
@ -2126,7 +2171,7 @@ namespace Slic3r {
stream << " ";
}
}
stream << "\" printable =\"" << item.printable << "\" />\n";
stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\" />\n";
}
stream << " </" << BUILD_TAG << ">\n";
@ -2344,6 +2389,31 @@ namespace Slic3r {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
const Transform3d& matrix = volume->get_matrix();
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
{
stream << matrix(r, c);
if ((r != 3) || (c != 3))
stream << " ";
}
}
stream << "\"/>\n";
// stores volume's source data
if (!volume->source.input_file.empty())
{
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
}
// stores volume's config data
for (const std::string& key : volume->config.keys())
{

View File

@ -12,6 +12,7 @@
#include "../PrintConfig.hpp"
#include "../Utils.hpp"
#include "../I18N.hpp"
#include "../Geometry.hpp"
#include "AMF.hpp"
@ -36,7 +37,8 @@
// Added x and y components of rotation
// Added x, y and z components of scale
// Added x, y and z components of mirror
const unsigned int VERSION_AMF = 2;
// 3 : Meshes saved in their local system; Added volumes' matrices and source data
const unsigned int VERSION_AMF = 3;
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
@ -560,15 +562,30 @@ void AMFParserContext::endElement(const char * /* name */)
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
Slic3r::Geometry::Transformation transform;
if (m_version > 2)
transform = m_volume->get_transformation();
Transform3d inv_matrix = transform.get_matrix().inverse();
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++ v)
memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = m_volume_facets[i++] * 3;
Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
if (m_version > 2)
// revert the vertices to the original mesh reference system
vertex = (inv_matrix * vertex.cast<double>()).cast<float>();
::memcpy((void*)facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float));
}
}
stl_get_size(&stl);
mesh.repair();
m_volume->set_mesh(std::move(mesh));
m_volume->center_geometry_after_creation();
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_volume = nullptr;
@ -664,6 +681,29 @@ void AMFParserContext::endElement(const char * /* name */)
} else if (strcmp(opt_key, "volume_type") == 0) {
m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
}
else if (strcmp(opt_key, "matrix") == 0) {
Geometry::Transformation transform;
transform.set_from_string(m_value[1]);
m_volume->set_transformation(transform);
}
else if (strcmp(opt_key, "source_file") == 0) {
m_volume->source.input_file = m_value[1];
}
else if (strcmp(opt_key, "source_object_id") == 0) {
m_volume->source.object_idx = ::atoi(m_value[1].c_str());
}
else if (strcmp(opt_key, "source_volume_id") == 0) {
m_volume->source.volume_idx = ::atoi(m_value[1].c_str());
}
else if (strcmp(opt_key, "source_offset_x") == 0) {
m_volume->source.mesh_offset(0) = ::atof(m_value[1].c_str());
}
else if (strcmp(opt_key, "source_offset_y") == 0) {
m_volume->source.mesh_offset(1) = ::atof(m_value[1].c_str());
}
else if (strcmp(opt_key, "source_offset_z") == 0) {
m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str());
}
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@ -1057,7 +1097,28 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
const indexed_triangle_set &its = volume->mesh().its;
stream << " <metadata type=\"slic3r.matrix\">";
const Transform3d& matrix = volume->get_matrix();
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
{
stream << matrix(r, c);
if ((r != 3) || (c != 3))
stream << " ";
}
}
stream << "</metadata>\n";
if (!volume->source.input_file.empty())
{
stream << " <metadata type=\"slic3r.source_file\">" << xml_escape(volume->source.input_file) << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_y\">" << volume->source.mesh_offset(1) << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_z\">" << volume->source.mesh_offset(2) << "</metadata>\n";
}
const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < its.indices.size(); ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)

View File

@ -138,7 +138,7 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
// We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo =
Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value);
Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value));
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {

View File

@ -787,8 +787,10 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
// Extrude 4 rounds of a brim around the future wipe tower.
box_coordinates box(wipeTower_box);
// the brim shall have 'normal' spacing with no extra void space
float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
for (size_t i = 0; i < 4; ++ i) {
box.expand(m_perimeter_width - m_layer_height*float(1.-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
box.expand(spacing);
writer.travel (box.ld, 7000)
.extrude(box.lu, 2100).extrude(box.ru)
.extrude(box.rd ).extrude(box.ld);
@ -800,6 +802,10 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
";-----------------------------------\n");
// Save actual brim width to be later passed to the Print object, which will use it
// for skirt calculation and pass it to GLCanvas for precise preview box
m_wipe_tower_brim_width = wipeTower_box.ld.x() - box.ld.x() + spacing/2.f;
m_print_brim = false; // Mark the brim as extruded
// Ask our writer about how much material was consumed:

View File

@ -92,6 +92,7 @@ public:
void generate(std::vector<std::vector<ToolChangeResult>> &result);
float get_depth() const { return m_wipe_tower_depth; }
float get_brim_width() const { return m_wipe_tower_brim_width; }
@ -203,6 +204,7 @@ private:
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_brim_width = 0.f; // Width of brim (mm)
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer

View File

@ -14,6 +14,9 @@
#include <stack>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#ifdef SLIC3R_DEBUG
#include "SVG.hpp"
#endif
@ -1329,6 +1332,32 @@ void Transformation::set_from_transform(const Transform3d& transform)
// std::cout << "something went wrong in extracting data from matrix" << std::endl;
}
void Transformation::set_from_string(const std::string& transform_str)
{
Transform3d transform = Transform3d::Identity();
if (!transform_str.empty())
{
std::vector<std::string> mat_elements_str;
boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on);
unsigned int size = (unsigned int)mat_elements_str.size();
if (size == 16)
{
unsigned int i = 0;
for (unsigned int r = 0; r < 4; ++r)
{
for (unsigned int c = 0; c < 4; ++c)
{
transform(r, c) = ::atof(mat_elements_str[i++].c_str());
}
}
}
}
set_from_transform(transform);
}
void Transformation::reset()
{
m_offset = Vec3d::Zero();

View File

@ -280,6 +280,7 @@ public:
void set_mirror(Axis axis, double mirror);
void set_from_transform(const Transform3d& transform);
void set_from_string(const std::string& transform_str);
void reset();

View File

@ -141,12 +141,12 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
for (ModelObject *o : model.objects)
{
if (boost::algorithm::iends_with(input_file, ".zip.amf"))
{
// we remove the .zip part of the extension to avoid it be added to filenames when exporting
o->input_file = boost::ireplace_last_copy(input_file, ".zip.", ".");
}
else
// if (boost::algorithm::iends_with(input_file, ".zip.amf"))
// {
// // we remove the .zip part of the extension to avoid it be added to filenames when exporting
// o->input_file = boost::ireplace_last_copy(input_file, ".zip.", ".");
// }
// else
o->input_file = input_file;
}
@ -170,6 +170,9 @@ ModelObject* Model::add_object(const char *name, const char *path, const Triangl
new_object->input_file = path;
ModelVolume *new_volume = new_object->add_volume(mesh);
new_volume->name = name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)this->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
new_object->invalidate_bounding_box();
return new_object;
}
@ -182,6 +185,9 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh
new_object->input_file = path;
ModelVolume *new_volume = new_object->add_volume(std::move(mesh));
new_volume->name = name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)this->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
new_object->invalidate_bounding_box();
return new_object;
}
@ -1543,7 +1549,7 @@ bool ModelVolume::is_splittable() const
return m_is_splittable == 1;
}
void ModelVolume::center_geometry_after_creation()
void ModelVolume::center_geometry_after_creation(bool update_source_offset)
{
Vec3d shift = this->mesh().bounding_box().center();
if (!shift.isApprox(Vec3d::Zero()))
@ -1554,6 +1560,9 @@ void ModelVolume::center_geometry_after_creation()
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
}
if (update_source_offset)
source.mesh_offset = shift;
}
void ModelVolume::calculate_convex_hull()

View File

@ -392,6 +392,18 @@ class ModelVolume final : public ObjectBase
{
public:
std::string name;
// struct used by reload from disk command to recover data from disk
struct Source
{
std::string input_file;
int object_idx{ -1 };
int volume_idx{ -1 };
Vec3d mesh_offset{ Vec3d::Zero() };
template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); }
};
Source source;
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
@ -440,7 +452,7 @@ public:
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
void center_geometry_after_creation();
void center_geometry_after_creation(bool update_source_offset = true);
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
@ -529,7 +541,7 @@ private:
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ObjectBase(other),
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == other.id() && this->config.id() == other.config.id());
@ -537,7 +549,7 @@ private:
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
name(other.name), source(other.source), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() != other.id() && this->config.id() == other.config.id());
@ -558,8 +570,8 @@ private:
}
template<class Archive> void load(Archive &ar) {
bool has_convex_hull;
ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
cereal::load_by_value(ar, config);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
cereal::load_by_value(ar, config);
assert(m_mesh);
if (has_convex_hull) {
cereal::load_optional(ar, m_convex_hull);
@ -571,8 +583,8 @@ private:
}
template<class Archive> void save(Archive &ar) const {
bool has_convex_hull = m_convex_hull.get() != nullptr;
ar(name, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
cereal::save_by_value(ar, config);
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
cereal::save_by_value(ar, config);
if (has_convex_hull)
cereal::save_optional(ar, m_convex_hull);
}

View File

@ -143,10 +143,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"use_relative_e_distances",
"use_volumetric_e",
"variable_layer_height",
"wipe",
"wipe_tower_x",
"wipe_tower_y",
"wipe_tower_rotation_angle"
"wipe"
};
static std::unordered_set<std::string> steps_ignore;
@ -167,7 +164,10 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "skirt_height"
|| opt_key == "skirt_distance"
|| opt_key == "min_skirt_length"
|| opt_key == "ooze_prevention") {
|| opt_key == "ooze_prevention"
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_rotation_angle") {
steps.emplace_back(psSkirt);
} else if (opt_key == "brim_width") {
steps.emplace_back(psBrim);
@ -208,6 +208,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "extra_loading_move"
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
steps.emplace_back(psSkirt);
} else if (
opt_key == "first_layer_extrusion_width"
|| opt_key == "min_layer_height"
@ -1186,6 +1187,8 @@ std::string Print::validate() const
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
if (m_config.ooze_prevention)
return L("Ooze prevention is currently not supported with the wipe tower enabled.");
if (m_config.use_volumetric_e)
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
if (m_objects.size() > 1) {
bool has_custom_layering = false;
@ -1502,6 +1505,14 @@ void Print::process()
obj->infill();
for (PrintObject *obj : m_objects)
obj->generate_support_material();
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
if (this->has_wipe_tower()) {
//this->set_status(95, L("Generating wipe tower"));
this->_make_wipe_tower();
}
this->set_done(psWipeTower);
}
if (this->set_started(psSkirt)) {
m_skirt.clear();
if (this->has_skirt()) {
@ -1518,14 +1529,6 @@ void Print::process()
}
this->set_done(psBrim);
}
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
if (this->has_wipe_tower()) {
//this->set_status(95, L("Generating wipe tower"));
this->_make_wipe_tower();
}
this->set_done(psWipeTower);
}
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
}
@ -1602,6 +1605,17 @@ void Print::_make_skirt()
}
}
// Include the wipe tower.
if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) {
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;
double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width;
Vec2d pt = Vec2d(m_config.wipe_tower_x-m_wipe_tower_data.brim_width, m_config.wipe_tower_y-m_wipe_tower_data.brim_width);
points.push_back(Point(scale_(pt.x()), scale_(pt.y())));
points.push_back(Point(scale_(pt.x()+width), scale_(pt.y())));
points.push_back(Point(scale_(pt.x()+width), scale_(pt.y()+depth)));
points.push_back(Point(scale_(pt.x()), scale_(pt.y()+depth)));
}
if (points.size() < 3)
// At least three points required for a convex hull.
return;
@ -1864,6 +1878,22 @@ bool Print::has_wipe_tower() const
m_config.nozzle_diameter.values.size() > 1;
}
const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_layer_height, double nozzle_diameter) const
{
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
if (! is_step_done(psWipeTower) && extruders_cnt !=0) {
float width = m_config.wipe_tower_width;
float brim_spacing = nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4);
const_cast<Print*>(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1);
const_cast<Print*>(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing;
}
return m_wipe_tower_data;
}
void Print::_make_wipe_tower()
{
m_wipe_tower_data.clear();
@ -1972,6 +2002,7 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
wipe_tower.generate(m_wipe_tower_data.tool_changes);
m_wipe_tower_data.depth = wipe_tower.get_depth();
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
// Unload the current filament over the purge tower.
coordf_t layer_height = m_objects.front()->config().layer_height.value;

View File

@ -226,6 +226,7 @@ struct WipeTowerData
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth;
float brim_width;
void clear() {
tool_ordering.clear();
@ -235,6 +236,7 @@ struct WipeTowerData
used_filament.clear();
number_of_toolchanges = -1;
depth = 0.f;
brim_width = 0.f;
}
};
@ -314,7 +316,6 @@ public:
bool has_infinite_skirt() const;
bool has_skirt() const;
float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
// Returns an empty string if valid, otherwise returns an error message.
std::string validate() const override;
@ -353,7 +354,7 @@ public:
// Wipe tower support.
bool has_wipe_tower() const;
const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; }
const WipeTowerData& wipe_tower_data(size_t extruders_cnt = 0, double first_layer_height = 0., double nozzle_diameter = 0.) const;
std::string output_filename(const std::string &filename_base = std::string()) const override;

View File

@ -749,6 +749,10 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionStrings { "" });
def->cli = ConfigOptionDef::nocli;
def = this->add("filament_vendor", coString);
def->set_default_value(new ConfigOptionString(L("(Unknown)")));
def->cli = ConfigOptionDef::nocli;
def = this->add("fill_angle", coFloat);
def->label = L("Fill angle");
def->category = L("Infill");
@ -2398,6 +2402,18 @@ void PrintConfigDef::init_sla_params()
// SLA Material settings.
def = this->add("material_type", coString);
def->label = L("SLA material type");
def->tooltip = L("SLA material type");
def->gui_type = "f_enum_open"; // TODO: ???
def->gui_flags = "show_value";
def->enum_values.push_back("Tough");
def->enum_values.push_back("Flexible");
def->enum_values.push_back("Casting");
def->enum_values.push_back("Dental");
def->enum_values.push_back("Heat-resistant");
def->set_default_value(new ConfigOptionString("Tough"));
def = this->add("initial_layer_height", coFloat);
def->label = L("Initial layer height");
def->tooltip = L("Initial layer height");
@ -2475,6 +2491,10 @@ void PrintConfigDef::init_sla_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("material_vendor", coString);
def->set_default_value(new ConfigOptionString(L("(Unknown)")));
def->cli = ConfigOptionDef::nocli;
def = this->add("default_sla_material_profile", coString);
def->label = L("Default SLA material profile");
def->tooltip = L("Default print profile associated with the current printer profile. "

View File

@ -52,6 +52,14 @@ enum FilamentType {
};
*/
enum SLAMaterial {
slamTough,
slamFlex,
slamCasting,
slamDental,
slamHeatResistant,
};
enum SLADisplayOrientation {
sladoLandscape,
sladoPortrait

View File

@ -393,9 +393,9 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
// Read the active config bundle, parse the config version.
PresetBundle bundle;
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
for (const VendorProfile &vp : bundle.vendors)
if (vp.id == cfg.name)
cfg.version.config_version = vp.config_version;
for (const auto &vp : bundle.vendors)
if (vp.second.id == cfg.name)
cfg.version.config_version = vp.second.config_version;
// Fill-in the min/max slic3r version from the config index, if possible.
try {
// Load the config index for the vendor.

View File

@ -28,6 +28,9 @@ static const std::string VENDOR_PREFIX = "vendor:";
static const std::string MODEL_PREFIX = "model:";
static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version";
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
const std::string AppConfig::SECTION_MATERIALS = "sla_materials";
void AppConfig::reset()
{
m_storage.clear();

View File

@ -80,6 +80,12 @@ public:
}
}
bool has_section(const std::string &section) const
{ return m_storage.find(section) != m_storage.end(); }
const std::map<std::string, std::string>& get_section(const std::string &section) const
{ return m_storage.find(section)->second; }
void set_section(const std::string &section, const std::map<std::string, std::string>& data)
{ m_storage[section] = data; }
void clear_section(const std::string &section)
{ m_storage[section].clear(); }
@ -125,6 +131,8 @@ public:
std::vector<std::string> get_recent_projects() const;
void set_recent_projects(const std::vector<std::string>& recent_projects);
static const std::string SECTION_FILAMENTS;
static const std::string SECTION_MATERIALS;
private:
// Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage;

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,15 @@ public:
RR_USER, // User requested the Wizard from the menus
};
ConfigWizard(wxWindow *parent, RunReason run_reason);
// What page should wizard start on
enum StartPage {
SP_WELCOME,
SP_PRINTERS,
SP_FILAMENTS,
SP_MATERIALS,
};
ConfigWizard(wxWindow *parent);
ConfigWizard(ConfigWizard &&) = delete;
ConfigWizard(const ConfigWizard &) = delete;
ConfigWizard &operator=(ConfigWizard &&) = delete;
@ -34,7 +42,7 @@ public:
~ConfigWizard();
// Run the Wizard. Return whether it was completed.
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
bool run(RunReason reason, StartPage start_page = SP_WELCOME);
static const wxString& name(const bool from_menu = false);

View File

@ -15,11 +15,14 @@
#include <wx/choice.h>
#include <wx/spinctrl.h>
#include <wx/textctrl.h>
#include <wx/listbox.h>
#include <wx/checklst.h>
#include <wx/radiobut.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
#include "AppConfig.hpp"
#include "Preset.hpp"
#include "PresetBundle.hpp"
#include "BedShapeDialog.hpp"
namespace fs = boost::filesystem;
@ -41,6 +44,76 @@ enum {
ROW_SPACING = 75,
};
// Configuration data structures extensions needed for the wizard
enum Technology {
// Bitflag equivalent of PrinterTechnology
T_FFF = 0x1,
T_SLA = 0x2,
T_ANY = ~0,
};
struct Materials
{
Technology technology;
std::set<const Preset*> presets;
std::set<std::string> types;
Materials(Technology technology) : technology(technology) {}
void push(const Preset *preset);
void clear();
bool containts(const Preset *preset) {
return presets.find(preset) != presets.end();
}
const std::string& appconfig_section() const;
const std::string& get_type(const Preset *preset) const;
const std::string& get_vendor(const Preset *preset) const;
template<class F> void filter_presets(const std::string &type, const std::string &vendor, F cb) {
for (const Preset *preset : presets) {
if ((type.empty() || get_type(preset) == type) && (vendor.empty() || get_vendor(preset) == vendor)) {
cb(preset);
}
}
}
static const std::string UNKNOWN;
static const std::string& get_filament_type(const Preset *preset);
static const std::string& get_filament_vendor(const Preset *preset);
static const std::string& get_material_type(const Preset *preset);
static const std::string& get_material_vendor(const Preset *preset);
};
struct Bundle
{
std::unique_ptr<PresetBundle> preset_bundle;
VendorProfile *vendor_profile;
const bool is_in_resources;
const bool is_prusa_bundle;
Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false);
Bundle(Bundle &&other);
const std::string& vendor_id() const { return vendor_profile->id; }
};
struct BundleMap: std::unordered_map<std::string /* = vendor ID */, Bundle>
{
static BundleMap load();
Bundle& prusa_bundle();
const Bundle& prusa_bundle() const;
};
struct PrinterPickerEvent;
// GUI elements
typedef std::function<bool(const VendorProfile::PrinterModel&)> ModelFilter;
struct PrinterPicker: wxPanel
@ -61,19 +134,22 @@ struct PrinterPicker: wxPanel
std::vector<Checkbox*> cboxes;
std::vector<Checkbox*> cboxes_alt;
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter);
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors);
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter);
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig);
void select_all(bool select, bool alternates = false);
void select_one(size_t i, bool select);
void on_checkbox(const Checkbox *cbox, bool checked);
bool any_selected() const;
int get_width() const { return width; }
const std::vector<int>& get_button_indexes() { return m_button_indexes; }
static const std::string PRINTER_PLACEHOLDER;
private:
int width;
std::vector<int> m_button_indexes;
void on_checkbox(const Checkbox *cbox, bool checked);
};
struct ConfigWizardPage: wxPanel
@ -87,43 +163,107 @@ struct ConfigWizardPage: wxPanel
virtual ~ConfigWizardPage();
template<class T>
void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
T* append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
{
content->Add(thing, proportion, flag, border);
return thing;
}
void append_text(wxString text);
wxStaticText* append_text(wxString text);
void append_spacer(int space);
ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
virtual void apply_custom_config(DynamicPrintConfig &config) {}
virtual void set_run_reason(ConfigWizard::RunReason run_reason) {}
virtual void on_activate() {}
};
struct PageWelcome: ConfigWizardPage
{
wxStaticText *welcome_text;
wxCheckBox *cbox_reset;
PageWelcome(ConfigWizard *parent);
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
};
struct PagePrinters: ConfigWizardPage
{
enum Technology {
// Bitflag equivalent of PrinterTechnology
T_FFF = 0x1,
T_SLA = 0x2,
T_Any = ~0,
};
std::vector<PrinterPicker *> printer_pickers;
Technology technology;
bool install;
PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology);
PagePrinters(ConfigWizard *parent,
wxString title,
wxString shortname,
const VendorProfile &vendor,
unsigned indent, Technology technology);
void select_all(bool select, bool alternates = false);
int get_width() const;
bool any_selected() const;
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
};
// Here we extend wxListBox and wxCheckListBox
// to make the client data API much easier to use.
template<class T, class D> struct DataList : public T
{
DataList(wxWindow *parent) : T(parent, wxID_ANY) {}
// Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing,
// eg. "ABS" is sorted before "(All)"
int append(const std::string &label, const D *data) {
void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
return this->Append(from_u8(label), ptr);
}
int append(const wxString &label, const D *data) {
void *ptr = reinterpret_cast<void*>(const_cast<D*>(data));
return this->Append(label, ptr);
}
const D& get_data(int n) {
return *reinterpret_cast<const D*>(this->GetClientData(n));
}
int find(const D &data) {
for (unsigned i = 0; i < this->GetCount(); i++) {
if (get_data(i) == data) { return i; }
}
return wxNOT_FOUND;
}
};
typedef DataList<wxListBox, std::string> StringList;
typedef DataList<wxCheckListBox, Preset> PresetList;
struct PageMaterials: ConfigWizardPage
{
Materials *materials;
StringList *list_l1, *list_l2;
PresetList *list_l3;
int sel1_prev, sel2_prev;
bool presets_loaded;
static const std::string EMPTY;
PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name);
void reload_presets();
void update_lists(int sel1, int sel2);
void select_material(int i);
void select_all(bool select);
void clear();
virtual void on_activate() override;
};
struct PageCustom: ConfigWizardPage
@ -150,13 +290,22 @@ struct PageUpdate: ConfigWizardPage
PageUpdate(ConfigWizard *parent);
};
struct PageMode: ConfigWizardPage
{
wxRadioButton *radio_simple;
wxRadioButton *radio_advanced;
wxRadioButton *radio_expert;
PageMode(ConfigWizard *parent);
void serialize_mode(AppConfig *app_config) const;
virtual void on_activate();
};
struct PageVendors: ConfigWizardPage
{
std::vector<PrinterPicker*> pickers;
PageVendors(ConfigWizard *parent);
void on_vendor_pick(size_t i);
};
struct PageFirmware: ConfigWizardPage
@ -194,6 +343,8 @@ struct PageTemperatures: ConfigWizardPage
virtual void apply_custom_config(DynamicPrintConfig &config);
};
typedef std::map<std::string /* = vendor ID */, PagePrinters*> Pages3rdparty;
class ConfigWizardIndex: public wxPanel
{
@ -210,12 +361,14 @@ public:
void go_prev();
void go_next();
void go_to(size_t i);
void go_to(ConfigWizardPage *page);
void go_to(const ConfigWizardPage *page);
void clear();
void msw_rescale();
int em() const { return em_w; }
static const size_t NO_ITEM = size_t(-1);
private:
struct Item
{
@ -228,12 +381,6 @@ private:
int em_w;
int em_h;
/* #ys_FIXME_delete_after_testing by VK
const wxBitmap bg;
const wxBitmap bullet_black;
const wxBitmap bullet_blue;
const wxBitmap bullet_white;
*/
ScalableBitmap bg;
ScalableBitmap bullet_black;
ScalableBitmap bullet_blue;
@ -245,9 +392,6 @@ private:
ssize_t item_hover;
size_t last_page;
/* #ys_FIXME_delete_after_testing by VK
int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em_w) + em_w; }
*/
int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; }
void on_paint(wxPaintEvent &evt);
@ -256,14 +400,24 @@ private:
wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent);
// ConfigWizard private data
struct ConfigWizard::priv
{
ConfigWizard *q;
ConfigWizard::RunReason run_reason;
AppConfig appconfig_vendors;
std::unordered_map<std::string, VendorProfile> vendors;
std::unordered_map<std::string, std::string> vendors_rsrc;
std::unique_ptr<DynamicPrintConfig> custom_config;
ConfigWizard::RunReason run_reason = RR_USER;
AppConfig appconfig_new; // Backing for vendor/model/variant and material selections in the GUI
BundleMap bundles; // Holds all loaded config bundles, the key is the vendor names.
// Materials refers to Presets in those bundles by pointers.
// Also we update the is_visible flag in printer Presets according to the
// PrinterPickers state.
Materials filaments; // Holds available filament presets and their types & vendors
Materials sla_materials; // Ditto for SLA materials
std::unique_ptr<DynamicPrintConfig> custom_config; // Backing for custom printer definition
bool any_fff_selected; // Used to decide whether to display Filaments page
bool any_sla_selected; // Used to decide whether to display SLA Materials page
wxScrolledWindow *hscroll = nullptr;
wxBoxSizer *hscroll_sizer = nullptr;
@ -279,9 +433,13 @@ struct ConfigWizard::priv
PageWelcome *page_welcome = nullptr;
PagePrinters *page_fff = nullptr;
PagePrinters *page_msla = nullptr;
PageMaterials *page_filaments = nullptr;
PageMaterials *page_sla_materials = nullptr;
PageCustom *page_custom = nullptr;
PageUpdate *page_update = nullptr;
PageVendors *page_vendors = nullptr; // XXX: ?
PageMode *page_mode = nullptr;
PageVendors *page_vendors = nullptr;
Pages3rdparty pages_3rdparty;
// Custom setup pages
PageFirmware *page_firmware = nullptr;
@ -289,17 +447,30 @@ struct ConfigWizard::priv
PageDiameters *page_diams = nullptr;
PageTemperatures *page_temps = nullptr;
priv(ConfigWizard *q) : q(q) {}
// Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex)
std::vector<ConfigWizardPage*> all_pages;
void load_pages(bool custom_setup);
priv(ConfigWizard *q)
: q(q)
, filaments(T_FFF)
, sla_materials(T_SLA)
, any_sla_selected(false)
{}
void load_pages();
void init_dialog_size();
bool check_first_variant() const;
void load_vendors();
void add_page(ConfigWizardPage *page);
void enable_next(bool enable);
void set_start_page(ConfigWizard::StartPage start_page);
void create_3rdparty_pages();
void set_run_reason(RunReason run_reason);
void update_materials(Technology technology);
void on_custom_setup(bool custom_wanted);
void on_custom_setup();
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
void on_3rdparty_install(const VendorProfile *vendor, bool install);
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);

View File

@ -1747,101 +1747,114 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
_set_current();
struct ModelVolumeState {
ModelVolumeState(const GLVolume *volume) :
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
ModelVolumeState(const ModelVolume *model_volume, const ObjectID &instance_id, const GLVolume::CompositeID &composite_id) :
model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
ModelVolumeState(const ObjectID &volume_id, const ObjectID &instance_id) :
model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
bool new_geometry() const { return this->volume_idx == size_t(-1); }
const ModelVolume *model_volume;
ModelVolumeState(const GLVolume* volume) :
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
ModelVolumeState(const ModelVolume* model_volume, const ObjectID& instance_id, const GLVolume::CompositeID& composite_id) :
model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
ModelVolumeState(const ObjectID& volume_id, const ObjectID& instance_id) :
model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
bool new_geometry() const { return this->volume_idx == size_t(-1); }
const ModelVolume* model_volume;
// ObjectID of ModelVolume + ObjectID of ModelInstance
// or timestamp of an SLAPrintObjectStep + ObjectID of ModelInstance
std::pair<size_t, size_t> geometry_id;
GLVolume::CompositeID composite_id;
// Volume index in the new GLVolume vector.
size_t volume_idx;
size_t volume_idx;
};
std::vector<ModelVolumeState> model_volume_state;
std::vector<ModelVolumeState> aux_volume_state;
std::vector<ModelVolumeState> aux_volume_state;
struct GLVolumeState {
GLVolumeState() :
volume_idx(-1) {}
GLVolumeState(const GLVolume* volume, unsigned int volume_idx) :
composite_id(volume->composite_id), volume_idx(volume_idx) {}
GLVolume::CompositeID composite_id;
// Volume index in the old GLVolume vector.
size_t volume_idx;
};
// SLA steps to pull the preview meshes for.
typedef std::array<SLAPrintObjectStep, 2> SLASteps;
SLASteps sla_steps = { slaposSupportTree, slaposPad };
struct SLASupportState {
std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step;
std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step;
};
// State of the sla_steps for all SLAPrintObjects.
std::vector<SLASupportState> sla_support_state;
std::vector<size_t> instance_ids_selected;
std::vector<size_t> map_glvolume_old_to_new(m_volumes.volumes.size(), size_t(-1));
std::vector<GLVolumeState> deleted_volumes;
std::vector<GLVolume*> glvolumes_new;
glvolumes_new.reserve(m_volumes.volumes.size());
auto model_volume_state_lower = [](const ModelVolumeState &m1, const ModelVolumeState &m2) { return m1.geometry_id < m2.geometry_id; };
auto model_volume_state_lower = [](const ModelVolumeState& m1, const ModelVolumeState& m2) { return m1.geometry_id < m2.geometry_id; };
m_reload_delayed = ! m_canvas->IsShown() && ! refresh_immediately && ! force_full_scene_refresh;
m_reload_delayed = !m_canvas->IsShown() && !refresh_immediately && !force_full_scene_refresh;
PrinterTechnology printer_technology = m_process->current_printer_technology();
PrinterTechnology printer_technology = m_process->current_printer_technology();
int volume_idx_wipe_tower_old = -1;
// Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed).
// First initialize model_volumes_new_sorted & model_instances_new_sorted.
for (int object_idx = 0; object_idx < (int)m_model->objects.size(); ++ object_idx) {
const ModelObject *model_object = m_model->objects[object_idx];
for (int instance_idx = 0; instance_idx < (int)model_object->instances.size(); ++ instance_idx) {
const ModelInstance *model_instance = model_object->instances[instance_idx];
for (int volume_idx = 0; volume_idx < (int)model_object->volumes.size(); ++ volume_idx) {
const ModelVolume *model_volume = model_object->volumes[volume_idx];
model_volume_state.emplace_back(model_volume, model_instance->id(), GLVolume::CompositeID(object_idx, volume_idx, instance_idx));
for (int object_idx = 0; object_idx < (int)m_model->objects.size(); ++object_idx) {
const ModelObject* model_object = m_model->objects[object_idx];
for (int instance_idx = 0; instance_idx < (int)model_object->instances.size(); ++instance_idx) {
const ModelInstance* model_instance = model_object->instances[instance_idx];
for (int volume_idx = 0; volume_idx < (int)model_object->volumes.size(); ++volume_idx) {
const ModelVolume* model_volume = model_object->volumes[volume_idx];
model_volume_state.emplace_back(model_volume, model_instance->id(), GLVolume::CompositeID(object_idx, volume_idx, instance_idx));
}
}
}
if (printer_technology == ptSLA) {
const SLAPrint *sla_print = this->sla_print();
#ifndef NDEBUG
const SLAPrint* sla_print = this->sla_print();
#ifndef NDEBUG
// Verify that the SLAPrint object is synchronized with m_model.
check_model_ids_equal(*m_model, sla_print->model());
#endif /* NDEBUG */
#endif /* NDEBUG */
sla_support_state.reserve(sla_print->objects().size());
for (const SLAPrintObject *print_object : sla_print->objects()) {
for (const SLAPrintObject* print_object : sla_print->objects()) {
SLASupportState state;
for (size_t istep = 0; istep < sla_steps.size(); ++ istep) {
state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]);
if (state.step[istep].state == PrintStateBase::DONE) {
if (! print_object->has_mesh(sla_steps[istep]))
for (size_t istep = 0; istep < sla_steps.size(); ++istep) {
state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]);
if (state.step[istep].state == PrintStateBase::DONE) {
if (!print_object->has_mesh(sla_steps[istep]))
// Consider the DONE step without a valid mesh as invalid for the purpose
// of mesh visualization.
state.step[istep].state = PrintStateBase::INVALID;
else
for (const ModelInstance *model_instance : print_object->model_object()->instances)
// Only the instances, which are currently printable, will have the SLA support structures kept.
// The instances outside the print bed will have the GLVolumes of their support structures released.
if (model_instance->is_printable())
for (const ModelInstance* model_instance : print_object->model_object()->instances)
// Only the instances, which are currently printable, will have the SLA support structures kept.
// The instances outside the print bed will have the GLVolumes of their support structures released.
if (model_instance->is_printable())
aux_volume_state.emplace_back(state.step[istep].timestamp, model_instance->id());
}
}
sla_support_state.emplace_back(state);
}
sla_support_state.emplace_back(state);
}
}
std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
std::sort(aux_volume_state .begin(), aux_volume_state .end(), model_volume_state_lower);
std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
// Release all ModelVolume based GLVolumes not found in the current Model.
for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++ volume_id) {
GLVolume *volume = m_volumes.volumes[volume_id];
for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) {
GLVolume* volume = m_volumes.volumes[volume_id];
ModelVolumeState key(volume);
ModelVolumeState *mvs = nullptr;
ModelVolumeState* mvs = nullptr;
if (volume->volume_idx() < 0) {
auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
if (it != aux_volume_state.end() && it->geometry_id == key.geometry_id)
// This can be an SLA support structure that should not be rendered (in case someone used undo
// to revert to before it was generated). We only reuse the volume if that's not the case.
if (m_model->objects[volume->composite_id.object_id]->sla_points_status != sla::PointsStatus::NoPoints)
mvs = &(*it);
} else {
auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower);
}
else {
auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower);
if (it != model_volume_state.end() && it->geometry_id == key.geometry_id)
mvs = &(*it);
mvs = &(*it);
}
// Emplace instance ID of the volume. Both the aux volumes and model volumes share the same instance ID.
// The wipe tower has its own wipe_tower_instance_id().
@ -1854,19 +1867,23 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(volume_idx_wipe_tower_old == -1);
volume_idx_wipe_tower_old = (int)volume_id;
}
if (! m_reload_delayed)
if (!m_reload_delayed)
{
deleted_volumes.emplace_back(volume, volume_id);
delete volume;
} else {
}
}
else {
// This GLVolume will be reused.
volume->set_sla_shift_z(0.0);
map_glvolume_old_to_new[volume_id] = glvolumes_new.size();
mvs->volume_idx = glvolumes_new.size();
glvolumes_new.emplace_back(volume);
// Update color of the volume based on the current extruder.
if (mvs->model_volume != nullptr) {
int extruder_id = mvs->model_volume->extruder_id();
if (extruder_id != -1)
volume->extruder_id = extruder_id;
if (mvs->model_volume != nullptr) {
int extruder_id = mvs->model_volume->extruder_id();
if (extruder_id != -1)
volume->extruder_id = extruder_id;
volume->is_modifier = !mvs->model_volume->is_model_part();
volume->set_color_from_model_volume(mvs->model_volume);
@ -1884,6 +1901,16 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
bool update_object_list = false;
auto find_old_volume_id = [&deleted_volumes](const GLVolume::CompositeID& id) -> unsigned int {
for (unsigned int i = 0; i < (unsigned int)deleted_volumes.size(); ++i)
{
const GLVolumeState& v = deleted_volumes[i];
if (v.composite_id == id)
return v.volume_idx;
}
return (unsigned int)-1;
};
if (m_volumes.volumes != glvolumes_new)
update_object_list = true;
m_volumes.volumes = std::move(glvolumes_new);
@ -1898,9 +1925,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// New volume.
unsigned int old_id = find_old_volume_id(it->composite_id);
if (old_id != -1)
map_glvolume_old_to_new[old_id] = m_volumes.volumes.size();
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized);
m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true;
update_object_list = true;
} else {
// Recycling an old GLVolume.
GLVolume &existing_volume = *m_volumes.volumes[it->volume_idx];
@ -1999,19 +2029,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
const Print *print = m_process->fff_print();
float depth = print->get_wipe_tower_depth();
// Calculate wipe tower brim spacing.
const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
double layer_height = print_config.opt_float("layer_height");
double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height);
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
double nozzle_diameter = print->config().nozzle_diameter.values[0];
float depth = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth;
float brim_width = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width;
if (!print->is_step_done(psWipeTower))
depth = (900.f/w) * (float)(extruders_count - 1);
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_spacing * 4.5f, m_initialized);
brim_width, m_initialized);
if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
@ -5284,20 +5312,18 @@ void GLCanvas3D::_load_fff_shells()
// adds wipe tower's volume
double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2);
const PrintConfig& config = print->config();
unsigned int extruders_count = config.nozzle_diameter.size();
size_t extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) {
float depth = print->get_wipe_tower_depth();
// Calculate wipe tower brim spacing.
const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
double layer_height = print_config.opt_float("layer_height");
double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height);
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
double nozzle_diameter = print->config().nozzle_diameter.values[0];
float depth = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth;
float brim_width = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width;
if (!print->is_step_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1);
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
!print->is_step_done(psWipeTower), brim_spacing * 4.5f, m_initialized);
!print->is_step_done(psWipeTower), brim_width, m_initialized);
}
}
}

View File

@ -101,49 +101,6 @@ const std::string& shortkey_alt_prefix()
return str;
}
bool config_wizard_startup(bool app_config_exists)
{
if (!app_config_exists || wxGetApp().preset_bundle->printers.size() <= 1) {
config_wizard(ConfigWizard::RR_DATA_EMPTY);
return true;
} else if (get_app_config()->legacy_datadir()) {
// Looks like user has legacy pre-vendorbundle data directory,
// explain what this is and run the wizard
MsgDataLegacy dlg;
dlg.ShowModal();
config_wizard(ConfigWizard::RR_DATA_LEGACY);
return true;
}
return false;
}
void config_wizard(int reason)
{
// Exit wizard if there are unsaved changes and the user cancels the action.
if (! wxGetApp().check_unsaved_changes())
return;
try {
ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater);
}
catch (const std::exception &e) {
show_error(nullptr, e.what());
}
wxGetApp().load_current_presets();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && model_has_multi_part_objects(wxGetApp().model()))
{
show_info(nullptr,
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
_(L("Please check and fix your object list.")),
_(L("Attention!")));
}
}
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
{

View File

@ -35,14 +35,6 @@ extern AppConfig* get_app_config();
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
// Checks if configuration wizard needs to run, calls config_wizard if so.
// Returns whether the Wizard ran.
extern bool config_wizard_startup(bool app_config_exists);
// Opens the configuration wizard, returns true if wizard is finished & accepted.
// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl.
extern void config_wizard(int run_reason);
// Change option value in config
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);

View File

@ -38,7 +38,6 @@
#include "../Utils/PresetUpdater.hpp"
#include "../Utils/PrintHost.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "ConfigWizard.hpp"
#include "slic3r/Config/Snapshot.hpp"
#include "ConfigSnapshotDialog.hpp"
#include "FirmwareDialog.hpp"
@ -46,6 +45,7 @@
#include "Tab.hpp"
#include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp"
#include "UpdateDialogs.hpp"
#ifdef __WXMSW__
#include <Shlobj.h>
@ -148,6 +148,7 @@ GUI_App::GUI_App()
: wxApp()
, m_em_unit(10)
, m_imgui(new ImGuiWrapper())
, m_wizard(nullptr)
{}
GUI_App::~GUI_App()
@ -204,7 +205,6 @@ bool GUI_App::on_init_inner()
// supplied as argument to --datadir; in that case we should still run the wizard
preset_bundle->setup_directories();
app_conf_exists = app_config->exists();
// load settings
app_conf_exists = app_config->exists();
if (app_conf_exists) {
@ -287,7 +287,7 @@ bool GUI_App::on_init_inner()
}
CallAfter([this] {
config_wizard_startup(app_conf_exists);
config_wizard_startup();
preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle);
});
@ -826,7 +826,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
switch (event.GetId() - config_id_base) {
case ConfigMenuWizard:
config_wizard(ConfigWizard::RR_USER);
run_wizard(ConfigWizard::RR_USER);
break;
case ConfigMenuTakeSnapshot:
// Take a configuration snapshot.
@ -1057,6 +1057,31 @@ void GUI_App::open_web_page_localized(const std::string &http_address)
wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe());
}
bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page)
{
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
if (! m_wizard) {
m_wizard = new ConfigWizard(mainframe);
}
const bool res = m_wizard->run(reason, start_page);
if (res) {
load_current_presets();
if (preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA
&& Slic3r::model_has_multi_part_objects(wxGetApp().model())) {
GUI::show_info(nullptr,
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
_(L("Please check and fix your object list.")),
_(L("Attention!")));
}
}
return res;
}
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)
{
if (name.empty()) { return; }
@ -1105,6 +1130,24 @@ void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
}
}
bool GUI_App::config_wizard_startup()
{
if (!app_conf_exists || preset_bundle->printers.size() <= 1) {
run_wizard(ConfigWizard::RR_DATA_EMPTY);
return true;
} else if (get_app_config()->legacy_datadir()) {
// Looks like user has legacy pre-vendorbundle data directory,
// explain what this is and run the wizard
MsgDataLegacy dlg;
dlg.ShowModal();
run_wizard(ConfigWizard::RR_DATA_LEGACY);
return true;
}
return false;
}
// static method accepting a wxWindow object as first parameter
// void warning_catcher{
// my($self, $message_dialog) = @_;

View File

@ -6,6 +6,7 @@
#include "libslic3r/PrintConfig.hpp"
#include "MainFrame.hpp"
#include "ImGuiWrapper.hpp"
#include "ConfigWizard.hpp"
#include <wx/app.h>
#include <wx/colour.h>
@ -69,6 +70,7 @@ enum ConfigMenuIDs {
};
class Tab;
class ConfigWizard;
static wxString dots("", wxConvUTF8);
@ -96,6 +98,7 @@ class GUI_App : public wxApp
std::unique_ptr<ImGuiWrapper> m_imgui;
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
ConfigWizard* m_wizard; // Managed by wxWindow tree
public:
bool OnInit() override;
@ -184,6 +187,7 @@ public:
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
void open_web_page_localized(const std::string &http_address);
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
private:
bool on_init_inner();
@ -191,6 +195,9 @@ private:
void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
void window_pos_sanitize(wxTopLevelWindow* window);
bool select_language();
bool config_wizard_startup();
#ifdef __WXMSW__
void associate_3mf_files();
#endif // __WXMSW__

View File

@ -1571,6 +1571,12 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
menu->AppendSeparator();
}
void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const
{
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater());
}
void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const
{
const wxString name = _(L("Change extruder"));
@ -1620,6 +1626,7 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
append_menu_items_osx(menu);
#endif // __WXOSX__
append_menu_item_reload_from_disk(menu);
append_menu_item_export_stl(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_scale_selection_to_fit_print_volume(menu);
@ -1643,6 +1650,7 @@ void ObjectList::create_sla_object_popupmenu(wxMenu *menu)
append_menu_items_osx(menu);
#endif // __WXOSX__
append_menu_item_reload_from_disk(menu);
append_menu_item_export_stl(menu);
append_menu_item_fix_through_netfabb(menu);
// rest of a object_sla_menu will be added later in:
@ -1655,8 +1663,9 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
append_menu_items_osx(menu);
#endif // __WXOSX__
append_menu_item_fix_through_netfabb(menu);
append_menu_item_reload_from_disk(menu);
append_menu_item_export_stl(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_split(menu);

View File

@ -237,7 +237,8 @@ public:
wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu) const ;
void append_menu_item_export_stl(wxMenu* menu) const;
void append_menu_item_reload_from_disk(wxMenu* menu) const;
void append_menu_item_change_extruder(wxMenu* menu) const;
void append_menu_item_delete(wxMenu* menu);
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);

View File

@ -120,9 +120,6 @@ static void set_font_and_background_style(wxWindow* win, const wxFont& font)
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
OG_Settings(parent, true)
#ifndef __APPLE__
, m_focused_option("")
#endif // __APPLE__
{
m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation");
@ -415,292 +412,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
m_og->sizer->Add(m_main_grid_sizer, 1, wxEXPAND | wxALL, border);
}
/*
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
OG_Settings(parent, true)
#ifndef __APPLE__
, m_focused_option("")
#endif // __APPLE__
{
m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation");
m_og->set_name(_(L("Object Manipulation")));
m_og->label_width = 12;//125;
m_og->set_grid_vgap(5);
m_og->m_on_change = std::bind(&ObjectManipulation::on_change, this, std::placeholders::_1, std::placeholders::_2);
m_og->m_fill_empty_value = std::bind(&ObjectManipulation::on_fill_empty_value, this, std::placeholders::_1);
m_og->m_set_focus = [this](const std::string& opt_key)
{
#ifndef __APPLE__
m_focused_option = opt_key;
#endif // __APPLE__
// needed to show the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true);
};
ConfigOptionDef def;
Line line = Line{ "Name", "Object name" };
auto manifold_warning_icon = [this](wxWindow* parent) {
m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
if (is_windows10())
m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
{
// if object/sub-object has no errors
if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData())
return;
wxGetApp().obj_list()->fix_through_netfabb();
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
});
return m_fix_throught_netfab_bitmap;
};
line.near_label_widget = manifold_warning_icon;
def.label = "";
def.gui_type = "legend";
def.tooltip = L("Object name");
#ifdef __APPLE__
def.width = 20;
#else
def.width = 22;
#endif
def.set_default_value(new ConfigOptionString{ " " });
line.append_option(Option(def, "object_name"));
m_og->append_line(line);
const int field_width = 5;
// Mirror button size:
const int mirror_btn_width = 3;
// Legend for object modification
line = Line{ "", "" };
def.label = "";
def.type = coString;
def.width = field_width - mirror_btn_width;
// Load bitmaps to be used for the mirroring buttons:
m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on");
m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off");
m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png");
static const char axes[] = { 'X', 'Y', 'Z' };
for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) {
const char label = axes[axis_idx];
def.set_default_value(new ConfigOptionString{ std::string(" ") + label });
Option option(def, std::string() + label + "_axis_legend");
// We will add a button to toggle mirroring to each axis:
auto mirror_button = [this, mirror_btn_width, axis_idx, label](wxWindow* parent) {
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label));
btn->SetBitmapDisabled_(m_mirror_bitmap_hidden);
m_mirror_buttons[axis_idx].first = btn;
m_mirror_buttons[axis_idx].second = mbShown;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) {
Axis axis = (Axis)(axis_idx + X);
if (m_mirror_buttons[axis_idx].second == mbHidden)
return;
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis));
}
else if (selection.is_single_full_instance()) {
for (unsigned int idx : selection.get_volume_idxs()){
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis));
}
}
else
return;
// Update mirroring at the GLVolumes.
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
canvas->do_mirror(L("Set Mirror"));
UpdateAndShow(true);
});
return sizer;
};
option.side_widget = mirror_button;
line.append_option(option);
}
line.near_label_widget = [this](wxWindow* parent) {
wxBitmapComboBox *combo = create_word_local_combo(parent);
combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent &evt) { this->set_world_coordinates(evt.GetSelection() != 1); }), combo->GetId());
m_word_local_combo = combo;
return combo;
};
m_og->append_line(line);
auto add_og_to_object_settings = [this, field_width](const std::string& option_name, const std::string& sidetext)
{
Line line = { _(option_name), "" };
ConfigOptionDef def;
def.type = coFloat;
def.set_default_value(new ConfigOptionFloat(0.0));
def.width = field_width;
if (option_name == "Scale") {
// Add "uniform scaling" button in front of "Scale" option
line.near_label_widget = [this](wxWindow* parent) {
auto btn = new LockButton(parent, wxID_ANY);
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){
event.Skip();
wxTheApp->CallAfter([btn, this]() { set_uniform_scaling(btn->IsLocked()); });
});
m_lock_bnt = btn;
return btn;
};
// Add reset scale button
auto reset_scale_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset scale")));
m_reset_scale_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale")));
change_scale_value(0, 100.);
change_scale_value(1, 100.);
change_scale_value(2, 100.);
});
return sizer;
};
line.append_widget(reset_scale_button);
}
else if (option_name == "Rotation") {
// Add reset rotation button
auto reset_rotation_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset rotation")));
m_reset_rotation_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
volume->set_volume_rotation(Vec3d::Zero());
}
else if (selection.is_single_full_instance()) {
for (unsigned int idx : selection.get_volume_idxs()){
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
volume->set_instance_rotation(Vec3d::Zero());
}
}
else
return;
// Update rotation at the GLVolumes.
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
canvas->do_rotate(L("Reset Rotation"));
UpdateAndShow(true);
});
return sizer;
};
line.append_widget(reset_rotation_button);
}
else if (option_name == "Position") {
// Add drop to bed button
auto drop_to_bed_button = [=](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed"));
btn->SetToolTip(_(L("Drop to bed")));
m_drop_to_bed_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
// ???
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed")));
change_position_value(0, diff.x());
change_position_value(1, diff.y());
change_position_value(2, diff.z());
}
});
return sizer;
};
line.append_widget(drop_to_bed_button);
}
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
else if (option_name == "Size") {
line.near_label_widget = [this](wxWindow* parent) {
return new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition,
create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
};
}
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
for (const char *axis : { "_x", "_y", "_z" }) {
if (axis[1] == 'z')
def.sidetext = sidetext;
Option option = Option(def, lower_name + axis);
option.opt.full_width = true;
line.append_option(option);
}
return line;
};
// Settings table
m_og->sidetext_width = 3;
m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label);
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label);
m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label);
m_og->append_line(add_og_to_object_settings(L("Size"), "mm"));
// call back for a rescale of button "Set uniform scale"
m_og->rescale_near_label_widget = [this](wxWindow* win) {
// rescale lock icon
auto *ctrl = dynamic_cast<LockButton*>(win);
if (ctrl != nullptr) {
ctrl->msw_rescale();
return;
}
if (win == m_fix_throught_netfab_bitmap)
return;
// rescale "place" of the empty icon (to correct layout of the "Size" and "Scale")
if (dynamic_cast<wxStaticBitmap*>(win) != nullptr)
win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
};
}
*/
void ObjectManipulation::Show(const bool show)
{
if (show != IsShown()) {
@ -710,9 +421,9 @@ void ObjectManipulation::Show(const bool show)
if (show && wxGetApp().get_mode() != comSimple) {
// Show the label and the name of the STL in simple mode only.
// Label "Name: "
/*m_og->get_grid_sizer()*/m_main_grid_sizer->Show(size_t(0), false);
m_main_grid_sizer->Show(size_t(0), false);
// The actual name of the STL.
/*m_og->get_grid_sizer()*/m_main_grid_sizer->Show(size_t(1), false);
m_main_grid_sizer->Show(size_t(1), false);
}
}
@ -833,26 +544,6 @@ void ObjectManipulation::update_if_dirty()
update_label(m_cache.rotate_label_string, m_new_rotate_label_string, m_rotate_Label);
update_label(m_cache.scale_label_string, m_new_scale_label_string, m_scale_Label);
/*
char axis[2] = "x";
for (int i = 0; i < 3; ++ i, ++ axis[0]) {
auto update = [this, i, &axis](Vec3d &cached, Vec3d &cached_rounded, const char *key, const Vec3d &new_value) {
wxString new_text = double_to_string(new_value(i), 2);
double new_rounded;
new_text.ToDouble(&new_rounded);
if (std::abs(cached_rounded(i) - new_rounded) > EPSILON) {
cached_rounded(i) = new_rounded;
m_og->set_value(std::string(key) + axis, new_text);
}
cached(i) = new_value(i);
};
update(m_cache.position, m_cache.position_rounded, "position_", m_new_position);
update(m_cache.scale, m_cache.scale_rounded, "scale_", m_new_scale);
update(m_cache.size, m_cache.size_rounded, "size_", m_new_size);
update(m_cache.rotation, m_cache.rotation_rounded, "rotation_", m_new_rotation);
}
*/
enum ManipulationEditorKey
{
mePosition = 0,
@ -1007,17 +698,10 @@ void ObjectManipulation::update_mirror_buttons_visibility()
#ifndef __APPLE__
void ObjectManipulation::emulate_kill_focus()
{
if (m_focused_option.empty())
if (!m_focused_editor)
return;
// we need to use a copy because the value of m_focused_option is modified inside on_change() and on_fill_empty_value()
std::string option = m_focused_option;
// see TextCtrl::propagate_value()
if (static_cast<wxTextCtrl*>(m_og->get_fieldc(option, 0)->getWindow())->GetValue().empty())
on_fill_empty_value(option);
else
on_change(option, 0);
m_focused_editor->kill_focus(this);
}
#endif // __APPLE__
@ -1156,40 +840,6 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
}
void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value)
{
Field* field = m_og->get_field(opt_key);
bool enter_pressed = (field != nullptr) && field->get_enter_pressed();
if (!enter_pressed)
{
// if the change does not come from the user pressing the ENTER key
// we need to hide the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false);
#ifndef __APPLE__
m_focused_option = "";
#endif // __APPLE__
}
else
// if the change comes from the user pressing the ENTER key, restore the key state
field->set_enter_pressed(false);
if (!m_cache.is_valid())
return;
int axis = opt_key.back() - 'x';
double new_value = boost::any_cast<double>(m_og->get_value(opt_key));
if (boost::starts_with(opt_key, "position_"))
change_position_value(axis, new_value);
else if (boost::starts_with(opt_key, "rotation_"))
change_rotation_value(axis, new_value);
else if (boost::starts_with(opt_key, "scale_"))
change_scale_value(axis, new_value);
else if (boost::starts_with(opt_key, "size_"))
change_size_value(axis, new_value);
}
void ObjectManipulation::on_change(const std::string& opt_key, int axis, double new_value)
{
if (!m_cache.is_valid())
@ -1205,42 +855,6 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double
change_size_value(axis, new_value);
}
void ObjectManipulation::on_fill_empty_value(const std::string& opt_key)
{
// needed to hide the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false);
#ifndef __APPLE__
m_focused_option = "";
#endif // __APPLE__
if (!m_cache.is_valid())
return;
const Vec3d *vec = nullptr;
Vec3d *rounded = nullptr;
if (boost::starts_with(opt_key, "position_")) {
vec = &m_cache.position;
rounded = &m_cache.position_rounded;
} else if (boost::starts_with(opt_key, "rotation_")) {
vec = &m_cache.rotation;
rounded = &m_cache.rotation_rounded;
} else if (boost::starts_with(opt_key, "scale_")) {
vec = &m_cache.scale;
rounded = &m_cache.scale_rounded;
} else if (boost::starts_with(opt_key, "size_")) {
vec = &m_cache.size;
rounded = &m_cache.size_rounded;
} else
assert(false);
if (vec != nullptr) {
int axis = opt_key.back() - 'x';
wxString new_text = double_to_string((*vec)(axis));
m_og->set_value(opt_key, new_text);
new_text.ToDouble(&(*rounded)(axis));
}
}
void ObjectManipulation::set_uniform_scaling(const bool new_value)
{
const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection();
@ -1338,43 +952,25 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
parent->on_change(m_opt_key, m_axis, get_value());
}, this->GetId());
this->Bind(wxEVT_KILL_FOCUS, [this, parent/*, edit_fn*/](wxFocusEvent& e)
this->Bind(wxEVT_KILL_FOCUS, [this, parent](wxFocusEvent& e)
{
if (!m_enter_pressed) {
parent->on_change(m_opt_key, m_axis, get_value());
parent->set_focused_editor(nullptr);
// if the change does not come from the user pressing the ENTER key
// we need to hide the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, false);
// #ifndef __WXGTK__
// /* Update data for next editor selection.
// * But under GTK it looks like there is no information about selected control at e.GetWindow(),
// * so we'll take it from wxEVT_LEFT_DOWN event
// * */
// LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
// if (new_editor)
// new_editor->set_focus_data();
// #endif // not __WXGTK__
}
if (!m_enter_pressed)
kill_focus(parent);
e.Skip();
}, this->GetId());
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& e)
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
{
parent->set_focused_editor(this);
// needed to show the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, true);
e.Skip();
}, this->GetId());
// #ifdef __WXGTK__ // Workaround! To take information about selectable range
// this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
// {
// set_focus_data();
// e.Skip();
// }, this->GetId());
// #endif //__WXGTK__
this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
{
// select all text using Ctrl+A
@ -1417,5 +1013,14 @@ void ManipulationEditor::set_value(const wxString& new_value)
SetValue(m_valid_value);
}
void ManipulationEditor::kill_focus(ObjectManipulation* parent)
{
parent->on_change(m_opt_key, m_axis, get_value());
// if the change does not come from the user pressing the ENTER key
// we need to hide the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, false);
}
} //namespace GUI
} //namespace Slic3r

View File

@ -32,6 +32,7 @@ public:
void msw_rescale();
void set_value(const wxString& new_value);
void kill_focus(ObjectManipulation *parent);
private:
double get_value();
@ -118,7 +119,7 @@ class ObjectManipulation : public OG_Settings
#ifndef __APPLE__
// Currently focused option name (empty if none)
std::string m_focused_option;
ManipulationEditor* m_focused_editor {nullptr};
#endif // __APPLE__
wxFlexGridSizer* m_main_grid_sizer;
@ -160,6 +161,7 @@ public:
void update_warning_icon_state(const wxString& tooltip);
void msw_rescale();
void on_change(const std::string& opt_key, int axis, double new_value);
void set_focused_editor(ManipulationEditor* focused_editor) { m_focused_editor = focused_editor; }
private:
void reset_settings_value();
@ -176,9 +178,6 @@ private:
void change_scale_value(int axis, double value);
void change_size_value(int axis, double value);
void do_scale(int axis, const Vec3d &scale) const;
void on_change(t_config_option_key opt_key, const boost::any& value);
void on_fill_empty_value(const std::string& opt_key);
};
}}

View File

@ -10,6 +10,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/log/trivial.hpp>
#include <wx/sizer.h>
@ -251,11 +252,18 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
auto selected_item = this->GetSelection();
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
if (marker == LABEL_ITEM_MARKER || marker == LABEL_ITEM_CONFIG_WIZARD) {
if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) {
this->SetSelection(this->last_selected);
evt.StopPropagation();
if (marker == LABEL_ITEM_CONFIG_WIZARD)
wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); });
if (marker >= LABEL_ITEM_WIZARD_PRINTERS) {
ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME;
switch (marker) {
case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break;
case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break;
case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break;
}
wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); });
}
} else if ( this->last_selected != selected_item ||
wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) {
this->last_selected = selected_item;
@ -1571,7 +1579,8 @@ struct Plater::priv
size_t count = 0; // To know how much space to reserve
for (auto obj : model.objects) count += obj->instances.size();
m_selected.clear(), m_unselected.clear();
m_selected.clear();
m_unselected.clear();
m_selected.reserve(count + 1 /* for optional wti */);
m_unselected.reserve(count + 1 /* for optional wti */);
}
@ -1585,11 +1594,12 @@ struct Plater::priv
// Set up arrange polygon for a ModelInstance and Wipe tower
template<class T> ArrangePolygon get_arrange_poly(T *obj) const {
ArrangePolygon ap = obj->get_arrange_polygon();
ap.priority = 0;
ap.bed_idx = ap.translation.x() / bed_stride();
ap.setter = [obj, this](const ArrangePolygon &p) {
ap.priority = 0;
ap.bed_idx = ap.translation.x() / bed_stride();
ap.setter = [obj, this](const ArrangePolygon &p) {
if (p.is_arranged()) {
auto t = p.translation; t.x() += p.bed_idx * bed_stride();
auto t = p.translation;
t.x() += p.bed_idx * bed_stride();
obj->apply_arrange_result(t, p.rotation);
}
};
@ -1620,7 +1630,8 @@ struct Plater::priv
obj_sel(model.objects.size(), nullptr);
for (auto &s : plater().get_selection().get_content())
if (s.first < int(obj_sel.size())) obj_sel[s.first] = &s.second;
if (s.first < int(obj_sel.size()))
obj_sel[size_t(s.first)] = &s.second;
// Go through the objects and check if inside the selection
for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) {
@ -1630,7 +1641,8 @@ struct Plater::priv
std::vector<bool> inst_sel(mo->instances.size(), false);
if (instlist)
for (auto inst_id : *instlist) inst_sel[inst_id] = true;
for (auto inst_id : *instlist)
inst_sel[size_t(inst_id)] = true;
for (size_t i = 0; i < inst_sel.size(); ++i) {
ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]);
@ -1902,6 +1914,7 @@ struct Plater::priv
bool can_fix_through_netfabb() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
bool can_reload_from_disk() const;
void msw_rescale_object_menu();
@ -1938,7 +1951,6 @@ private:
* */
std::string m_last_fff_printer_profile_name;
std::string m_last_sla_printer_profile_name;
bool m_update_objects_list_on_loading{ true };
};
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
@ -2464,11 +2476,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
_(L("Object too large?")));
}
if (m_update_objects_list_on_loading)
{
for (const size_t idx : obj_idxs) {
wxGetApp().obj_list()->add_object_to_list(idx);
}
for (const size_t idx : obj_idxs) {
wxGetApp().obj_list()->add_object_to_list(idx);
}
update();
@ -2759,9 +2768,8 @@ void Plater::priv::ArrangeJob::process() {
try {
arrangement::arrange(m_selected, m_unselected, min_d, bedshape,
[this, count](unsigned st) {
if (st >
0) // will not finalize after last one
update_status(count - st, arrangestr);
if (st > 0) // will not finalize after last one
update_status(int(count - st), arrangestr);
},
[this]() { return was_canceled(); });
} catch (std::exception & /*e*/) {
@ -3093,88 +3101,110 @@ void Plater::priv::update_sla_scene()
void Plater::priv::reload_from_disk()
{
Plater::TakeSnapshot snapshot(q, _(L("Reload from Disk")));
Plater::TakeSnapshot snapshot(q, _(L("Reload from disk")));
auto& selection = get_selection();
const auto obj_orig_idx = selection.get_object_idx();
if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; }
int instance_idx = selection.get_instance_idx();
const Selection& selection = get_selection();
auto *object_orig = model.objects[obj_orig_idx];
std::vector<fs::path> input_paths(1, object_orig->input_file);
// disable render to avoid to show intermediate states
view3D->get_canvas3d()->enable_render(false);
// disable update of objects list while loading to avoid to show intermediate states
m_update_objects_list_on_loading = false;
const auto new_idxs = load_files(input_paths, true, false);
if (new_idxs.empty())
{
// error while loading
view3D->get_canvas3d()->enable_render(true);
if (selection.is_wipe_tower())
return;
}
for (const auto idx : new_idxs)
// struct to hold selected ModelVolumes by their indices
struct SelectedVolume
{
ModelObject *object = model.objects[idx];
object->config.apply(object_orig->config);
int object_idx;
int volume_idx;
object->clear_instances();
for (const ModelInstance *instance : object_orig->instances)
// operators needed by std::algorithms
bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); }
bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); }
};
std::vector<SelectedVolume> selected_volumes;
// collects selected ModelVolumes
const std::set<unsigned int>& selected_volumes_idxs = selection.get_volume_idxs();
for (unsigned int idx : selected_volumes_idxs)
{
const GLVolume* v = selection.get_volume(idx);
int o_idx = v->object_idx();
int v_idx = v->volume_idx();
selected_volumes.push_back({ o_idx, v_idx });
}
std::sort(selected_volumes.begin(), selected_volumes.end());
selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end());
// collects paths of files to load
std::vector<fs::path> input_paths;
for (const SelectedVolume& v : selected_volumes)
{
const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx];
if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file))
input_paths.push_back(volume->source.input_file);
}
std::sort(input_paths.begin(), input_paths.end());
input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end());
// load one file at a time
for (size_t i = 0; i < input_paths.size(); ++i)
{
const auto& path = input_paths[i].string();
Model new_model;
try
{
object->add_instance(*instance);
}
for (const ModelVolume* v : object_orig->volumes)
{
if (v->is_modifier())
object->add_volume(*v);
}
Vec3d offset = object_orig->origin_translation - object->origin_translation;
if (object->volumes.size() == object_orig->volumes.size())
{
for (size_t i = 0; i < object->volumes.size(); i++)
new_model = Model::read_from_file(path, nullptr, true, false);
for (ModelObject* model_object : new_model.objects)
{
object->volumes[i]->config.apply(object_orig->volumes[i]->config);
object->volumes[i]->translate(offset);
model_object->center_around_origin();
model_object->ensure_on_bed();
}
}
catch (std::exception&)
{
// error while loading
view3D->get_canvas3d()->enable_render(true);
return;
}
// XXX: Restore more: layer_height_ranges, layer_height_profile (?)
// update the selected volumes whose source is the current file
for (const SelectedVolume& old_v : selected_volumes)
{
ModelObject* old_model_object = model.objects[old_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[old_v.volume_idx];
int new_volume_idx = old_volume->source.volume_idx;
int new_object_idx = old_volume->source.object_idx;
if (old_volume->source.input_file == path)
{
if (new_object_idx < (int)new_model.objects.size())
{
ModelObject* new_model_object = new_model.objects[new_object_idx];
if (new_volume_idx < (int)new_model_object->volumes.size())
{
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
ModelVolume* new_volume = old_model_object->volumes.back();
new_volume->set_new_unique_id();
new_volume->config.apply(old_volume->config);
new_volume->set_type(old_volume->type());
new_volume->set_material_id(old_volume->material_id());
new_volume->set_transformation(old_volume->get_transformation());
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
}
}
}
}
}
// re-enable update of objects list
m_update_objects_list_on_loading = true;
model.adjust_min_z();
// puts the new objects into the list
for (const auto idx : new_idxs)
{
wxGetApp().obj_list()->add_object_to_list(idx);
}
remove(obj_orig_idx);
// update 3D scene
update();
// new GLVolumes have been created at this point, so update their printable state
for (size_t i = 0; i < model.objects.size(); ++i)
{
view3D->get_canvas3d()->update_instance_printable_state_for_object(i);
}
// re-enable render
view3D->get_canvas3d()->enable_render(true);
// the previous call to remove() clears the selection
// select newly added objects
selection.clear();
for (const auto idx : new_idxs)
{
selection.add_instance((unsigned int)idx - 1, instance_idx, false);
}
}
void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/)
@ -3599,6 +3629,9 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q);
sidebar->obj_list()->append_menu_item_export_stl(menu);
}
else {
@ -3625,8 +3658,8 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
menu->AppendSeparator();
append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
[this](wxCommandEvent&) { reload_from_disk(); });
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q);
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
[this](wxCommandEvent&) { q->export_stl(false, true); });
@ -3781,6 +3814,48 @@ bool Plater::priv::can_mirror() const
return get_selection().is_from_single_instance();
}
bool Plater::priv::can_reload_from_disk() const
{
// struct to hold selected ModelVolumes by their indices
struct SelectedVolume
{
int object_idx;
int volume_idx;
// operators needed by std::algorithms
bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); }
bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); }
};
std::vector<SelectedVolume> selected_volumes;
const Selection& selection = get_selection();
// collects selected ModelVolumes
const std::set<unsigned int>& selected_volumes_idxs = selection.get_volume_idxs();
for (unsigned int idx : selected_volumes_idxs)
{
const GLVolume* v = selection.get_volume(idx);
int o_idx = v->object_idx();
int v_idx = v->volume_idx();
selected_volumes.push_back({ o_idx, v_idx });
}
std::sort(selected_volumes.begin(), selected_volumes.end());
selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end());
// collects paths of files to load
std::vector<fs::path> paths;
for (const SelectedVolume& v : selected_volumes)
{
const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx];
if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file))
paths.push_back(volume->source.input_file);
}
std::sort(paths.begin(), paths.end());
paths.erase(std::unique(paths.begin(), paths.end()), paths.end());
return !paths.empty();
}
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
bool new_shape = bed.set_shape(shape, custom_texture, custom_model);
@ -4563,6 +4638,11 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
}
}
void Plater::reload_from_disk()
{
p->reload_from_disk();
}
bool Plater::has_toolpaths_to_export() const
{
return p->preview->get_canvas3d()->has_toolpaths_to_export();
@ -5108,6 +5188,7 @@ bool Plater::can_copy_to_clipboard() const
bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); }
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }

View File

@ -56,8 +56,12 @@ public:
ScalableButton* edit_btn { nullptr };
enum LabelItemType {
LABEL_ITEM_MARKER = 0x4d,
LABEL_ITEM_CONFIG_WIZARD = 0x4e
LABEL_ITEM_MARKER = 0xffffff01,
LABEL_ITEM_WIZARD_PRINTERS,
LABEL_ITEM_WIZARD_FILAMENTS,
LABEL_ITEM_WIZARD_MATERIALS,
LABEL_ITEM_MAX,
};
void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER);
@ -183,6 +187,7 @@ public:
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
void reload_from_disk();
bool has_toolpaths_to_export() const;
void export_toolpaths_to_obj() const;
void reslice();
@ -249,6 +254,7 @@ public:
bool can_copy_to_clipboard() const;
bool can_undo() const;
bool can_redo() const;
bool can_reload_from_disk() const;
void msw_rescale();

View File

@ -99,6 +99,9 @@ static const std::unordered_map<std::string, std::string> pre_family_model_map {
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
{
static const std::string printer_model_key = "printer_model:";
static const std::string filaments_section = "default_filaments";
static const std::string materials_section = "default_sla_materials";
const std::string id = path.stem().string();
if (! boost::filesystem::exists(path)) {
@ -107,6 +110,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
VendorProfile res(id);
// Helper to get compulsory fields
auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator
{
auto res = tree.find(key);
@ -116,6 +120,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
return res;
};
// Load the header
const auto &vendor_section = get_or_throw(tree, "vendor")->second;
res.name = get_or_throw(vendor_section, "name")->second.data();
@ -127,6 +132,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
res.config_version = std::move(*config_version);
}
// Load URLs
const auto config_update_url = vendor_section.find("config_update_url");
if (config_update_url != vendor_section.not_found()) {
res.config_update_url = config_update_url->second.data();
@ -141,6 +147,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
return res;
}
// Load printer models
for (auto &section : tree) {
if (boost::starts_with(section.first, printer_model_key)) {
VendorProfile::PrinterModel model;
@ -182,6 +189,24 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
}
}
// Load filaments and sla materials to be installed by default
const auto filaments = tree.find(filaments_section);
if (filaments != tree.not_found()) {
for (auto &pair : filaments->second) {
if (pair.second.data() == "1") {
res.default_filaments.insert(pair.first);
}
}
}
const auto materials = tree.find(materials_section);
if (materials != tree.not_found()) {
for (auto &pair : materials->second) {
if (pair.second.data() == "1") {
res.default_sla_materials.insert(pair.first);
}
}
}
return res;
}
@ -351,10 +376,17 @@ bool Preset::update_compatible(const Preset &active_printer, const DynamicPrintC
void Preset::set_visible_from_appconfig(const AppConfig &app_config)
{
if (vendor == nullptr) { return; }
const std::string &model = config.opt_string("printer_model");
const std::string &variant = config.opt_string("printer_variant");
if (model.empty() || variant.empty()) { return; }
is_visible = app_config.get_variant(vendor->id, model, variant);
if (type == TYPE_PRINTER) {
const std::string &model = config.opt_string("printer_model");
const std::string &variant = config.opt_string("printer_variant");
if (model.empty() || variant.empty()) { return; }
is_visible = app_config.get_variant(vendor->id, model, variant);
} else if (type == TYPE_FILAMENT) {
is_visible = app_config.has("filaments", name);
} else if (type == TYPE_SLA_MATERIAL) {
is_visible = app_config.has("sla_materials", name);
}
}
const std::vector<std::string>& Preset::print_options()
@ -404,7 +436,7 @@ const std::vector<std::string>& Preset::filament_options()
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
@ -501,11 +533,13 @@ const std::vector<std::string>& Preset::sla_material_options()
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"material_type",
"initial_layer_height",
"exposure_time",
"initial_exposure_time",
"material_correction",
"material_notes",
"material_vendor",
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"
@ -1054,7 +1088,9 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_CONFIG_WIZARD);
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS);
} else if (m_type == Preset::TYPE_SLA_MATERIAL) {
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS);
}
ui->SetSelection(selected_preset_item);
@ -1300,7 +1336,7 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name)
}
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors)
std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const VendorMap &new_vendors)
{
std::vector<std::string> duplicates;
for (Preset &preset : other.m_presets) {
@ -1311,9 +1347,9 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
if (it == m_presets.end() || it->name != preset.name) {
if (preset.vendor != nullptr) {
// Re-assign a pointer to the vendor structure in the new PresetBundle.
auto it = new_vendors.find(*preset.vendor);
auto it = new_vendors.find(preset.vendor->id);
assert(it != new_vendors.end());
preset.vendor = &(*it);
preset.vendor = &it->second;
}
this->m_presets.emplace(it, std::move(preset));
} else

View File

@ -2,6 +2,8 @@
#define slic3r_Preset_hpp_
#include <deque>
#include <set>
#include <unordered_map>
#include <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree_fwd.hpp>
@ -71,9 +73,14 @@ public:
};
std::vector<PrinterModel> models;
std::set<std::string> default_filaments;
std::set<std::string> default_sla_materials;
VendorProfile() {}
VendorProfile(std::string id) : id(std::move(id)) {}
// Load VendorProfile from an ini file.
// If `load_all` is false, only the header with basic info (name, version, URLs) is loaded.
static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
@ -84,6 +91,12 @@ public:
bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }
};
// Note: it is imporant that map is used here rather than unordered_map,
// because we need iterators to not be invalidated,
// because Preset and the ConfigWizard hold pointers to VendorProfiles.
// XXX: maybe set is enough (cf. changes in Wizard)
typedef std::map<std::string, VendorProfile> VendorMap;
class Preset
{
public:
@ -433,7 +446,7 @@ protected:
bool select_preset_by_name_strict(const std::string &name);
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors);
std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
private:
PresetCollection();

View File

@ -8,6 +8,7 @@
#include <algorithm>
#include <fstream>
#include <unordered_set>
#include <boost/filesystem.hpp>
#include <boost/algorithm/clamp.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -41,6 +42,8 @@ static std::vector<std::string> s_project_options {
"wiping_volumes_matrix"
};
const char *PresetBundle::PRUSA_BUNDLE = "PrusaResearch";
PresetBundle::PresetBundle() :
prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())),
filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())),
@ -194,7 +197,7 @@ void PresetBundle::setup_directories()
}
}
void PresetBundle::load_presets(const AppConfig &config, const std::string &preferred_model_id)
void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_model_id)
{
// First load the vendor specific system presets.
std::string errors_cummulative = this->load_system_presets();
@ -325,13 +328,71 @@ void PresetBundle::load_installed_printers(const AppConfig &config)
}
}
void PresetBundle::load_installed_filaments(AppConfig &config)
{
if (! config.has_section(AppConfig::SECTION_FILAMENTS)) {
std::unordered_set<const Preset*> comp_filaments;
for (const Preset &printer : printers) {
if (! printer.is_visible || printer.printer_technology() != ptFFF) {
continue;
}
for (const Preset &filament : filaments) {
if (filament.is_compatible_with_printer(printer)) {
comp_filaments.insert(&filament);
}
}
}
for (const auto &filament: comp_filaments) {
config.set(AppConfig::SECTION_FILAMENTS, filament->name, "1");
}
}
for (auto &preset : filaments) {
preset.set_visible_from_appconfig(config);
}
}
void PresetBundle::load_installed_sla_materials(AppConfig &config)
{
if (! config.has_section(AppConfig::SECTION_MATERIALS)) {
std::unordered_set<const Preset*> comp_sla_materials;
for (const Preset &printer : printers) {
if (! printer.is_visible || printer.printer_technology() != ptSLA) {
continue;
}
for (const Preset &material : sla_materials) {
if (material.is_compatible_with_printer(printer)) {
comp_sla_materials.insert(&material);
}
}
}
for (const auto &material: comp_sla_materials) {
config.set(AppConfig::SECTION_MATERIALS, material->name, "1");
}
}
for (auto &preset : sla_materials) {
preset.set_visible_from_appconfig(config);
}
}
// Load selections (current print, current filaments, current printer) from config.ini
// This is done on application start up or after updates are applied.
void PresetBundle::load_selections(const AppConfig &config, const std::string &preferred_model_id)
void PresetBundle::load_selections(AppConfig &config, const std::string &preferred_model_id)
{
// Update visibility of presets based on application vendor / model / variant configuration.
this->load_installed_printers(config);
// Update visibility of filament and sla material presets
this->load_installed_filaments(config);
this->load_installed_sla_materials(config);
// Parse the initial print / filament / printer profile names.
std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print"));
std::string initial_sla_print_profile_name = remove_ini_suffix(config.get("presets", "sla_print"));
@ -1032,9 +1093,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
auto vp = VendorProfile::from_ini(tree, path);
if (vp.num_variants() == 0)
return 0;
vendor_profile = &(*this->vendors.insert(vp).first);
vendor_profile = &this->vendors.insert({vp.id, vp}).first->second;
}
if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) {
return 0;
}
@ -1572,6 +1633,9 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
selected_preset_item = ui->GetCount() - 1;
}
}
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS);
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->check_selection();

View File

@ -4,7 +4,9 @@
#include "AppConfig.hpp"
#include "Preset.hpp"
#include <memory>
#include <set>
#include <unordered_map>
#include <boost/filesystem/path.hpp>
class wxWindow;
@ -31,7 +33,7 @@ public:
// Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets.
// Load selections (current print, current filaments, current printer) from config.ini
// This is done just once on application start up.
void load_presets(const AppConfig &config, const std::string &preferred_model_id = "");
void load_presets(AppConfig &config, const std::string &preferred_model_id = "");
// Export selections (current print, current filaments, current printer) into config.ini
void export_selections(AppConfig &config);
@ -52,7 +54,8 @@ public:
// There will be an entry for each system profile loaded,
// and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors.
std::set<VendorProfile> vendors;
// std::set<VendorProfile> vendors;
VendorMap vendors;
struct ObsoletePresets {
std::vector<std::string> prints;
@ -131,19 +134,25 @@ public:
void load_default_preset_bitmaps(wxWindow *window);
// Set the is_visible flag for printer vendors, printer models and printer variants
// based on the user configuration.
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
void load_installed_printers(const AppConfig &config);
static const char *PRUSA_BUNDLE;
private:
std::string load_system_presets();
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> merge_presets(PresetBundle &&other);
// Set the "enabled" flag for printer vendors, printer models and printer variants
// based on the user configuration.
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
void load_installed_printers(const AppConfig &config);
// Set the is_visible flag for filaments and sla materials,
// apply defaults based on enabled printers when no filaments/materials are installed.
void load_installed_filaments(AppConfig &config);
void load_installed_sla_materials(AppConfig &config);
// Load selections (current print, current filaments, current printer) from config.ini
// This is done just once on application start up.
void load_selections(const AppConfig &config, const std::string &preferred_model_id = "");
void load_selections(AppConfig &config, const std::string &preferred_model_id = "");
// Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path.
// and the external config is just referenced, not stored into user profile directory.

View File

@ -472,7 +472,7 @@ void Selection::volumes_changed(const std::vector<size_t> &map_volume_old_to_new
for (unsigned int idx : m_list)
if (map_volume_old_to_new[idx] != size_t(-1)) {
unsigned int new_idx = (unsigned int)map_volume_old_to_new[idx];
assert((*m_volumes)[new_idx]->selected);
(*m_volumes)[new_idx]->selected = true;
list_new.insert(new_idx);
}
m_list = std::move(list_new);

View File

@ -227,9 +227,9 @@ void Tab::create_preset_tab()
m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this);
m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) {
//! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox,
//! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox,
//! but the OSX version derived from wxOwnerDrawnCombo, instead of:
//! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data());
//! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data());
//! we doing next:
int selected_item = m_presets_choice->GetSelection();
if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty())
@ -241,7 +241,7 @@ void Tab::create_preset_tab()
selected_string == "------- User presets -------"*/) {
m_presets_choice->SetSelection(m_selected_preset_item);
if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer")))
wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); });
wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER); });
return;
}
m_selected_preset_item = selected_item;

View File

@ -455,7 +455,7 @@ Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr;
std::vector<wxBitmap*> bmps;
std::vector<std::string> colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config();
if (bmps.empty())
if (colors.empty())
return bmps;
unsigned char rgb[3];

View File

@ -153,7 +153,7 @@ struct PresetUpdater::priv
bool get_file(const std::string &url, const fs::path &target_path) const;
void prune_tmps() const;
void sync_version() const;
void sync_config(const std::set<VendorProfile> vendors);
void sync_config(const VendorMap vendors);
void check_install_indices() const;
Updates get_config_updates() const;
@ -266,7 +266,7 @@ void PresetUpdater::priv::sync_version() const
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
void PresetUpdater::priv::sync_config(const VendorMap vendors)
{
BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
@ -276,13 +276,13 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
for (auto &index : index_db) {
if (cancel) { return; }
const auto vendor_it = vendors.find(VendorProfile(index.vendor()));
const auto vendor_it = vendors.find(index.vendor());
if (vendor_it == vendors.end()) {
BOOST_LOG_TRIVIAL(warning) << "No such vendor: " << index.vendor();
continue;
}
const VendorProfile &vendor = *vendor_it;
const VendorProfile &vendor = vendor_it->second;
if (vendor.config_update_url.empty()) {
BOOST_LOG_TRIVIAL(info) << "Vendor has no config_update_url: " << vendor.name;
continue;
@ -574,7 +574,7 @@ void PresetUpdater::sync(PresetBundle *preset_bundle)
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
std::set<VendorProfile> vendors = preset_bundle->vendors;
VendorMap vendors = preset_bundle->vendors;
p->thread = std::move(std::thread([this, vendors]() {
this->p->prune_tmps();
@ -643,13 +643,10 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
// (snapshot is taken beforehand)
p->perform_updates(std::move(updates));
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) {
if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) {
return R_INCOMPAT_EXIT;
}
GUI::wxGetApp().load_current_presets();
return R_INCOMPAT_CONFIGURED;
} else {
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
@ -694,8 +691,8 @@ void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool
BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size();
for (const auto &bundle : bundles) {
auto path_in_rsrc = p->rsrc_path / bundle;
auto path_in_vendors = p->vendor_path / bundle;
auto path_in_rsrc = (p->rsrc_path / bundle).replace_extension(".ini");
auto path_in_vendors = (p->vendor_path / bundle).replace_extension(".ini");
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", "");
}

View File

@ -11,7 +11,7 @@ add_library(Catch2::Catch2 ALIAS Catch2)
include(Catch)
add_library(test_catch2_common INTERFACE)
target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE)
target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2)
add_library(test_common INTERFACE)

View File

@ -1,5 +1,6 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <fstream>
#include <libnest2d.h>
@ -224,12 +225,12 @@ TEST_CASE("Area", "[Geometry]") {
using namespace libnest2d;
RectangleItem rect(10, 10);
REQUIRE(rect.area() == 100);
REQUIRE(rect.area() == Approx(100));
RectangleItem rect2 = {100, 100};
REQUIRE(rect2.area() == 10000);
REQUIRE(rect2.area() == Approx(10000));
Item item = {
{61, 97},
@ -288,11 +289,9 @@ TEST_CASE("IsPointInsidePolygon", "[Geometry]") {
// REQUIRE_FALSE(ShapeLike::intersects(s1, s2));
//}
// Simple TEST_CASE, does not use gmock
TEST_CASE("LeftAndDownPolygon", "[Geometry]")
{
using namespace libnest2d;
using namespace libnest2d;
Box bin(100, 100);
BottomLeftPlacer placer(bin);
@ -339,7 +338,6 @@ TEST_CASE("LeftAndDownPolygon", "[Geometry]")
}
}
// Simple TEST_CASE, does not use gmock
TEST_CASE("ArrangeRectanglesTight", "[Nesting]")
{
using namespace libnest2d;
@ -449,8 +447,8 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
[](const Item &i1, const Item &i2) {
return i1.binId() < i2.binId();
});
size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1;
auto groups = size_t(max_group == rects.end() ? 0 : max_group->binId() + 1);
REQUIRE(groups == 1u);
REQUIRE(
@ -477,8 +475,6 @@ using namespace libnest2d;
template<long long SCALE = 1, class Bin>
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin, int idx = 0) {
std::string loc = "out";
static std::string svg_header =
@ -494,20 +490,20 @@ void exportSVG(std::vector<std::reference_wrapper<Item>>& result, const Bin& bin
if(out.is_open()) {
out << svg_header;
Item rbin( RectangleItem(bin.width(), bin.height()) );
for(unsigned i = 0; i < rbin.vertexCount(); i++) {
auto v = rbin.vertex(i);
for(unsigned j = 0; j < rbin.vertexCount(); j++) {
auto v = rbin.vertex(j);
setY(v, -getY(v)/SCALE + 500 );
setX(v, getX(v)/SCALE);
rbin.setVertex(i, v);
rbin.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(Item& sh : r) {
Item tsh(sh.transformedShape());
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
auto v = tsh.vertex(i);
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
auto v = tsh.vertex(j);
setY(v, -getY(v)/SCALE + 500);
setX(v, getX(v)/SCALE);
tsh.setVertex(i, v);
tsh.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
@ -570,7 +566,7 @@ TEST_CASE("convexHull", "[Geometry]") {
REQUIRE(chull.size() == poly.size());
}
TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
// Get the input items and define the bin.
std::vector<Item> input = prusaParts();
@ -579,21 +575,15 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
// Do the nesting. Check in each step if the remaining items are less than
// in the previous step. (Some algorithms can place more items in one step)
size_t pcount = input.size();
libnest2d::nest(input, bin, [&pcount](unsigned cnt) {
REQUIRE(cnt < pcount);
pcount = cnt;
});
// Get the number of logical bins: search for the max binId...
auto max_binid_it = std::max_element(input.begin(), input.end(),
[](const Item &i1, const Item &i2) {
return i1.binId() < i2.binId();
});
auto bins = size_t(max_binid_it == input.end() ? 0 :
max_binid_it->binId() + 1);
size_t bins = libnest2d::nest(input, bin, 0, {},
ProgressFunction{[&pcount](unsigned cnt) {
REQUIRE(cnt < pcount);
pcount = cnt;
}});
// For prusa parts, 2 bins should be enough...
REQUIRE(bins > 0u);
REQUIRE(bins <= 2u);
// All parts should be processed by the algorithm
@ -617,29 +607,83 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
}
}
TEST_CASE("NestEmptyItemShouldBeUntouched", "[Nesting]") {
TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") {
auto bin = Box(250000000, 210000000); // dummy bin
std::vector<Item> items;
items.emplace_back(Item{}); // Emplace empty item
items.emplace_back(Item{0, 200, 0}); // Emplace zero area item
libnest2d::nest(items, bin);
size_t bins = libnest2d::nest(items, bin);
REQUIRE(bins == 0u);
for (auto &itm : items) REQUIRE(itm.binId() == BIN_ID_UNSET);
}
TEST_CASE("NestLargeItemShouldBeUntouched", "[Nesting]") {
TEST_CASE("LargeItemShouldBeUntouched", "[Nesting]") {
auto bin = Box(250000000, 210000000); // dummy bin
std::vector<Item> items;
items.emplace_back(RectangleItem{250000001, 210000001}); // Emplace large item
libnest2d::nest(items, bin);
size_t bins = libnest2d::nest(items, bin);
REQUIRE(bins == 0u);
REQUIRE(items.front().binId() == BIN_ID_UNSET);
}
TEST_CASE("Items can be preloaded", "[Nesting]") {
auto bin = Box({0, 0}, {250000000, 210000000}); // dummy bin
std::vector<Item> items;
items.reserve(2);
NestConfig<> cfg;
cfg.placer_config.alignment = NestConfig<>::Placement::Alignment::DONT_ALIGN;
items.emplace_back(RectangleItem{10000000, 10000000});
Item &fixed_rect = items.back();
fixed_rect.translate(bin.center());
items.emplace_back(RectangleItem{20000000, 20000000});
Item &movable_rect = items.back();
movable_rect.translate(bin.center());
SECTION("Preloaded Item should be untouched") {
fixed_rect.markAsFixedInBin(0);
size_t bins = libnest2d::nest(items, bin, 0, cfg);
REQUIRE(bins == 1);
REQUIRE(fixed_rect.binId() == 0);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
REQUIRE(movable_rect.binId() == 0);
REQUIRE(movable_rect.translation().X != bin.center().X);
REQUIRE(movable_rect.translation().Y != bin.center().Y);
}
SECTION("Preloaded Item should not affect free bins") {
fixed_rect.markAsFixedInBin(1);
size_t bins = libnest2d::nest(items, bin, 0, cfg);
REQUIRE(bins == 2);
REQUIRE(fixed_rect.binId() == 1);
REQUIRE(fixed_rect.translation().X == bin.center().X);
REQUIRE(fixed_rect.translation().Y == bin.center().Y);
REQUIRE(movable_rect.binId() == 0);
auto bb = movable_rect.boundingBox();
REQUIRE(bb.center().X == bin.center().X);
REQUIRE(bb.center().Y == bin.center().Y);
}
}
namespace {
struct ItemPair {
@ -969,8 +1013,8 @@ TEST_CASE("mergePileWithPolygon", "[Geometry]") {
REQUIRE(result.size() == 1);
RectangleItem ref(45, 15);
REQUIRE(shapelike::area(result.front()) == ref.area());
REQUIRE(shapelike::area(result.front()) == Approx(ref.area()));
}
namespace {