Print.cpp 158 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097
  1. #include "clipper/clipper_z.hpp"
  2. #include "Exception.hpp"
  3. #include "Print.hpp"
  4. #include "BoundingBox.hpp"
  5. #include "ClipperUtils.hpp"
  6. #include "Extruder.hpp"
  7. #include "Flow.hpp"
  8. #include "Fill/FillBase.hpp"
  9. #include "Geometry.hpp"
  10. #include "I18N.hpp"
  11. #include "ShortestPath.hpp"
  12. #include "SupportMaterial.hpp"
  13. #include "Thread.hpp"
  14. #include "GCode.hpp"
  15. #include "GCode/WipeTower.hpp"
  16. #include "Utils.hpp"
  17. //#include "PrintExport.hpp"
  18. #include <float.h>
  19. #include <algorithm>
  20. #include <limits>
  21. #include <unordered_set>
  22. #include <boost/filesystem/path.hpp>
  23. #include <boost/format.hpp>
  24. #include <boost/log/trivial.hpp>
  25. // Mark string for localization and translate.
  26. #define L(s) Slic3r::I18N::translate(s)
  27. namespace Slic3r {
  28. template class PrintState<PrintStep, psCount>;
  29. template class PrintState<PrintObjectStep, posCount>;
  30. void Print::clear()
  31. {
  32. tbb::mutex::scoped_lock lock(this->state_mutex());
  33. // The following call should stop background processing if it is running.
  34. this->invalidate_all_steps();
  35. for (PrintObject *object : m_objects)
  36. delete object;
  37. m_objects.clear();
  38. for (PrintRegion *region : m_regions)
  39. delete region;
  40. m_regions.clear();
  41. m_model.clear_objects();
  42. }
  43. //PrintRegion* Print::add_region()
  44. //{
  45. // m_regions.emplace_back(new PrintRegion(this));
  46. // return m_regions.back();
  47. //}
  48. PrintRegion* Print::add_region(const PrintRegionConfig &config)
  49. {
  50. m_regions.emplace_back(new PrintRegion(this, config));
  51. return m_regions.back();
  52. }
  53. // Called by Print::apply().
  54. // This method only accepts PrintConfig option keys.
  55. bool Print::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
  56. {
  57. if (opt_keys.empty())
  58. return false;
  59. // Cache the plenty of parameters, which influence the G-code generator only,
  60. // or they are only notes not influencing the generated G-code.
  61. static std::unordered_set<std::string> steps_gcode = {
  62. "avoid_crossing_perimeters",
  63. "avoid_crossing_perimeters_max_detour",
  64. "avoid_crossing_not_first_layer",
  65. "bed_shape",
  66. "bed_temperature",
  67. "chamber_temperature",
  68. "before_layer_gcode",
  69. "between_objects_gcode",
  70. "bridge_acceleration",
  71. "bridge_internal_acceleration",
  72. "bridge_fan_speed",
  73. "bridge_internal_fan_speed",
  74. "colorprint_heights",
  75. "complete_objects_sort",
  76. "cooling",
  77. "default_acceleration",
  78. "deretract_speed",
  79. "disable_fan_first_layers",
  80. "duplicate_distance",
  81. "end_gcode",
  82. "end_filament_gcode",
  83. "external_perimeter_acceleration",
  84. "external_perimeter_cut_corners",
  85. "external_perimeter_fan_speed",
  86. "extrusion_axis",
  87. "extruder_clearance_height",
  88. "extruder_clearance_radius",
  89. "extruder_colour",
  90. "extruder_offset",
  91. "extruder_fan_offset"
  92. "extruder_temperature_offset",
  93. "extrusion_multiplier",
  94. "fan_always_on",
  95. "fan_below_layer_time",
  96. "fan_kickstart",
  97. "fan_speedup_overhangs",
  98. "fan_speedup_time",
  99. "fan_percentage",
  100. "filament_colour",
  101. "filament_custom_variables",
  102. "filament_diameter",
  103. "filament_density",
  104. "filament_notes",
  105. "filament_cost",
  106. "filament_spool_weight",
  107. "first_layer_acceleration",
  108. "first_layer_bed_temperature",
  109. "first_layer_flow_ratio",
  110. "first_layer_speed",
  111. "first_layer_infill_speed",
  112. "first_layer_min_speed",
  113. "full_fan_speed_layer",
  114. "gap_fill_acceleration",
  115. "gap_fill_speed",
  116. "gcode_comments",
  117. "gcode_filename_illegal_char",
  118. "gcode_label_objects",
  119. "gcode_precision_xyz",
  120. "gcode_precision_e",
  121. "infill_acceleration",
  122. "ironing_acceleration",
  123. "layer_gcode",
  124. "max_fan_speed",
  125. "max_gcode_per_second",
  126. "max_print_height",
  127. "max_print_speed",
  128. "max_volumetric_speed",
  129. "min_fan_speed",
  130. "min_length",
  131. "min_print_speed",
  132. "milling_toolchange_end_gcode",
  133. "milling_toolchange_start_gcode",
  134. "milling_offset",
  135. "milling_z_offset",
  136. "milling_z_lift",
  137. #ifdef HAS_PRESSURE_EQUALIZER
  138. "max_volumetric_extrusion_rate_slope_positive",
  139. "max_volumetric_extrusion_rate_slope_negative",
  140. #endif /* HAS_PRESSURE_EQUALIZER */
  141. "notes",
  142. "only_retract_when_crossing_perimeters",
  143. "output_filename_format",
  144. "overhangs_acceleration",
  145. "perimeter_acceleration",
  146. "post_process",
  147. "printer_notes",
  148. "retract_before_travel",
  149. "retract_before_wipe",
  150. "retract_layer_change",
  151. "retract_length",
  152. "retract_length_toolchange",
  153. "retract_lift",
  154. "retract_lift_above",
  155. "retract_lift_below",
  156. "retract_lift_first_layer",
  157. "retract_lift_top",
  158. "retract_restart_extra",
  159. "retract_restart_extra_toolchange",
  160. "retract_speed",
  161. "single_extruder_multi_material_priming",
  162. "slowdown_below_layer_time",
  163. "solid_infill_acceleration",
  164. "support_material_acceleration",
  165. "support_material_interface_acceleration",
  166. "standby_temperature_delta",
  167. "start_gcode",
  168. "start_gcode_manual",
  169. "start_filament_gcode",
  170. "thin_walls_acceleration",
  171. "thin_walls_speed",
  172. "time_estimation_compensation",
  173. "tool_name",
  174. "toolchange_gcode",
  175. "top_fan_speed",
  176. "top_solid_infill_acceleration",
  177. "threads",
  178. "travel_acceleration",
  179. "travel_deceleration_use_target",
  180. "travel_speed",
  181. "travel_speed_z",
  182. "use_firmware_retraction",
  183. "use_relative_e_distances",
  184. "use_volumetric_e",
  185. "variable_layer_height",
  186. "wipe",
  187. "wipe_speed",
  188. "wipe_extra_perimeter"
  189. };
  190. static std::unordered_set<std::string> steps_ignore;
  191. std::vector<PrintStep> steps;
  192. std::vector<PrintObjectStep> osteps;
  193. bool invalidated = false;
  194. for (const t_config_option_key &opt_key : opt_keys) {
  195. if (steps_gcode.find(opt_key) != steps_gcode.end()) {
  196. // These options only affect G-code export or they are just notes without influence on the generated G-code,
  197. // so there is nothing to invalidate.
  198. steps.emplace_back(psGCodeExport);
  199. } else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
  200. // These steps have no influence on the G-code whatsoever. Just ignore them.
  201. } else if (
  202. opt_key == "skirts"
  203. || opt_key == "skirt_height"
  204. || opt_key == "draft_shield"
  205. || opt_key == "skirt_brim"
  206. || opt_key == "skirt_distance"
  207. || opt_key == "skirt_distance_from_brim"
  208. || opt_key == "min_skirt_length"
  209. || opt_key == "complete_objects_one_skirt"
  210. || opt_key == "complete_objects_one_brim"
  211. || opt_key == "ooze_prevention"
  212. || opt_key == "wipe_tower_x"
  213. || opt_key == "wipe_tower_y"
  214. || opt_key == "wipe_tower_rotation_angle") {
  215. steps.emplace_back(psSkirt);
  216. } else if (
  217. opt_key == "complete_objects") {
  218. steps.emplace_back(psBrim);
  219. steps.emplace_back(psSkirt);
  220. steps.emplace_back(psWipeTower);
  221. } else if (
  222. opt_key == "brim_inside_holes"
  223. || opt_key == "brim_width"
  224. || opt_key == "brim_width_interior"
  225. || opt_key == "brim_offset"
  226. || opt_key == "brim_ears"
  227. || opt_key == "brim_ears_detection_length"
  228. || opt_key == "brim_ears_max_angle"
  229. || opt_key == "brim_ears_pattern") {
  230. steps.emplace_back(psBrim);
  231. steps.emplace_back(psSkirt);
  232. } else if (
  233. opt_key == "filament_shrink"
  234. || opt_key == "nozzle_diameter"
  235. || opt_key == "model_precision"
  236. || opt_key == "resolution"
  237. || opt_key == "resolution_internal"
  238. || opt_key == "slice_closing_radius"
  239. // Spiral Vase forces different kind of slicing than the normal model:
  240. // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
  241. // Therefore toggling the Spiral Vase on / off requires complete reslicing.
  242. || opt_key == "spiral_vase"
  243. || opt_key == "z_step") {
  244. osteps.emplace_back(posSlice);
  245. } else if (
  246. opt_key == "filament_type"
  247. || opt_key == "filament_soluble"
  248. || opt_key == "first_layer_temperature"
  249. || opt_key == "filament_loading_speed"
  250. || opt_key == "filament_loading_speed_start"
  251. || opt_key == "filament_unloading_speed"
  252. || opt_key == "filament_unloading_speed_start"
  253. || opt_key == "filament_toolchange_delay"
  254. || opt_key == "filament_cooling_moves"
  255. || opt_key == "filament_minimal_purge_on_wipe_tower"
  256. || opt_key == "filament_cooling_initial_speed"
  257. || opt_key == "filament_cooling_final_speed"
  258. || opt_key == "filament_ramming_parameters"
  259. || opt_key == "filament_max_speed"
  260. || opt_key == "filament_max_volumetric_speed"
  261. || opt_key == "filament_use_skinnydip" // skinnydip params start
  262. || opt_key == "filament_use_fast_skinnydip"
  263. || opt_key == "filament_skinnydip_distance"
  264. || opt_key == "filament_melt_zone_pause"
  265. || opt_key == "filament_cooling_zone_pause"
  266. || opt_key == "filament_toolchange_temp"
  267. || opt_key == "filament_enable_toolchange_temp"
  268. || opt_key == "filament_enable_toolchange_part_fan"
  269. || opt_key == "filament_toolchange_part_fan_speed"
  270. || opt_key == "filament_dip_insertion_speed"
  271. || opt_key == "filament_dip_extraction_speed" //skinnydip params end
  272. || opt_key == "gcode_flavor"
  273. || opt_key == "high_current_on_filament_swap"
  274. || opt_key == "infill_first"
  275. || opt_key == "single_extruder_multi_material"
  276. || opt_key == "temperature"
  277. || opt_key == "wipe_tower"
  278. || opt_key == "wipe_tower_width"
  279. || opt_key == "wipe_tower_bridging"
  280. || opt_key == "wipe_tower_no_sparse_layers"
  281. || opt_key == "wiping_volumes_matrix"
  282. || opt_key == "parking_pos_retraction"
  283. || opt_key == "cooling_tube_retraction"
  284. || opt_key == "cooling_tube_length"
  285. || opt_key == "extra_loading_move"
  286. || opt_key == "z_offset"
  287. || opt_key == "wipe_tower_brim") {
  288. steps.emplace_back(psWipeTower);
  289. steps.emplace_back(psSkirt);
  290. }
  291. else if (
  292. opt_key == "first_layer_extrusion_width"
  293. || opt_key == "min_layer_height"
  294. || opt_key == "max_layer_height"
  295. || opt_key == "filament_max_overlap") {
  296. osteps.emplace_back(posPerimeters);
  297. osteps.emplace_back(posInfill);
  298. osteps.emplace_back(posSupportMaterial);
  299. steps.emplace_back(psSkirt);
  300. steps.emplace_back(psBrim);
  301. }
  302. else if (opt_key == "posSlice")
  303. osteps.emplace_back(posSlice);
  304. else if (opt_key == "posPerimeters")
  305. osteps.emplace_back(posPerimeters);
  306. else if (opt_key == "posPrepareInfill")
  307. osteps.emplace_back(posPrepareInfill);
  308. else if (opt_key == "posInfill")
  309. osteps.emplace_back(posInfill);
  310. else if (opt_key == "posSupportMaterial")
  311. osteps.emplace_back(posSupportMaterial);
  312. else if (opt_key == "posCount")
  313. osteps.emplace_back(posCount);
  314. else {
  315. // for legacy, if we can't handle this option let's invalidate all steps
  316. //FIXME invalidate all steps of all objects as well?
  317. invalidated |= this->invalidate_all_steps();
  318. // Continue with the other opt_keys to possibly invalidate any object specific steps.
  319. }
  320. }
  321. sort_remove_duplicates(steps);
  322. for (PrintStep step : steps)
  323. invalidated |= this->invalidate_step(step);
  324. sort_remove_duplicates(osteps);
  325. for (PrintObjectStep ostep : osteps)
  326. for (PrintObject *object : m_objects)
  327. invalidated |= object->invalidate_step(ostep);
  328. return invalidated;
  329. }
  330. bool Print::invalidate_step(PrintStep step)
  331. {
  332. bool invalidated = Inherited::invalidate_step(step);
  333. // Propagate to dependent steps.
  334. if (step == psSkirt)
  335. invalidated |= Inherited::invalidate_step(psBrim);
  336. if (step == psBrim) // this one only if skirt_distance_from_brim
  337. invalidated |= Inherited::invalidate_step(psSkirt);
  338. if (step != psGCodeExport)
  339. invalidated |= Inherited::invalidate_step(psGCodeExport);
  340. return invalidated;
  341. }
  342. // returns true if an object step is done on all objects
  343. // and there's at least one object
  344. bool Print::is_step_done(PrintObjectStep step) const
  345. {
  346. if (m_objects.empty())
  347. return false;
  348. tbb::mutex::scoped_lock lock(this->state_mutex());
  349. for (const PrintObject *object : m_objects)
  350. if (! object->is_step_done_unguarded(step))
  351. return false;
  352. return true;
  353. }
  354. // returns 0-based indices of used extruders
  355. std::set<uint16_t> Print::object_extruders(const PrintObjectPtrs &objects) const
  356. {
  357. std::set<uint16_t> extruders;
  358. std::vector<unsigned char> region_used(m_regions.size(), false);
  359. for (const PrintObject *object : objects)
  360. for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes)
  361. if (! volumes_per_region.empty())
  362. region_used[&volumes_per_region - &object->region_volumes.front()] = true;
  363. for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region)
  364. if (region_used[idx_region])
  365. m_regions[idx_region]->collect_object_printing_extruders(extruders);
  366. return extruders;
  367. }
  368. // returns 0-based indices of used extruders
  369. std::set<uint16_t> Print::support_material_extruders() const
  370. {
  371. std::set<uint16_t> extruders;
  372. bool support_uses_current_extruder = false;
  373. auto num_extruders = (uint16_t)m_config.nozzle_diameter.size();
  374. for (PrintObject *object : m_objects) {
  375. if (object->has_support_material()) {
  376. assert(object->config().support_material_extruder >= 0);
  377. if (object->config().support_material_extruder == 0)
  378. support_uses_current_extruder = true;
  379. else {
  380. uint16_t i = (uint16_t)object->config().support_material_extruder - 1;
  381. extruders.insert((i >= num_extruders) ? 0 : i);
  382. }
  383. if (object->config().support_material_interface_layers > 0) {
  384. assert(object->config().support_material_interface_extruder >= 0);
  385. if (object->config().support_material_interface_extruder == 0)
  386. support_uses_current_extruder = true;
  387. else {
  388. uint16_t i = (uint16_t)object->config().support_material_interface_extruder - 1;
  389. extruders.insert((i >= num_extruders) ? 0 : i);
  390. }
  391. }
  392. }
  393. }
  394. if (support_uses_current_extruder)
  395. // Add all object extruders to the support extruders as it is not know which one will be used to print supports.
  396. append(extruders, this->object_extruders(m_objects));
  397. return extruders;
  398. }
  399. // returns 0-based indices of used extruders
  400. std::set<uint16_t> Print::extruders() const
  401. {
  402. std::set<uint16_t> extruders = this->object_extruders(m_objects);
  403. append(extruders, this->support_material_extruders());
  404. return extruders;
  405. }
  406. uint16_t Print::num_object_instances() const
  407. {
  408. uint16_t instances = 0;
  409. for (const PrintObject *print_object : m_objects)
  410. instances += (uint16_t)print_object->instances().size();
  411. return instances;
  412. }
  413. double Print::max_allowed_layer_height() const
  414. {
  415. double nozzle_diameter_max = 0.;
  416. for (unsigned int extruder_id : this->extruders())
  417. nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id));
  418. return nozzle_diameter_max;
  419. }
  420. // Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
  421. // in the exact order and with the same IDs.
  422. // It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
  423. void Print::model_volume_list_update_supports_seams(ModelObject &model_object_dst, const ModelObject &model_object_new)
  424. {
  425. typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
  426. std::vector<ModelVolumeWithStatus> old_volumes;
  427. old_volumes.reserve(model_object_dst.volumes.size());
  428. for (const ModelVolume *model_volume : model_object_dst.volumes)
  429. old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
  430. auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
  431. auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
  432. std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
  433. model_object_dst.volumes.clear();
  434. model_object_dst.volumes.reserve(model_object_new.volumes.size());
  435. for (const ModelVolume *model_volume_src : model_object_new.volumes) {
  436. ModelVolumeWithStatus key(model_volume_src, false);
  437. auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
  438. if (it != old_volumes.end() && model_volume_equal(*it, key)) {
  439. // The volume was found in the old list. Just copy it.
  440. assert(! it->second); // not consumed yet
  441. it->second = true;
  442. ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
  443. // For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
  444. assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
  445. model_object_dst.volumes.emplace_back(model_volume_dst);
  446. if (model_volume_dst->is_support_modifier() || model_volume_dst->is_seam_position()) {
  447. // For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
  448. model_volume_dst->set_type(model_volume_src->type());
  449. model_volume_dst->set_transformation(model_volume_src->get_transformation());
  450. }
  451. assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
  452. } else {
  453. // The volume was not found in the old list. Create a new copy.
  454. assert(model_volume_src->is_support_modifier() || model_volume_src->is_seam_position());
  455. model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
  456. model_object_dst.volumes.back()->set_model_object(&model_object_dst);
  457. }
  458. }
  459. // Release the non-consumed old volumes (those were deleted from the new list).
  460. for (ModelVolumeWithStatus &mv_with_status : old_volumes)
  461. if (! mv_with_status.second)
  462. delete mv_with_status.first;
  463. }
  464. static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
  465. {
  466. size_t i_src, i_dst;
  467. for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
  468. const ModelVolume &mv_src = *model_object_src.volumes[i_src];
  469. ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
  470. if (mv_src.type() != type) {
  471. ++ i_src;
  472. continue;
  473. }
  474. if (mv_dst.type() != type) {
  475. ++ i_dst;
  476. continue;
  477. }
  478. assert(mv_src.id() == mv_dst.id());
  479. // Copy the ModelVolume data.
  480. mv_dst.name = mv_src.name;
  481. mv_dst.config.assign_config(mv_src.config);
  482. assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
  483. mv_dst.supported_facets.assign(mv_src.supported_facets);
  484. assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
  485. mv_dst.seam_facets.assign(mv_src.seam_facets);
  486. //FIXME what to do with the materials?
  487. // mv_dst.m_material_id = mv_src.m_material_id;
  488. ++ i_src;
  489. ++ i_dst;
  490. }
  491. }
  492. static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
  493. {
  494. assert(lr_dst.size() == lr_src.size());
  495. auto it_src = lr_src.cbegin();
  496. for (auto &kvp_dst : lr_dst) {
  497. const auto &kvp_src = *it_src ++;
  498. assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
  499. assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
  500. // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
  501. // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
  502. kvp_dst.second = kvp_src.second;
  503. }
  504. }
  505. static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
  506. {
  507. typedef Transform3d::Scalar T;
  508. const T *lv = lhs.data();
  509. const T *rv = rhs.data();
  510. for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
  511. if (*lv < *rv)
  512. return true;
  513. else if (*lv > *rv)
  514. return false;
  515. }
  516. return false;
  517. }
  518. static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
  519. {
  520. typedef Transform3d::Scalar T;
  521. const T *lv = lhs.data();
  522. const T *rv = rhs.data();
  523. for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
  524. if (*lv != *rv)
  525. return false;
  526. return true;
  527. }
  528. struct PrintObjectTrafoAndInstances
  529. {
  530. Transform3d trafo;
  531. PrintInstances instances;
  532. bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
  533. };
  534. // Generate a list of trafos and XY offsets for instances of a ModelObject
  535. static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
  536. {
  537. std::set<PrintObjectTrafoAndInstances> trafos;
  538. PrintObjectTrafoAndInstances trafo;
  539. for (ModelInstance *model_instance : model_object.instances)
  540. if (model_instance->is_printable()) {
  541. trafo.trafo = model_instance->get_matrix();
  542. auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
  543. // Reset the XY axes of the transformation.
  544. trafo.trafo.data()[12] = 0;
  545. trafo.trafo.data()[13] = 0;
  546. // Search or insert a trafo.
  547. auto it = trafos.emplace(trafo).first;
  548. const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
  549. }
  550. return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
  551. }
  552. // Compare just the layer ranges and their layer heights, not the associated configs.
  553. // Ignore the layer heights if check_layer_heights is false.
  554. static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
  555. {
  556. if (lr1.size() != lr2.size())
  557. return false;
  558. auto it2 = lr2.begin();
  559. for (const auto &kvp1 : lr1) {
  560. const auto &kvp2 = *it2 ++;
  561. if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
  562. std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
  563. (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
  564. return false;
  565. }
  566. return true;
  567. }
  568. // Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
  569. static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
  570. {
  571. auto it_a = va.begin();
  572. auto it_b = vb.begin();
  573. while (it_a != va.end() || it_b != vb.end()) {
  574. if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
  575. // Skip any CustomGCode items, which are not tool changes.
  576. ++ it_a;
  577. continue;
  578. }
  579. if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
  580. // Skip any CustomGCode items, which are not tool changes.
  581. ++ it_b;
  582. continue;
  583. }
  584. if (it_a == va.end() || it_b == vb.end())
  585. // va or vb contains more Tool Changes than the other.
  586. return true;
  587. assert(it_a->type == CustomGCode::ToolChange);
  588. assert(it_b->type == CustomGCode::ToolChange);
  589. if (*it_a != *it_b)
  590. // The two Tool Changes differ.
  591. return true;
  592. ++ it_a;
  593. ++ it_b;
  594. }
  595. // There is no change in custom Tool Changes.
  596. return false;
  597. }
  598. // Collect diffs of configuration values at various containers,
  599. // resolve the filament rectract overrides of extruder retract values.
  600. void Print::config_diffs(
  601. const DynamicPrintConfig &new_full_config,
  602. t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys &region_diff,
  603. t_config_option_keys &full_config_diff,
  604. DynamicPrintConfig &filament_overrides) const
  605. {
  606. // Collect changes to print config, account for overrides of extruder retract values by filament presets.
  607. {
  608. const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
  609. const std::string filament_prefix = "filament_";
  610. for (const t_config_option_key &opt_key : m_config.keys()) {
  611. const ConfigOption *opt_old = m_config.option(opt_key);
  612. assert(opt_old != nullptr);
  613. const ConfigOption *opt_new = new_full_config.option(opt_key);
  614. // assert(opt_new != nullptr);
  615. if (opt_new == nullptr)
  616. //FIXME This may happen when executing some test cases.
  617. continue;
  618. const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
  619. if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
  620. // An extruder retract override is available at some of the filament presets.
  621. if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
  622. auto opt_copy = opt_new->clone();
  623. opt_copy->apply_override(opt_new_filament);
  624. if (*opt_old == *opt_copy)
  625. delete opt_copy;
  626. else {
  627. filament_overrides.set_key_value(opt_key, opt_copy);
  628. print_diff.emplace_back(opt_key);
  629. }
  630. }
  631. } else if (*opt_new != *opt_old)
  632. print_diff.emplace_back(opt_key);
  633. }
  634. }
  635. // Collect changes to object and region configs.
  636. object_diff = m_default_object_config.diff(new_full_config);
  637. region_diff = m_default_region_config.diff(new_full_config);
  638. // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
  639. for (const t_config_option_key &opt_key : new_full_config.keys()) {
  640. const ConfigOption *opt_old = m_full_print_config.option(opt_key);
  641. const ConfigOption *opt_new = new_full_config.option(opt_key);
  642. if (opt_old == nullptr || *opt_new != *opt_old)
  643. full_config_diff.emplace_back(opt_key);
  644. }
  645. }
  646. std::vector<ObjectID> Print::print_object_ids() const
  647. {
  648. std::vector<ObjectID> out;
  649. // Reserve one more for the caller to append the ID of the Print itself.
  650. out.reserve(m_objects.size() + 1);
  651. for (const PrintObject *print_object : m_objects)
  652. out.emplace_back(print_object->id());
  653. return out;
  654. }
  655. Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
  656. {
  657. #ifdef _DEBUG
  658. check_model_ids_validity(model);
  659. #endif /* _DEBUG */
  660. // Normalize the config.
  661. new_full_config.option("print_settings_id", true);
  662. new_full_config.option("filament_settings_id", true);
  663. new_full_config.option("printer_settings_id", true);
  664. new_full_config.option("physical_printer_settings_id", true);
  665. new_full_config.normalize_fdm();
  666. // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
  667. t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
  668. DynamicPrintConfig filament_overrides;
  669. this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides);
  670. // Do not use the ApplyStatus as we will use the max function when updating apply_status.
  671. unsigned int apply_status = APPLY_STATUS_UNCHANGED;
  672. auto update_apply_status = [&apply_status](bool invalidated)
  673. { apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
  674. if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
  675. update_apply_status(false);
  676. // Grab the lock for the Print / PrintObject milestones.
  677. tbb::mutex::scoped_lock lock(this->state_mutex());
  678. // The following call may stop the background processing.
  679. if (! print_diff.empty())
  680. update_apply_status(this->invalidate_state_by_config_options(print_diff));
  681. // Apply variables to placeholder parser. The placeholder parser is used by G-code export,
  682. // which should be stopped if print_diff is not empty.
  683. size_t num_extruders = m_config.nozzle_diameter.size();
  684. bool num_extruders_changed = false;
  685. if (! full_config_diff.empty()) {
  686. update_apply_status(this->invalidate_step(psGCodeExport));
  687. // Set the profile aliases for the PrintBase::output_filename()
  688. m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
  689. m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
  690. m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
  691. m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
  692. // We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
  693. // see "Placeholders do not respect filament overrides." GH issue #3649
  694. m_placeholder_parser.apply_config(filament_overrides);
  695. // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
  696. m_config.apply_only(new_full_config, print_diff, true);
  697. //FIXME use move semantics once ConfigBase supports it.
  698. m_config.apply(filament_overrides);
  699. // Handle changes to object config defaults
  700. m_default_object_config.apply_only(new_full_config, object_diff, true);
  701. // Handle changes to regions config defaults
  702. m_default_region_config.apply_only(new_full_config, region_diff, true);
  703. m_full_print_config = std::move(new_full_config);
  704. if (num_extruders != m_config.nozzle_diameter.size()) {
  705. num_extruders = m_config.nozzle_diameter.size();
  706. num_extruders_changed = true;
  707. }
  708. }
  709. class LayerRanges
  710. {
  711. public:
  712. LayerRanges() {}
  713. // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
  714. void assign(const t_layer_config_ranges &in) {
  715. m_ranges.clear();
  716. m_ranges.reserve(in.size());
  717. // Input ranges are sorted lexicographically. First range trims the other ranges.
  718. coordf_t last_z = 0;
  719. for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
  720. if (range.first.second > last_z) {
  721. coordf_t min_z = std::max(range.first.first, 0.);
  722. if (min_z > last_z + EPSILON) {
  723. m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
  724. last_z = min_z;
  725. }
  726. if (range.first.second > last_z + EPSILON) {
  727. const DynamicPrintConfig *cfg = &range.second.get();
  728. m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
  729. last_z = range.first.second;
  730. }
  731. }
  732. if (m_ranges.empty())
  733. m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
  734. else if (m_ranges.back().second == nullptr)
  735. m_ranges.back().first.second = DBL_MAX;
  736. else
  737. m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
  738. }
  739. const DynamicPrintConfig* config(const t_layer_height_range &range) const {
  740. auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
  741. // #ys_FIXME_COLOR
  742. // assert(it != m_ranges.end());
  743. // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
  744. // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
  745. if (it == m_ranges.end() ||
  746. std::abs(it->first.first - range.first) > EPSILON ||
  747. std::abs(it->first.second - range.second) > EPSILON )
  748. return nullptr; // desired range doesn't found
  749. return (it == m_ranges.end()) ? nullptr : it->second;
  750. }
  751. std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
  752. std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
  753. private:
  754. std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
  755. };
  756. struct ModelObjectStatus {
  757. enum Status {
  758. Unknown,
  759. Old,
  760. New,
  761. Moved,
  762. Deleted,
  763. };
  764. ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
  765. ObjectID id;
  766. Status status;
  767. LayerRanges layer_ranges;
  768. // Search by id.
  769. bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
  770. };
  771. std::set<ModelObjectStatus> model_object_status;
  772. // 1) Synchronize model objects.
  773. if (model.id() != m_model.id()) {
  774. // Kill everything, initialize from scratch.
  775. // Stop background processing.
  776. this->call_cancel_callback();
  777. update_apply_status(this->invalidate_all_steps());
  778. for (PrintObject *object : m_objects) {
  779. model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
  780. update_apply_status(object->invalidate_all_steps());
  781. delete object;
  782. }
  783. m_objects.clear();
  784. for (PrintRegion *region : m_regions)
  785. delete region;
  786. m_regions.clear();
  787. m_model.assign_copy(model);
  788. for (const ModelObject *model_object : m_model.objects)
  789. model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
  790. } else {
  791. if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
  792. update_apply_status(num_extruders_changed ||
  793. // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
  794. //FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
  795. // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
  796. (num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
  797. // The Tool Ordering and the Wipe Tower are no more valid.
  798. this->invalidate_steps({ psWipeTower, psGCodeExport }) :
  799. // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
  800. this->invalidate_step(psGCodeExport));
  801. m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
  802. }
  803. if (model_object_list_equal(m_model, model)) {
  804. // The object list did not change.
  805. for (const ModelObject *model_object : m_model.objects)
  806. model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
  807. } else if (model_object_list_extended(m_model, model)) {
  808. // Add new objects. Their volumes and configs will be synchronized later.
  809. update_apply_status(this->invalidate_step(psGCodeExport));
  810. for (const ModelObject *model_object : m_model.objects)
  811. model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
  812. for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
  813. model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
  814. m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
  815. m_model.objects.back()->set_model(&m_model);
  816. }
  817. } else {
  818. // Reorder the objects, add new objects.
  819. // First stop background processing before shuffling or deleting the PrintObjects in the object list.
  820. this->call_cancel_callback();
  821. update_apply_status(this->invalidate_step(psGCodeExport));
  822. // Second create a new list of objects.
  823. std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
  824. m_model.objects.clear();
  825. m_model.objects.reserve(model.objects.size());
  826. auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
  827. std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
  828. for (const ModelObject *mobj : model.objects) {
  829. auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
  830. if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
  831. // New ModelObject added.
  832. m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
  833. m_model.objects.back()->set_model(&m_model);
  834. model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
  835. } else {
  836. // Existing ModelObject re-added (possibly moved in the list).
  837. m_model.objects.emplace_back(*it);
  838. model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
  839. }
  840. }
  841. bool deleted_any = false;
  842. for (ModelObject *&model_object : model_objects_old) {
  843. if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
  844. model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
  845. deleted_any = true;
  846. } else
  847. // Do not delete this ModelObject instance.
  848. model_object = nullptr;
  849. }
  850. if (deleted_any) {
  851. // Delete PrintObjects of the deleted ModelObjects.
  852. std::vector<PrintObject*> print_objects_old = std::move(m_objects);
  853. m_objects.clear();
  854. m_objects.reserve(print_objects_old.size());
  855. for (PrintObject *print_object : print_objects_old) {
  856. auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
  857. assert(it_status != model_object_status.end());
  858. if (it_status->status == ModelObjectStatus::Deleted) {
  859. update_apply_status(print_object->invalidate_all_steps());
  860. delete print_object;
  861. } else
  862. m_objects.emplace_back(print_object);
  863. }
  864. for (ModelObject *model_object : model_objects_old)
  865. delete model_object;
  866. }
  867. }
  868. }
  869. // 2) Map print objects including their transformation matrices.
  870. struct PrintObjectStatus {
  871. enum Status {
  872. Unknown,
  873. Deleted,
  874. Reused,
  875. New
  876. };
  877. PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
  878. id(print_object->model_object()->id()),
  879. print_object(print_object),
  880. trafo(print_object->trafo()),
  881. status(status) {}
  882. PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
  883. // ID of the ModelObject & PrintObject
  884. ObjectID id;
  885. // Pointer to the old PrintObject
  886. PrintObject *print_object;
  887. // Trafo generated with model_object->world_matrix(true)
  888. Transform3d trafo;
  889. Status status;
  890. // Search by id.
  891. bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
  892. };
  893. std::multiset<PrintObjectStatus> print_object_status;
  894. for (PrintObject *print_object : m_objects)
  895. print_object_status.emplace(PrintObjectStatus(print_object));
  896. // 3) Synchronize ModelObjects & PrintObjects.
  897. for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
  898. ModelObject &model_object = *m_model.objects[idx_model_object];
  899. auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
  900. assert(it_status != model_object_status.end());
  901. assert(it_status->status != ModelObjectStatus::Deleted);
  902. const ModelObject& model_object_new = *model.objects[idx_model_object];
  903. const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
  904. if (it_status->status == ModelObjectStatus::New)
  905. // PrintObject instances will be added in the next loop.
  906. continue;
  907. // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
  908. assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
  909. // Check whether a model part volume was added or removed, their transformations or order changed.
  910. // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
  911. bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
  912. bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
  913. bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
  914. model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
  915. bool seam_position_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SEAM_POSITION);
  916. if (model_parts_differ || modifiers_differ ||
  917. model_object.origin_translation != model_object_new.origin_translation ||
  918. ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
  919. ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
  920. // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
  921. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
  922. for (auto it = range.first; it != range.second; ++ it) {
  923. update_apply_status(it->print_object->invalidate_all_steps());
  924. const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
  925. }
  926. // Copy content of the ModelObject including its ID, do not change the parent.
  927. model_object.assign_copy(model_object_new);
  928. } else if (supports_differ || seam_position_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
  929. // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
  930. if (supports_differ) {
  931. this->call_cancel_callback();
  932. update_apply_status(false);
  933. }
  934. // Invalidate just the supports step.
  935. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
  936. for (auto it = range.first; it != range.second; ++ it)
  937. update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
  938. if (supports_differ) {
  939. // Copy just the support volumes.
  940. model_volume_list_update_supports_seams(model_object, model_object_new);
  941. }else if (seam_position_differ) {
  942. // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
  943. this->call_cancel_callback();
  944. update_apply_status(false);
  945. // Invalidate just the gcode step.
  946. invalidate_step(psGCodeExport);
  947. // Copy just the seam volumes.
  948. model_volume_list_update_supports_seams(model_object, model_object_new);
  949. }
  950. } else if (model_custom_seam_data_changed(model_object, model_object_new)) {
  951. update_apply_status(this->invalidate_step(psGCodeExport));
  952. }
  953. if (! model_parts_differ && ! modifiers_differ) {
  954. // Synchronize Object's config.
  955. bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
  956. if (object_config_changed)
  957. model_object.config.assign_config(model_object_new.config);
  958. if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
  959. PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
  960. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
  961. for (auto it = range.first; it != range.second; ++ it) {
  962. t_config_option_keys diff = it->print_object->config().diff(new_config);
  963. if (! diff.empty()) {
  964. update_apply_status(it->print_object->invalidate_state_by_config_options(diff));
  965. it->print_object->config_apply_only(new_config, diff, true);
  966. }
  967. }
  968. }
  969. // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
  970. //FIXME What to do with m_material_id?
  971. model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
  972. model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
  973. layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
  974. // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
  975. model_object.name = model_object_new.name;
  976. model_object.input_file = model_object_new.input_file;
  977. // Only refresh ModelInstances if there is any change.
  978. if (model_object.instances.size() != model_object_new.instances.size() ||
  979. ! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
  980. // G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
  981. update_apply_status(this->invalidate_step(psGCodeExport));
  982. model_object.clear_instances();
  983. model_object.instances.reserve(model_object_new.instances.size());
  984. for (const ModelInstance *model_instance : model_object_new.instances) {
  985. model_object.instances.emplace_back(new ModelInstance(*model_instance));
  986. model_object.instances.back()->set_model_object(&model_object);
  987. }
  988. } else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
  989. [](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
  990. l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
  991. // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
  992. // This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
  993. model_object.invalidate_bounding_box();
  994. // Synchronize the content of instances.
  995. auto new_instance = model_object_new.instances.begin();
  996. for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
  997. (*old_instance)->set_transformation((*new_instance)->get_transformation());
  998. (*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
  999. (*old_instance)->printable = (*new_instance)->printable;
  1000. }
  1001. }
  1002. }
  1003. }
  1004. // 4) Generate PrintObjects from ModelObjects and their instances.
  1005. {
  1006. std::vector<PrintObject*> print_objects_new;
  1007. print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
  1008. bool new_objects = false;
  1009. // Walk over all new model objects and check, whether there are matching PrintObjects.
  1010. for (ModelObject *model_object : m_model.objects) {
  1011. auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
  1012. std::vector<const PrintObjectStatus*> old;
  1013. if (range.first != range.second) {
  1014. old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
  1015. for (auto it = range.first; it != range.second; ++ it)
  1016. if (it->status != PrintObjectStatus::Deleted)
  1017. old.emplace_back(&(*it));
  1018. }
  1019. // Generate a list of trafos and XY offsets for instances of a ModelObject
  1020. // Producing the config for PrintObject on demand, caching it at print_object_last.
  1021. const PrintObject *print_object_last = nullptr;
  1022. auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
  1023. print_object->config_apply(print_object_last ?
  1024. print_object_last->config() :
  1025. PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
  1026. print_object_last = print_object;
  1027. };
  1028. std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
  1029. if (old.empty()) {
  1030. // Simple case, just generate new instances.
  1031. for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
  1032. PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
  1033. print_object_apply_config(print_object);
  1034. print_objects_new.emplace_back(print_object);
  1035. // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
  1036. new_objects = true;
  1037. }
  1038. continue;
  1039. }
  1040. // Complex case, try to merge the two lists.
  1041. // Sort the old lexicographically by their trafos.
  1042. std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
  1043. // Merge the old / new lists.
  1044. auto it_old = old.begin();
  1045. for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
  1046. for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
  1047. if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
  1048. // This is a new instance (or a set of instances with the same trafo). Just add it.
  1049. PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
  1050. print_object_apply_config(print_object);
  1051. print_objects_new.emplace_back(print_object);
  1052. // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
  1053. new_objects = true;
  1054. if (it_old != old.end())
  1055. const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
  1056. } else {
  1057. // The PrintObject already exists and the copies differ.
  1058. PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
  1059. if (status != PrintBase::APPLY_STATUS_UNCHANGED)
  1060. update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
  1061. print_objects_new.emplace_back((*it_old)->print_object);
  1062. const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
  1063. }
  1064. }
  1065. }
  1066. if (m_objects != print_objects_new) {
  1067. this->call_cancel_callback();
  1068. update_apply_status(this->invalidate_all_steps());
  1069. m_objects = print_objects_new;
  1070. // Delete the PrintObjects marked as Unknown or Deleted.
  1071. bool deleted_objects = false;
  1072. for (auto &pos : print_object_status)
  1073. if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
  1074. update_apply_status(pos.print_object->invalidate_all_steps());
  1075. delete pos.print_object;
  1076. deleted_objects = true;
  1077. }
  1078. if (new_objects || deleted_objects)
  1079. update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
  1080. if (new_objects)
  1081. update_apply_status(false);
  1082. }
  1083. print_object_status.clear();
  1084. }
  1085. // 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions.
  1086. // Update reference counts of regions from the remaining PrintObjects and their volumes.
  1087. // Regions with zero references could and should be reused.
  1088. for (PrintRegion *region : m_regions)
  1089. region->m_refcnt = 0;
  1090. for (PrintObject *print_object : m_objects) {
  1091. for (int idx_region = 0; idx_region < print_object->region_volumes.size(); ++idx_region) {
  1092. if (!print_object->region_volumes[idx_region].empty())
  1093. ++ m_regions[idx_region]->m_refcnt;
  1094. }
  1095. }
  1096. // All regions now have distinct settings.
  1097. // Check whether applying the new region config defaults we'd get different regions.
  1098. for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
  1099. PrintRegion &region = *m_regions[region_id];
  1100. PrintRegionConfig this_region_config;
  1101. bool this_region_config_set = false;
  1102. for (PrintObject *print_object : m_objects) {
  1103. const LayerRanges *layer_ranges;
  1104. {
  1105. auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
  1106. assert(it_status != model_object_status.end());
  1107. assert(it_status->status != ModelObjectStatus::Deleted);
  1108. layer_ranges = &it_status->layer_ranges;
  1109. }
  1110. if (region_id < print_object->region_volumes.size()) {
  1111. for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
  1112. const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
  1113. const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
  1114. if (this_region_config_set) {
  1115. // If the new config for this volume differs from the other
  1116. // volume configs currently associated to this region, it means
  1117. // the region subdivision does not make sense anymore.
  1118. if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
  1119. // Regions were split. Reset this print_object.
  1120. goto print_object_end;
  1121. } else {
  1122. this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
  1123. for (size_t i = 0; i < region_id; ++ i) {
  1124. const PrintRegion &region_other = *m_regions[i];
  1125. if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
  1126. // Regions were merged. Reset this print_object.
  1127. goto print_object_end;
  1128. }
  1129. this_region_config_set = true;
  1130. }
  1131. }
  1132. }
  1133. continue;
  1134. print_object_end:
  1135. update_apply_status(print_object->invalidate_all_steps());
  1136. // Decrease the references to regions from this volume.
  1137. int ireg = 0;
  1138. for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
  1139. if (! volumes.empty())
  1140. -- m_regions[ireg]->m_refcnt;
  1141. ++ ireg;
  1142. }
  1143. print_object->region_volumes.clear();
  1144. }
  1145. if (this_region_config_set) {
  1146. t_config_option_keys diff = region.config().diff(this_region_config);
  1147. if (! diff.empty()) {
  1148. region.config_apply_only(this_region_config, diff, false);
  1149. for (PrintObject *print_object : m_objects)
  1150. if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty())
  1151. update_apply_status(print_object->invalidate_state_by_config_options(diff));
  1152. }
  1153. }
  1154. }
  1155. // Possibly add new regions for the newly added or resetted PrintObjects.
  1156. for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
  1157. PrintObject &print_object0 = *m_objects[idx_print_object];
  1158. const ModelObject &model_object = *print_object0.model_object();
  1159. const LayerRanges *layer_ranges;
  1160. {
  1161. auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
  1162. assert(it_status != model_object_status.end());
  1163. assert(it_status->status != ModelObjectStatus::Deleted);
  1164. layer_ranges = &it_status->layer_ranges;
  1165. }
  1166. std::vector<int> regions_in_object;
  1167. regions_in_object.reserve(64);
  1168. for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
  1169. PrintObject &print_object = *m_objects[i];
  1170. bool fresh = print_object.region_volumes.empty();
  1171. unsigned int volume_id = 0;
  1172. unsigned int idx_region_in_object = 0;
  1173. for (const ModelVolume *volume : model_object.volumes) {
  1174. if (! volume->is_model_part() && ! volume->is_modifier()) {
  1175. ++ volume_id;
  1176. continue;
  1177. }
  1178. // Filter the layer ranges, so they do not overlap and they contain at least a single layer.
  1179. // Now insert a volume with a layer range to its own region.
  1180. for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
  1181. int region_id = -1;
  1182. if (&print_object == &print_object0) {
  1183. // Get the config applied to this volume.
  1184. PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
  1185. // Find an existing print region with the same config.
  1186. int idx_empty_slot = -1;
  1187. for (int i = 0; i < (int)m_regions.size(); ++ i) {
  1188. if (m_regions[i]->m_refcnt == 0) {
  1189. if (idx_empty_slot == -1)
  1190. idx_empty_slot = i;
  1191. } else if (config.equals(m_regions[i]->config())) {
  1192. region_id = i;
  1193. break;
  1194. }
  1195. }
  1196. // If no region exists with the same config, create a new one.
  1197. if (region_id == -1) {
  1198. if (idx_empty_slot == -1) {
  1199. region_id = (int)m_regions.size();
  1200. this->add_region(config);
  1201. } else {
  1202. region_id = idx_empty_slot;
  1203. m_regions[region_id]->set_config(std::move(config));
  1204. }
  1205. }
  1206. regions_in_object.emplace_back(region_id);
  1207. } else
  1208. region_id = regions_in_object[idx_region_in_object ++];
  1209. // Assign volume to a region.
  1210. if (fresh) {
  1211. if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
  1212. ++ m_regions[region_id]->m_refcnt;
  1213. print_object.add_region_volume(region_id, volume_id, it_range->first);
  1214. }
  1215. }
  1216. ++ volume_id;
  1217. }
  1218. }
  1219. }
  1220. // Update SlicingParameters for each object where the SlicingParameters is not valid.
  1221. // If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
  1222. // (posSlicing and posSupportMaterial was invalidated).
  1223. for (PrintObject *object : m_objects)
  1224. object->update_slicing_parameters();
  1225. #ifdef _DEBUG
  1226. check_model_ids_equal(m_model, model);
  1227. #endif /* _DEBUG */
  1228. return static_cast<ApplyStatus>(apply_status);
  1229. }
  1230. bool Print::has_infinite_skirt() const
  1231. {
  1232. return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
  1233. }
  1234. bool Print::has_skirt() const
  1235. {
  1236. return (m_config.skirt_height > 0 && m_config.skirts > 0) || this->has_infinite_skirt();
  1237. }
  1238. static inline bool sequential_print_horizontal_clearance_valid(const Print &print)
  1239. {
  1240. if (print.config().extruder_clearance_radius == 0)
  1241. return true;
  1242. Polygons convex_hulls_other;
  1243. std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
  1244. const double dist_grow = PrintConfig::min_object_distance(&print.default_region_config()) * 2;
  1245. for (const PrintObject *print_object : print.objects()) {
  1246. const double object_grow = print.config().complete_objects_one_brim ? dist_grow : std::max(dist_grow, print_object->config().brim_width.value);
  1247. assert(! print_object->model_object()->instances.empty());
  1248. assert(! print_object->instances().empty());
  1249. ObjectID model_object_id = print_object->model_object()->id();
  1250. auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
  1251. // Get convex hull of all printable volumes assigned to this print object.
  1252. ModelInstance *model_instance0 = print_object->model_object()->instances.front();
  1253. if (it_convex_hull == map_model_object_to_convex_hull.end()) {
  1254. // Calculate the convex hull of a printable object.
  1255. // Grow convex hull with the clearance margin.
  1256. // FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
  1257. // which causes that the warning will be showed after arrangement with the
  1258. // appropriate object distance. Even if I set this to jtMiter the warning still shows up.
  1259. it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
  1260. offset(print_object->model_object()->convex_hull_2d(
  1261. Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
  1262. // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
  1263. // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
  1264. float(scale_(0.5 * object_grow - EPSILON)),
  1265. jtRound, float(scale_(0.1))).front());
  1266. }
  1267. // Make a copy, so it may be rotated for instances.
  1268. //FIXME seems like the rotation isn't taken into account
  1269. Polygon convex_hull0 = it_convex_hull->second;
  1270. //this can create bugs in macos, for reasons.
  1271. double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
  1272. if (std::abs(z_diff) > EPSILON)
  1273. convex_hull0.rotate(z_diff);
  1274. // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
  1275. for (const PrintInstance& instance : print_object->instances()) {
  1276. Polygon convex_hull = convex_hull0;
  1277. // instance.shift is a position of a centered object, while model object may not be centered.
  1278. // Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
  1279. convex_hull.translate(instance.shift - print_object->center_offset());
  1280. if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty())
  1281. return false;
  1282. convex_hulls_other.emplace_back(std::move(convex_hull));
  1283. }
  1284. /*
  1285. 'old' superslicer sequential_print_horizontal_clearance_valid, that is better at skirts, but need some works, as the arrange has changed.
  1286. // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
  1287. for (const PrintInstance &instance : print_object->instances()) {
  1288. Polygons convex_hull = print_object->model_object()->convex_hull_2d(
  1289. Geometry::assemble_transform(Vec3d::Zero(),
  1290. instance.model_instance->get_rotation(), instance.model_instance->get_scaling_factor(), instance.model_instance->get_mirror()));
  1291. // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
  1292. // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
  1293. //float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
  1294. //jtRound, float(scale_(0.1)));
  1295. if (convex_hull.empty())
  1296. continue;
  1297. // instance.shift is a position of a centered object, while model object may not be centered.
  1298. // Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
  1299. for(Polygon &poly : convex_hull)
  1300. poly.translate(instance.shift - print_object->center_offset());
  1301. if (! intersection(
  1302. convex_hulls_other,
  1303. offset(convex_hull[0], double(scale_(PrintConfig::min_object_distance(&instance.print_object->config(),0.)) - SCALED_EPSILON), jtRound, scale_(0.1))).empty())
  1304. return false;
  1305. double extra_grow = PrintConfig::min_object_distance(&instance.print_object->config(), 1.);
  1306. if (extra_grow > 0)
  1307. convex_hull = offset(convex_hull, scale_(extra_grow));
  1308. polygons_append(convex_hulls_other, convex_hull);
  1309. }
  1310. */
  1311. }
  1312. return true;
  1313. }
  1314. static inline bool sequential_print_vertical_clearance_valid(const Print &print)
  1315. {
  1316. std::vector<const PrintInstance*> print_instances_ordered = sort_object_instances_by_model_order(print);
  1317. // Ignore the last instance printed.
  1318. print_instances_ordered.pop_back();
  1319. // Find the other highest instance.
  1320. auto it = std::max_element(print_instances_ordered.begin(), print_instances_ordered.end(), [](auto l, auto r) {
  1321. return l->print_object->height() < r->print_object->height();
  1322. });
  1323. return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
  1324. }
  1325. double Print::get_object_first_layer_height(const PrintObject& object) const {
  1326. //get object first layer height
  1327. double object_first_layer_height = object.config().first_layer_height.value;
  1328. if (object.config().first_layer_height.percent) {
  1329. std::set<uint16_t> object_extruders;
  1330. for (size_t region_id = 0; region_id < object.region_volumes.size(); ++region_id) {
  1331. if (object.region_volumes[region_id].empty()) continue;
  1332. const PrintRegion* region = this->regions()[region_id];
  1333. PrintRegion::collect_object_printing_extruders(config(), object.config(), region->config(), object_extruders);
  1334. }
  1335. object_first_layer_height = 1000000000;
  1336. for (uint16_t extruder_id : object_extruders) {
  1337. double nozzle_diameter = config().nozzle_diameter.values[extruder_id];
  1338. object_first_layer_height = std::min(object_first_layer_height, object.config().first_layer_height.get_abs_value(nozzle_diameter));
  1339. }
  1340. }
  1341. return object_first_layer_height;
  1342. }
  1343. double Print::get_first_layer_height() const
  1344. {
  1345. if (m_objects.empty())
  1346. throw Slic3r::InvalidArgument("first_layer_height() can't be called without PrintObjects");
  1347. double min_layer_height = 10000000000.;
  1348. for(PrintObject* obj : m_objects)
  1349. min_layer_height = std::fmin(min_layer_height, get_object_first_layer_height(*obj));
  1350. if(min_layer_height == 10000000000.)
  1351. throw Slic3r::InvalidArgument("first_layer_height() can't be computed");
  1352. return min_layer_height;
  1353. }
  1354. // Precondition: Print::validate() requires the Print::apply() to be called its invocation.
  1355. std::pair<PrintBase::PrintValidationError, std::string> Print::validate() const
  1356. {
  1357. if (m_objects.empty())
  1358. return { PrintBase::PrintValidationError::pveWrongPosition, L("All objects are outside of the print volume.") };
  1359. if (extruders().empty())
  1360. return { PrintBase::PrintValidationError::pveNoPrint, L("The supplied settings will cause an empty print.") };
  1361. if (m_config.complete_objects) {
  1362. if (! sequential_print_horizontal_clearance_valid(*this))
  1363. return { PrintBase::PrintValidationError::pveWrongPosition, L("Some objects are too close; your extruder will collide with them.") };
  1364. if (! sequential_print_vertical_clearance_valid(*this))
  1365. return { PrintBase::PrintValidationError::pveWrongPosition,L("Some objects are too tall and cannot be printed without extruder collisions.") };
  1366. }
  1367. if (m_config.spiral_vase) {
  1368. size_t total_copies_count = 0;
  1369. for (const PrintObject *object : m_objects)
  1370. total_copies_count += object->instances().size();
  1371. // #4043
  1372. if (total_copies_count > 1 && ! m_config.complete_objects.value)
  1373. return { PrintBase::PrintValidationError::pveWrongSettings,L("Only a single object may be printed at a time in Spiral Vase mode. "
  1374. "Either remove all but the last object, or enable sequential mode by \"complete_objects\".") };
  1375. assert(m_objects.size() == 1 || config().complete_objects.value);
  1376. size_t num_regions = 0;
  1377. for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : m_objects.front()->region_volumes)
  1378. if (! volumes_per_region.empty())
  1379. ++ num_regions;
  1380. if (num_regions > 1)
  1381. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Spiral Vase option can only be used when printing single material objects.") };
  1382. }
  1383. if (this->has_wipe_tower() && ! m_objects.empty()) {
  1384. // Make sure all extruders use same diameter filament and have the same nozzle diameter
  1385. // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
  1386. double first_nozzle_diam = m_config.nozzle_diameter.get_at(*extruders().begin());
  1387. double first_filament_diam = m_config.filament_diameter.get_at(*extruders().begin());
  1388. for (const auto& extruder_idx : extruders()) {
  1389. double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
  1390. double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
  1391. if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
  1392. || std::abs((filament_diam-first_filament_diam)/first_filament_diam) > 0.1)
  1393. return { PrintBase::PrintValidationError::pveWrongSettings,L("The wipe tower is only supported if all extruders have the same nozzle diameter "
  1394. "and use filaments of the same diameter.") };
  1395. }
  1396. if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfSprinter && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin
  1397. && m_config.gcode_flavor != gcfKlipper)
  1398. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.") };
  1399. if (! m_config.use_relative_e_distances)
  1400. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).") };
  1401. if (m_config.ooze_prevention)
  1402. return { PrintBase::PrintValidationError::pveWrongSettings,L("Ooze prevention is currently not supported with the wipe tower enabled.") };
  1403. if (m_config.use_volumetric_e)
  1404. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).") };
  1405. if (m_config.complete_objects && extruders().size() > 1)
  1406. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is currently not supported for multimaterial sequential prints.") };
  1407. if (m_objects.size() > 1) {
  1408. bool has_custom_layering = false;
  1409. std::vector<std::vector<coordf_t>> layer_height_profiles;
  1410. for (const PrintObject *object : m_objects) {
  1411. has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
  1412. if (has_custom_layering) {
  1413. layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
  1414. break;
  1415. }
  1416. }
  1417. const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters();
  1418. size_t tallest_object_idx = 0;
  1419. if (has_custom_layering)
  1420. PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
  1421. for (size_t i = 1; i < m_objects.size(); ++ i) {
  1422. const PrintObject *object = m_objects[i];
  1423. const SlicingParameters &slicing_params = object->slicing_parameters();
  1424. if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
  1425. std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
  1426. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is only supported for multiple objects if they have equal layer heights") };
  1427. if (slicing_params.raft_layers() != slicing_params0.raft_layers())
  1428. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers") };
  1429. if (object->config().support_material_contact_distance_type != m_objects.front()->config().support_material_contact_distance_type
  1430. || object->config().support_material_contact_distance_top != m_objects.front()->config().support_material_contact_distance_top
  1431. || object->config().support_material_contact_distance_bottom != m_objects.front()->config().support_material_contact_distance_bottom)
  1432. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance") };
  1433. if (! equal_layering(slicing_params, slicing_params0))
  1434. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower is only supported for multiple objects if they are sliced equally.") };
  1435. if (has_custom_layering) {
  1436. PrintObject::update_layer_height_profile(*object->model_object(), slicing_params, layer_height_profiles[i]);
  1437. if (*(layer_height_profiles[i].end()-2) > *(layer_height_profiles[tallest_object_idx].end()-2))
  1438. tallest_object_idx = i;
  1439. }
  1440. }
  1441. if (has_custom_layering) {
  1442. const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
  1443. for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
  1444. if (idx_object == tallest_object_idx)
  1445. continue;
  1446. const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
  1447. // The comparison of the profiles is not just about element-wise equality, some layers may not be
  1448. // explicitely included. Always remember z and height of last reference layer that in the vector
  1449. // and compare to that. In case some layers are in the vectors multiple times, only the last entry is
  1450. // taken into account and compared.
  1451. size_t i = 0; // index into tested profile
  1452. size_t j = 0; // index into reference profile
  1453. coordf_t ref_z = -1.;
  1454. coordf_t next_ref_z = layer_height_profile_tallest[0];
  1455. coordf_t ref_height = -1.;
  1456. while (i < layer_height_profile.size()) {
  1457. coordf_t this_z = layer_height_profile[i];
  1458. // find the last entry with this z
  1459. while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
  1460. i += 2;
  1461. coordf_t this_height = layer_height_profile[i+1];
  1462. if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
  1463. ref_z = next_ref_z;
  1464. do { // one layer can be in the vector several times
  1465. ref_height = layer_height_profile_tallest[j+1];
  1466. if (j+2 >= layer_height_profile_tallest.size())
  1467. break;
  1468. j += 2;
  1469. next_ref_z = layer_height_profile_tallest[j];
  1470. } while (ref_z == next_ref_z);
  1471. }
  1472. if (std::abs(this_height - ref_height) > EPSILON)
  1473. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe tower is only supported if all objects have the same variable layer height") };
  1474. i += 2;
  1475. }
  1476. }
  1477. }
  1478. }
  1479. }
  1480. {
  1481. std::set<uint16_t> extruders = this->extruders();
  1482. // Find the smallest used nozzle diameter and the number of unique nozzle diameters.
  1483. double min_nozzle_diameter = std::numeric_limits<double>::max();
  1484. double max_nozzle_diameter = 0;
  1485. for (uint16_t extruder_id : extruders) {
  1486. double dmr = m_config.nozzle_diameter.get_at(extruder_id);
  1487. min_nozzle_diameter = std::min(min_nozzle_diameter, dmr);
  1488. max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
  1489. }
  1490. #if 0
  1491. // We currently allow one to assign extruders with a higher index than the number
  1492. // of physical extruders the machine is equipped with, as the Printer::apply() clamps them.
  1493. unsigned int total_extruders_count = m_config.nozzle_diameter.size();
  1494. for (const auto& extruder_idx : extruders)
  1495. if ( extruder_idx >= total_extruders_count )
  1496. return L("One or more object were assigned an extruder that the printer does not have.");
  1497. #endif
  1498. const double print_first_layer_height = get_first_layer_height();
  1499. for (PrintObject *object : m_objects) {
  1500. if (object->config().raft_layers > 0 || object->config().support_material.value) {
  1501. if ((object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) {
  1502. // The object has some form of support and either support_material_extruder or support_material_interface_extruder
  1503. // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
  1504. // are of the same diameter.
  1505. return { PrintBase::PrintValidationError::pveWrongSettings,L("Printing with multiple extruders of differing nozzle diameters. "
  1506. "If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), "
  1507. "all nozzles have to be of the same diameter.") };
  1508. }
  1509. if (this->has_wipe_tower()) {
  1510. if (object->config().support_material_contact_distance_type.value == zdNone) {
  1511. // Soluble interface
  1512. if (! object->config().support_material_synchronize_layers)
  1513. return { PrintBase::PrintValidationError::pveWrongSettings,L("For the Wipe Tower to work with the soluble supports, the support layers need to be synchronized with the object layers.") };
  1514. } else {
  1515. // Non-soluble interface
  1516. if (object->config().support_material_extruder != 0 || object->config().support_material_interface_extruder != 0)
  1517. return { PrintBase::PrintValidationError::pveWrongSettings,L("The Wipe Tower currently supports the non-soluble supports only if they are printed with the current extruder without triggering a tool change. "
  1518. "(both support_material_extruder and support_material_interface_extruder need to be set to 0).") };
  1519. }
  1520. }
  1521. }
  1522. const double object_first_layer_height = get_object_first_layer_height(*object);
  1523. // validate layer_height for each region
  1524. for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) {
  1525. if (object->region_volumes[region_id].empty()) continue;
  1526. const PrintRegion* region = this->regions()[region_id];
  1527. std::set<uint16_t> object_extruders;
  1528. PrintRegion::collect_object_printing_extruders(config(), object->config(), region->config(), object_extruders);
  1529. double layer_height = object->config().layer_height.value;
  1530. for (uint16_t extruder_id : object_extruders) {
  1531. double nozzle_diameter = config().nozzle_diameter.get_at(extruder_id);
  1532. double min_layer_height = config().min_layer_height.get_abs_value(extruder_id, nozzle_diameter);
  1533. double max_layer_height = config().max_layer_height.get_abs_value(extruder_id, nozzle_diameter);
  1534. if (max_layer_height < EPSILON) max_layer_height = nozzle_diameter * 0.75;
  1535. if (min_layer_height > max_layer_height) return { PrintBase::PrintValidationError::pveWrongSettings, L("Min layer height can't be greater than Max layer height") };
  1536. if (max_layer_height > nozzle_diameter) return { PrintBase::PrintValidationError::pveWrongSettings, L("Max layer height can't be greater than nozzle diameter") };
  1537. double skirt_width = Flow::new_from_config_width(frPerimeter,
  1538. *Flow::extrusion_option("skirt_extrusion_width", m_default_region_config),
  1539. (float)m_config.nozzle_diameter.get_at(extruder_id),
  1540. print_first_layer_height,
  1541. 1,0 //don't care, all i want if width from width
  1542. ).width;
  1543. //check first layer
  1544. if (object->region_volumes[region_id].front().first.first < object_first_layer_height) {
  1545. if (object_first_layer_height + EPSILON < min_layer_height)
  1546. return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be thinner than %s")) % "min layer height").str() };
  1547. for (auto tuple : std::vector<std::pair<double, const char*>>{
  1548. {nozzle_diameter, "nozzle diameter"},
  1549. {max_layer_height, "max layer height"},
  1550. {skirt_width, "skirt extrusion width"},
  1551. {object->config().support_material ? region->width(FlowRole::frSupportMaterial, true, *object) : object_first_layer_height, "support material extrusion width"},
  1552. {region->width(FlowRole::frPerimeter, true, *object), "perimeter extrusion width"},
  1553. {region->width(FlowRole::frExternalPerimeter, true, *object), "perimeter extrusion width"},
  1554. {region->width(FlowRole::frInfill, true, *object), "infill extrusion width"},
  1555. {region->width(FlowRole::frSolidInfill, true, *object), "solid infill extrusion width"},
  1556. {region->width(FlowRole::frTopSolidInfill, true, *object), "top solid infill extrusion width"},
  1557. })
  1558. if (object_first_layer_height > tuple.first + EPSILON)
  1559. return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be greater than %s")) % tuple.second).str() };
  1560. }
  1561. //check not-first layer
  1562. if (object->region_volumes[region_id].front().first.second > layer_height) {
  1563. if (layer_height + EPSILON < min_layer_height)
  1564. return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("First layer height can't be higher than %s")) % "min layer height").str() };
  1565. for (auto tuple : std::vector<std::pair<double, const char*>>{
  1566. {nozzle_diameter, "nozzle diameter"},
  1567. {max_layer_height, "max layer height"},
  1568. {skirt_width, "skirt extrusion width"},
  1569. {object->config().support_material ? region->width(FlowRole::frSupportMaterial, false, *object) : layer_height, "support material extrusion width"},
  1570. {region->width(FlowRole::frPerimeter, false, *object), "perimeter extrusion width"},
  1571. {region->width(FlowRole::frExternalPerimeter, false, *object), "perimeter extrusion width"},
  1572. {region->width(FlowRole::frInfill, false, *object), "infill extrusion width"},
  1573. {region->width(FlowRole::frSolidInfill, false, *object), "solid infill extrusion width"},
  1574. {region->width(FlowRole::frTopSolidInfill, false, *object), "top solid infill extrusion width"},
  1575. })
  1576. if (layer_height > tuple.first + EPSILON)
  1577. return { PrintBase::PrintValidationError::pveWrongSettings, (boost::format(L("Layer height can't be greater than %s")) % tuple.second).str() };
  1578. }
  1579. }
  1580. }
  1581. }
  1582. }
  1583. return { PrintValidationError::pveNone, std::string() };
  1584. }
  1585. #if 0
  1586. // the bounding box of objects placed in copies position
  1587. // (without taking skirt/brim/support material into account)
  1588. BoundingBox Print::bounding_box() const
  1589. {
  1590. BoundingBox bb;
  1591. for (const PrintObject *object : m_objects)
  1592. for (const PrintInstance &instance : object->instances()) {
  1593. BoundingBox bb2(object->bounding_box());
  1594. bb.merge(bb2.min + instance.shift);
  1595. bb.merge(bb2.max + instance.shift);
  1596. }
  1597. return bb;
  1598. }
  1599. // the total bounding box of extrusions, including skirt/brim/support material
  1600. // this methods needs to be called even when no steps were processed, so it should
  1601. // only use configuration values
  1602. BoundingBox Print::total_bounding_box() const
  1603. {
  1604. // get objects bounding box
  1605. BoundingBox bb = this->bounding_box();
  1606. // we need to offset the objects bounding box by at least half the perimeters extrusion width
  1607. Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter);
  1608. double extra = perimeter_flow.width/2;
  1609. // consider support material
  1610. if (this->has_support_material()) {
  1611. extra = std::max(extra, SUPPORT_MATERIAL_MARGIN);
  1612. }
  1613. // consider brim and skirt
  1614. if (m_config.brim_width.value > 0) {
  1615. Flow brim_flow = this->brim_flow();
  1616. extra = std::max(extra, m_config.brim_width.value + brim_flow.width/2);
  1617. }
  1618. if (this->has_skirt()) {
  1619. int skirts = m_config.skirts.value + m_config.skirt_brim.value;
  1620. if (skirts == 0 && this->has_infinite_skirt()) skirts = 1;
  1621. Flow skirt_flow = this->skirt_flow();
  1622. if (m_config.skirt_distance_from_brim)
  1623. extra += m_config.brim_width.value
  1624. + m_config.skirt_distance.value
  1625. + skirts * skirt_flow.spacing()
  1626. + skirt_flow.width / 2;
  1627. else
  1628. extra = std::max(
  1629. extra,
  1630. m_config.brim_width.value
  1631. + m_config.skirt_distance.value
  1632. + skirts * skirt_flow.spacing()
  1633. + skirt_flow.width/2
  1634. );
  1635. }
  1636. if (extra > 0)
  1637. bb.offset(scale_(extra));
  1638. return bb;
  1639. }
  1640. #endif
  1641. Flow Print::brim_flow(size_t extruder_id, const PrintObjectConfig& brim_config) const
  1642. {
  1643. //use default region, but current object config.
  1644. PrintRegionConfig tempConf = m_default_region_config;
  1645. tempConf.parent = &brim_config;
  1646. return Flow::new_from_config_width(
  1647. frPerimeter,
  1648. *Flow::extrusion_option("brim_extrusion_width", tempConf),
  1649. (float)m_config.nozzle_diameter.get_at(extruder_id),
  1650. (float)get_first_layer_height(),
  1651. (extruder_id < m_config.nozzle_diameter.values.size()) ? brim_config.get_computed_value("filament_max_overlap", extruder_id) : 1,
  1652. 0
  1653. );
  1654. }
  1655. Flow Print::skirt_flow(size_t extruder_id, bool first_layer/*=false*/) const
  1656. {
  1657. if (m_objects.empty())
  1658. throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects");
  1659. //get extruder used to compute first layer height
  1660. double max_nozzle_diam;
  1661. for (PrintObject* pobject : m_objects) {
  1662. PrintObject& object = *pobject;
  1663. std::set<uint16_t> object_extruders;
  1664. for (size_t region_id = 0; region_id < object.region_volumes.size(); ++region_id) {
  1665. if (object.region_volumes[region_id].empty()) continue;
  1666. const PrintRegion* region = this->regions()[region_id];
  1667. PrintRegion::collect_object_printing_extruders(config(), object.config(), region->config(), object_extruders);
  1668. }
  1669. //get object first layer extruder
  1670. int first_layer_extruder = 0;
  1671. for (uint16_t extruder_id : object_extruders) {
  1672. double nozzle_diameter = config().nozzle_diameter.values[extruder_id];
  1673. max_nozzle_diam = std::max(max_nozzle_diam, nozzle_diameter);
  1674. }
  1675. }
  1676. //send m_default_object_config becasue it's the lowest config needed (extrusion_option need config from object & print)
  1677. return Flow::new_from_config_width(
  1678. frPerimeter,
  1679. *Flow::extrusion_option("skirt_extrusion_width", m_default_region_config),
  1680. (float)max_nozzle_diam,
  1681. (float)get_first_layer_height(),
  1682. 1, // hard to say what extruder we have here(many) m_default_region_config.get_computed_value("filament_max_overlap", extruder -1),
  1683. 0
  1684. );
  1685. }
  1686. bool Print::has_support_material() const
  1687. {
  1688. for (const PrintObject *object : m_objects)
  1689. if (object->has_support_material())
  1690. return true;
  1691. return false;
  1692. }
  1693. /* This method assigns extruders to the volumes having a material
  1694. but not having extruders set in the volume config. */
  1695. void Print::auto_assign_extruders(ModelObject* model_object) const
  1696. {
  1697. // only assign extruders if object has more than one volume
  1698. if (model_object->volumes.size() < 2)
  1699. return;
  1700. // size_t extruders = m_config.nozzle_diameter.values.size();
  1701. for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
  1702. ModelVolume *volume = model_object->volumes[volume_id];
  1703. //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
  1704. if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
  1705. volume->config.set("extruder", int(volume_id + 1));
  1706. }
  1707. }
  1708. // Slicing process, running at a background thread.
  1709. void Print::process()
  1710. {
  1711. name_tbb_thread_pool_threads();
  1712. bool something_done = !is_step_done_unguarded(psBrim);
  1713. BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
  1714. for (PrintObject *obj : m_objects)
  1715. obj->make_perimeters();
  1716. this->set_status(70, L("Infilling layers"));
  1717. //note: as object seems to be sliced independantly, it's maybe possible to add a tbb parallel_loop with simple partitioner on infill,
  1718. // as prepare_infill has some function not //
  1719. for (PrintObject *obj : m_objects)
  1720. obj->infill();
  1721. for (PrintObject *obj : m_objects)
  1722. obj->ironing();
  1723. for (PrintObject *obj : m_objects)
  1724. obj->generate_support_material();
  1725. if (this->set_started(psWipeTower)) {
  1726. m_wipe_tower_data.clear();
  1727. m_tool_ordering.clear();
  1728. if (this->has_wipe_tower()) {
  1729. //this->set_status(95, L("Generating wipe tower"));
  1730. this->_make_wipe_tower();
  1731. } else if (! this->config().complete_objects.value) {
  1732. // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
  1733. m_tool_ordering = ToolOrdering(*this, -1, false);
  1734. if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
  1735. throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
  1736. }
  1737. this->set_done(psWipeTower);
  1738. }
  1739. if (this->set_started(psSkirt)) {
  1740. m_skirt.clear();
  1741. m_skirt_first_layer.reset();
  1742. m_skirt_convex_hull.clear();
  1743. m_first_layer_convex_hull.points.clear();
  1744. for (PrintObject *obj : m_objects) {
  1745. obj->m_skirt.clear();
  1746. obj->m_skirt_first_layer.reset();
  1747. }
  1748. if (this->has_skirt()) {
  1749. this->set_status(88, L("Generating skirt"));
  1750. if (config().complete_objects && !config().complete_objects_one_skirt){
  1751. for (PrintObject *obj : m_objects){
  1752. //create a skirt "pattern" (one per object)
  1753. const std::vector<PrintInstance> copies{ obj->instances() };
  1754. obj->m_instances.clear();
  1755. obj->m_instances.emplace_back();
  1756. this->_make_skirt({ obj }, obj->m_skirt, obj->m_skirt_first_layer);
  1757. obj->m_instances = copies;
  1758. }
  1759. } else {
  1760. this->_make_skirt(m_objects, m_skirt, m_skirt_first_layer);
  1761. }
  1762. }
  1763. this->set_done(psSkirt);
  1764. }
  1765. if (this->set_started(psBrim)) {
  1766. m_brim.clear();
  1767. //group object per brim settings
  1768. m_first_layer_convex_hull.points.clear();
  1769. std::vector<std::vector<PrintObject*>> obj_groups;
  1770. for (PrintObject *obj : m_objects) {
  1771. obj->m_brim.clear();
  1772. bool added = false;
  1773. for (std::vector<PrintObject*> &obj_group : obj_groups) {
  1774. if (obj_group.front()->config().brim_ears.value == obj->config().brim_ears.value
  1775. && obj_group.front()->config().brim_ears_max_angle.value == obj->config().brim_ears_max_angle.value
  1776. && obj_group.front()->config().brim_ears_pattern.value == obj->config().brim_ears_pattern.value
  1777. && obj_group.front()->config().brim_inside_holes.value == obj->config().brim_inside_holes.value
  1778. && obj_group.front()->config().brim_offset.value == obj->config().brim_offset.value
  1779. && obj_group.front()->config().brim_width.value == obj->config().brim_width.value
  1780. && obj_group.front()->config().brim_width_interior.value == obj->config().brim_width_interior.value
  1781. && obj_group.front()->config().first_layer_extrusion_width.value == obj->config().first_layer_extrusion_width.value) {
  1782. added = true;
  1783. obj_group.push_back(obj);
  1784. }
  1785. }
  1786. if (!added) {
  1787. obj_groups.emplace_back();
  1788. obj_groups.back().push_back(obj);
  1789. }
  1790. }
  1791. ExPolygons brim_area;
  1792. if (obj_groups.size() > 1) {
  1793. for (std::vector<PrintObject*> &obj_group : obj_groups)
  1794. for (const PrintObject *object : obj_group)
  1795. if (!object->m_layers.empty())
  1796. for (const PrintInstance &pt : object->m_instances) {
  1797. int first_idx = brim_area.size();
  1798. brim_area.insert(brim_area.end(), object->m_layers.front()->lslices.begin(), object->m_layers.front()->lslices.end());
  1799. for (int i = first_idx; i < brim_area.size(); i++) {
  1800. brim_area[i].translate(pt.shift.x(), pt.shift.y());
  1801. }
  1802. }
  1803. }
  1804. for (std::vector<PrintObject*> &obj_group : obj_groups) {
  1805. const PrintObjectConfig &brim_config = obj_group.front()->config();
  1806. if (brim_config.brim_width > 0 || brim_config.brim_width_interior > 0) {
  1807. this->set_status(88, L("Generating brim"));
  1808. if (config().complete_objects && !config().complete_objects_one_brim) {
  1809. for (PrintObject *obj : obj_group) {
  1810. //get flow
  1811. std::set<uint16_t> set_extruders = this->object_extruders({ obj });
  1812. append(set_extruders, this->support_material_extruders());
  1813. Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : *set_extruders.begin(), obj->config());
  1814. //don't consider other objects/instances. It's not possible because it's duplicated by some code afterward... i think.
  1815. brim_area.clear();
  1816. //create a brim "pattern" (one per object)
  1817. const std::vector<PrintInstance> copies{ obj->instances() };
  1818. obj->m_instances.clear();
  1819. obj->m_instances.emplace_back();
  1820. if (brim_config.brim_width > 0) {
  1821. if (brim_config.brim_ears)
  1822. this->_make_brim_ears(flow, { obj }, brim_area, obj->m_brim);
  1823. else
  1824. this->_make_brim(flow, { obj }, brim_area, obj->m_brim);
  1825. }
  1826. if (brim_config.brim_width_interior > 0) {
  1827. _make_brim_interior(flow, { obj }, brim_area, obj->m_brim);
  1828. }
  1829. obj->m_instances = copies;
  1830. }
  1831. } else {
  1832. if (obj_groups.size() > 1)
  1833. brim_area = union_ex(brim_area);
  1834. //get the first extruder in the list for these objects... replicating gcode generation
  1835. std::set<uint16_t> set_extruders = this->object_extruders(m_objects);
  1836. append(set_extruders, this->support_material_extruders());
  1837. Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : *set_extruders.begin(), m_default_object_config);
  1838. if (brim_config.brim_ears)
  1839. this->_make_brim_ears(flow, obj_group, brim_area, m_brim);
  1840. else
  1841. this->_make_brim(flow, obj_group, brim_area, m_brim);
  1842. if (brim_config.brim_width_interior > 0)
  1843. _make_brim_interior(flow, obj_group, brim_area, m_brim);
  1844. }
  1845. }
  1846. }
  1847. // Brim depends on skirt (brim lines are trimmed by the skirt lines), therefore if
  1848. // the skirt gets invalidated, brim gets invalidated as well and the following line is called.
  1849. this->finalize_first_layer_convex_hull();
  1850. this->set_done(psBrim);
  1851. }
  1852. BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
  1853. //notify gui that the slicing/preview structs are ready to be drawed
  1854. if (something_done)
  1855. this->set_status(90, L("Slicing done"), SlicingStatus::FlagBits::SLICING_ENDED);
  1856. }
  1857. // G-code export process, running at a background thread.
  1858. // The export_gcode may die for various reasons (fails to process output_filename_format,
  1859. // write error into the G-code, cannot execute post-processing scripts).
  1860. // It is up to the caller to show an error message.
  1861. std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
  1862. {
  1863. // output everything to a G-code file
  1864. // The following call may die if the output_filename_format template substitution fails.
  1865. std::string path = this->output_filepath(path_template);
  1866. std::string message;
  1867. if (!path.empty() && result == nullptr) {
  1868. // Only show the path if preview_data is not set -> running from command line.
  1869. message = L("Exporting G-code");
  1870. message += " to ";
  1871. message += path;
  1872. } else
  1873. message = L("Generating G-code");
  1874. this->set_status(90, message);
  1875. // The following line may die for multiple reasons.
  1876. GCode gcode;
  1877. gcode.do_export(this, path.c_str(), result, thumbnail_cb);
  1878. return path.c_str();
  1879. }
  1880. void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollection &out, std::optional<ExtrusionEntityCollection>& out_first_layer)
  1881. {
  1882. // First off we need to decide how tall the skirt must be.
  1883. // The skirt_height option from config is expressed in layers, but our
  1884. // object might have different layer heights, so we need to find the print_z
  1885. // of the highest layer involved.
  1886. // Note that unless has_infinite_skirt() == true
  1887. // the actual skirt might not reach this $skirt_height_z value since the print
  1888. // order of objects on each layer is not guaranteed and will not generally
  1889. // include the thickest object first. It is just guaranteed that a skirt is
  1890. // prepended to the first 'n' layers (with 'n' = skirt_height).
  1891. // $skirt_height_z in this case is the highest possible skirt height for safety.
  1892. coordf_t skirt_height_z = 0.;
  1893. for (const PrintObject *object : objects) {
  1894. size_t skirt_layers = this->has_infinite_skirt() ?
  1895. object->layer_count() :
  1896. std::min(size_t(m_config.skirt_height.value), object->layer_count());
  1897. skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
  1898. }
  1899. // Collect points from all layers contained in skirt height.
  1900. Points points;
  1901. for (const PrintObject *object : objects) {
  1902. Points object_points;
  1903. // Get object layers up to skirt_height_z.
  1904. for (const Layer *layer : object->m_layers) {
  1905. if (layer->print_z > skirt_height_z)
  1906. break;
  1907. for (const ExPolygon &expoly : layer->lslices)
  1908. // Collect the outer contour points only, ignore holes for the calculation of the convex hull.
  1909. append(object_points, expoly.contour.points);
  1910. }
  1911. // Get support layers up to skirt_height_z.
  1912. for (const SupportLayer *layer : object->support_layers()) {
  1913. if (layer->print_z > skirt_height_z)
  1914. break;
  1915. for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) {
  1916. Polylines poly;
  1917. extrusion_entity->collect_polylines(poly);
  1918. for (const Polyline& polyline : poly)
  1919. append(object_points, polyline.points);
  1920. }
  1921. }
  1922. // if brim, it superseed object & support for first layer
  1923. if (config().skirt_distance_from_brim) {
  1924. // get first layer support
  1925. if (!object->support_layers().empty() && object->support_layers().front()->print_z == object->m_layers[0]->print_z) {
  1926. Points support_points;
  1927. for (const ExtrusionEntity* extrusion_entity : object->support_layers().front()->support_fills.entities) {
  1928. Polylines poly;
  1929. extrusion_entity->collect_polylines(poly);
  1930. for (const Polyline& polyline : poly)
  1931. append(support_points, polyline.points);
  1932. }
  1933. Polygon hull_support = Slic3r::Geometry::convex_hull(support_points);
  1934. for (const Polygon& poly : offset(hull_support, scale_(object->config().brim_width)))
  1935. append(object_points, poly.points);
  1936. }
  1937. // get object
  1938. for (const ExPolygon& expoly : object->m_layers[0]->lslices)
  1939. for (const Polygon& poly : offset(expoly.contour, scale_(object->config().brim_width)))
  1940. append(object_points, poly.points);
  1941. }
  1942. // Repeat points for each object copy.
  1943. for (const PrintInstance &instance : object->instances()) {
  1944. Points copy_points = object_points;
  1945. for (Point &pt : copy_points)
  1946. pt += instance.shift;
  1947. append(points, copy_points);
  1948. }
  1949. }
  1950. // Include the wipe tower.
  1951. append(points, this->first_layer_wipe_tower_corners());
  1952. if (points.size() < 3)
  1953. // At least three points required for a convex hull.
  1954. return;
  1955. this->throw_if_canceled();
  1956. Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
  1957. // Skirt may be printed on several layers, having distinct layer heights,
  1958. // but loops must be aligned so can't vary width/spacing
  1959. std::vector<size_t> extruders;
  1960. std::vector<double> extruders_e_per_mm;
  1961. {
  1962. std::set<uint16_t> set_extruders = this->object_extruders(objects);
  1963. append(set_extruders, this->support_material_extruders());
  1964. extruders.reserve(set_extruders.size());
  1965. extruders_e_per_mm.reserve(set_extruders.size());
  1966. for (unsigned int extruder_id : set_extruders) {
  1967. Flow flow = this->skirt_flow(extruder_id);
  1968. double mm3_per_mm = flow.mm3_per_mm();
  1969. extruders.push_back(extruder_id);
  1970. extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config).e_per_mm(mm3_per_mm));
  1971. }
  1972. }
  1973. // Number of skirt loops per skirt layer.
  1974. size_t n_skirts = m_config.skirts.value;
  1975. size_t n_skirts_first_layer = n_skirts + m_config.skirt_brim.value;
  1976. if (this->has_infinite_skirt() && n_skirts == 0)
  1977. n_skirts = 1;
  1978. if (m_config.skirt_brim.value > 0)
  1979. out_first_layer.emplace();
  1980. // Initial offset of the brim inner edge from the object (possible with a support & raft).
  1981. // The skirt will touch the brim if the brim is extruded.
  1982. float distance = float(scale_(m_config.skirt_distance.value) - this->skirt_flow(extruders[extruders.size() - 1]).spacing() / 2.);
  1983. size_t lines_per_extruder = (n_skirts + extruders.size() - 1) / extruders.size();
  1984. size_t current_lines_per_extruder = n_skirts - lines_per_extruder * (extruders.size() - 1);
  1985. // Draw outlines from outside to inside.
  1986. // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
  1987. std::vector<coordf_t> extruded_length(extruders.size(), 0.);
  1988. for (size_t i = std::max(n_skirts, n_skirts_first_layer), extruder_idx = 0, nb_skirts = 1; i > 0; -- i) {
  1989. bool first_layer_only = i <= (n_skirts_first_layer - n_skirts);
  1990. Flow flow = this->skirt_flow(extruders[extruders.size() - (1+ extruder_idx)]);
  1991. float spacing = flow.spacing();
  1992. double mm3_per_mm = flow.mm3_per_mm();
  1993. this->throw_if_canceled();
  1994. // Offset the skirt outside.
  1995. distance += float(scale_(spacing/2));
  1996. // Generate the skirt centerline.
  1997. Polygon loop;
  1998. {
  1999. Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
  2000. //make sure the skirt is simple enough
  2001. Geometry::simplify_polygons(loops, flow.scaled_width() / 10, &loops);
  2002. if (loops.empty())
  2003. break;
  2004. loop = loops.front();
  2005. }
  2006. distance += float(scale_(spacing / 2));
  2007. // Extrude the skirt loop.
  2008. ExtrusionLoop eloop(elrSkirt);
  2009. eloop.paths.emplace_back(ExtrusionPath(
  2010. ExtrusionPath(
  2011. erSkirt,
  2012. (float)mm3_per_mm, // this will be overridden at G-code export time
  2013. flow.width,
  2014. (float)get_first_layer_height() // this will be overridden at G-code export time
  2015. )));
  2016. eloop.paths.back().polyline = loop.split_at_first_point();
  2017. //we make it clowkwise, but as it will be reversed, it will be ccw
  2018. eloop.make_clockwise();
  2019. if(!first_layer_only)
  2020. out.append(eloop);
  2021. if(out_first_layer)
  2022. out_first_layer->append(eloop);
  2023. if (m_config.min_skirt_length.value > 0 && !first_layer_only) {
  2024. // The skirt length is limited. Sum the total amount of filament length extruded, in mm.
  2025. extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
  2026. if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
  2027. // Not extruded enough yet with the current extruder. Add another loop.
  2028. if (i == 1)
  2029. ++ i;
  2030. } else {
  2031. assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
  2032. // Enough extruded with the current extruder. Extrude with the next one,
  2033. // until the prescribed number of skirt loops is extruded.
  2034. if (extruder_idx + 1 < extruders.size())
  2035. if (nb_skirts < current_lines_per_extruder) {
  2036. nb_skirts++;
  2037. } else {
  2038. current_lines_per_extruder = lines_per_extruder;
  2039. nb_skirts = 1;
  2040. ++extruder_idx;
  2041. }
  2042. }
  2043. } else {
  2044. // The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
  2045. }
  2046. }
  2047. // Brims were generated inside out, reverse to print the outmost contour first.
  2048. out.reverse();
  2049. if (out_first_layer)
  2050. out_first_layer->reverse();
  2051. // Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull.
  2052. for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(this->skirt_flow(extruders[extruders.size() - 1]).scaled_spacing()), ClipperLib::jtRound, float(scale_(0.1))))
  2053. append(m_skirt_convex_hull, std::move(poly.points));
  2054. }
  2055. void Print::_extrude_brim_from_tree(std::vector<std::vector<BrimLoop>>& loops, const Polygons& frontiers, const Flow& flow, ExtrusionEntityCollection& out, bool reversed/*= false*/) {
  2056. // nest contour loops (same as in perimetergenerator)
  2057. for (int d = loops.size() - 1; d >= 1; --d) {
  2058. std::vector<BrimLoop>& contours_d = loops[d];
  2059. // loop through all contours having depth == d
  2060. for (int i = 0; i < (int)contours_d.size(); ++i) {
  2061. const BrimLoop& loop = contours_d[i];
  2062. // find the contour loop that contains it
  2063. for (int t = d - 1; t >= 0; --t) {
  2064. for (size_t j = 0; j < loops[t].size(); ++j) {
  2065. BrimLoop& candidate_parent = loops[t][j];
  2066. bool test = reversed
  2067. ? loop.polygon().contains(candidate_parent.lines.front().first_point())
  2068. : candidate_parent.polygon().contains(loop.lines.front().first_point());
  2069. if (test) {
  2070. candidate_parent.children.push_back(loop);
  2071. contours_d.erase(contours_d.begin() + i);
  2072. --i;
  2073. goto NEXT_CONTOUR;
  2074. }
  2075. }
  2076. }
  2077. //didn't find a contour: add it as a root loop
  2078. loops[0].push_back(loop);
  2079. contours_d.erase(contours_d.begin() + i);
  2080. --i;
  2081. NEXT_CONTOUR:;
  2082. }
  2083. }
  2084. for (int i = loops.size() - 1; i > 0; --i) {
  2085. if (loops[i].empty()) {
  2086. loops.erase(loops.begin() + i);
  2087. }
  2088. }
  2089. //def
  2090. //cut loops if they go inside a forbidden region
  2091. std::function<void(BrimLoop&)> cut_loop = [&frontiers, &flow, reversed](BrimLoop& to_cut) {
  2092. Polylines result;
  2093. if (to_cut.is_loop) {
  2094. result = intersection_pl(Polygons{ to_cut.polygon() }, frontiers, true);
  2095. } else {
  2096. result = intersection_pl(to_cut.lines, frontiers, true);
  2097. }
  2098. //remove too small segments
  2099. for (int i = 0; i < result.size(); i++) {
  2100. if (result[i].length() < flow.scaled_width() * 2) {
  2101. result.erase(result.begin() + i);
  2102. i--;
  2103. }
  2104. }
  2105. if (result.empty()) {
  2106. to_cut.lines.clear();
  2107. } else {
  2108. if (to_cut.lines != result) {
  2109. to_cut.lines = result;
  2110. if (reversed) {
  2111. std::reverse(to_cut.lines.begin(), to_cut.lines.end());
  2112. }
  2113. to_cut.is_loop = false;
  2114. }
  2115. }
  2116. };
  2117. //calls, deep-first
  2118. std::list< std::pair<BrimLoop*,int>> cut_child_first;
  2119. for (std::vector<BrimLoop>& loops : loops) {
  2120. for (BrimLoop& loop : loops) {
  2121. cut_child_first.emplace_front(&loop, 0);
  2122. //flat recurtion
  2123. while (!cut_child_first.empty()) {
  2124. if (cut_child_first.front().first->children.size() <= cut_child_first.front().second) {
  2125. //if no child to cut, cut ourself and pop
  2126. cut_loop(*cut_child_first.front().first);
  2127. cut_child_first.pop_front();
  2128. } else {
  2129. // more child to cut, push the next
  2130. cut_child_first.front().second++;
  2131. cut_child_first.emplace_front(&cut_child_first.front().first->children[cut_child_first.front().second - 1], 0);
  2132. }
  2133. }
  2134. }
  2135. }
  2136. this->throw_if_canceled();
  2137. //def: push into extrusions, in the right order
  2138. float mm3_per_mm = float(flow.mm3_per_mm());
  2139. float width = float(flow.width);
  2140. float height = float(get_first_layer_height());
  2141. int nextIdx = 0;
  2142. std::function<void(BrimLoop&, ExtrusionEntityCollection*)>* extrude_ptr;
  2143. std::function<void(BrimLoop&, ExtrusionEntityCollection*) > extrude = [&mm3_per_mm, &width, &height, &extrude_ptr, &nextIdx](BrimLoop& to_cut, ExtrusionEntityCollection* parent) {
  2144. int idx = nextIdx++;
  2145. //bool i_have_line = !to_cut.line.points.empty() && to_cut.line.is_valid();
  2146. bool i_have_line = to_cut.lines.size() > 0 && to_cut.lines.front().size() > 0 && to_cut.lines.front().is_valid();
  2147. if (!i_have_line && to_cut.children.empty()) {
  2148. //nothing
  2149. } else if (i_have_line && to_cut.children.empty()) {
  2150. for(Polyline& line : to_cut.lines)
  2151. if (line.points.back() == line.points.front()) {
  2152. ExtrusionPath path(erSkirt, mm3_per_mm, width, height);
  2153. path.polyline.points = line.points;
  2154. parent->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt));
  2155. } else {
  2156. ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height);
  2157. parent->entities.push_back(extrusion_path);
  2158. extrusion_path->polyline = line;
  2159. }
  2160. } else if (!i_have_line && !to_cut.children.empty()) {
  2161. if (to_cut.children.size() == 1) {
  2162. (*extrude_ptr)(to_cut.children[0], parent);
  2163. } else {
  2164. ExtrusionEntityCollection* mycoll = new ExtrusionEntityCollection();
  2165. //mycoll->no_sort = true;
  2166. parent->entities.push_back(mycoll);
  2167. for (BrimLoop& child : to_cut.children)
  2168. (*extrude_ptr)(child, mycoll);
  2169. //remove un-needed collection if possible
  2170. if (mycoll->entities.size() == 1) {
  2171. parent->entities.back() = mycoll->entities.front();
  2172. mycoll->entities.clear();
  2173. delete mycoll;
  2174. } else if (mycoll->entities.size() == 0) {
  2175. parent->remove(parent->entities.size() - 1);
  2176. }
  2177. }
  2178. } else {
  2179. ExtrusionEntityCollection* print_me_first = new ExtrusionEntityCollection();
  2180. parent->entities.push_back(print_me_first);
  2181. print_me_first->set_can_sort_reverse(false, false);
  2182. for (Polyline& line : to_cut.lines)
  2183. if (line.points.back() == line.points.front()) {
  2184. ExtrusionPath path(erSkirt, mm3_per_mm, width, height);
  2185. path.polyline.points = line.points;
  2186. print_me_first->entities.emplace_back(new ExtrusionLoop(std::move(path), elrSkirt));
  2187. } else {
  2188. ExtrusionPath* extrusion_path = new ExtrusionPath(erSkirt, mm3_per_mm, width, height);
  2189. print_me_first->entities.push_back(extrusion_path);
  2190. extrusion_path->polyline = line;
  2191. }
  2192. if (to_cut.children.size() == 1) {
  2193. (*extrude_ptr)(to_cut.children[0], print_me_first);
  2194. } else {
  2195. ExtrusionEntityCollection* children = new ExtrusionEntityCollection();
  2196. //children->no_sort = true;
  2197. print_me_first->entities.push_back(children);
  2198. for (BrimLoop& child : to_cut.children)
  2199. (*extrude_ptr)(child, children);
  2200. //remove un-needed collection if possible
  2201. if (children->entities.size() == 1) {
  2202. print_me_first->entities.back() = children->entities.front();
  2203. children->entities.clear();
  2204. delete children;
  2205. } else if (children->entities.size() == 0) {
  2206. print_me_first->remove(parent->entities.size() - 1);
  2207. }
  2208. }
  2209. }
  2210. };
  2211. extrude_ptr = &extrude;
  2212. if (loops.empty()) {
  2213. BOOST_LOG_TRIVIAL(error) << "Failed to extrude brim: no loops to extrude, are you sure your settings are ok?";
  2214. return;
  2215. }
  2216. //launch extrude
  2217. for (BrimLoop& loop : loops[0]) {
  2218. extrude(loop, &out);
  2219. }
  2220. }
  2221. //TODO: test if no regression vs old _make_brim.
  2222. // this new one can extrude brim for an object inside an other object.
  2223. void Print::_make_brim(const Flow &flow, const PrintObjectPtrs &objects, ExPolygons &unbrimmable, ExtrusionEntityCollection &out) {
  2224. const coord_t scaled_spacing = flow.scaled_spacing();
  2225. const PrintObjectConfig &brim_config = objects.front()->config();
  2226. coord_t brim_offset = scale_(brim_config.brim_offset.value);
  2227. ExPolygons islands;
  2228. for (PrintObject *object : objects) {
  2229. ExPolygons object_islands;
  2230. for (ExPolygon &expoly : object->m_layers.front()->lslices)
  2231. if(brim_config.brim_inside_holes || brim_config.brim_width_interior > 0)
  2232. object_islands.push_back(brim_offset == 0 ? expoly : offset_ex(expoly, brim_offset)[0]);
  2233. else
  2234. object_islands.emplace_back(brim_offset == 0 ? to_expolygon(expoly.contour) : offset_ex(to_expolygon(expoly.contour), brim_offset)[0]);
  2235. if (!object->support_layers().empty()) {
  2236. Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(flow.spacing_ratio, float(SCALED_EPSILON));
  2237. for (Polygon poly : polys) {
  2238. object_islands.emplace_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]);
  2239. }
  2240. }
  2241. islands.reserve(islands.size() + object_islands.size() * object->m_instances.size());
  2242. for (const PrintInstance &pt : object->m_instances) {
  2243. for (ExPolygon &poly : object_islands) {
  2244. islands.push_back(poly);
  2245. islands.back().translate(pt.shift.x(), pt.shift.y());
  2246. }
  2247. }
  2248. }
  2249. this->throw_if_canceled();
  2250. //simplify & merge
  2251. ExPolygons unbrimmable_areas;
  2252. for (ExPolygon &expoly : islands)
  2253. for (ExPolygon &expoly : expoly.simplify(SCALED_RESOLUTION))
  2254. unbrimmable_areas.emplace_back(std::move(expoly));
  2255. islands = union_ex(unbrimmable_areas, true);
  2256. unbrimmable_areas = islands;
  2257. //get the brimmable area
  2258. const size_t num_loops = size_t(floor(std::max(0.,(brim_config.brim_width.value - brim_config.brim_offset.value)) / flow.spacing()));
  2259. ExPolygons brimmable_areas;
  2260. for (ExPolygon &expoly : islands) {
  2261. for (Polygon poly : offset(expoly.contour, num_loops * scaled_spacing, jtSquare)) {
  2262. brimmable_areas.emplace_back();
  2263. brimmable_areas.back().contour = poly;
  2264. brimmable_areas.back().contour.make_counter_clockwise();
  2265. brimmable_areas.back().holes.push_back(expoly.contour);
  2266. brimmable_areas.back().holes.back().make_clockwise();
  2267. }
  2268. }
  2269. brimmable_areas = union_ex(brimmable_areas);
  2270. this->throw_if_canceled();
  2271. //don't collide with objects
  2272. brimmable_areas = diff_ex(brimmable_areas, unbrimmable_areas, true);
  2273. brimmable_areas = diff_ex(brimmable_areas, unbrimmable, true);
  2274. this->throw_if_canceled();
  2275. //now get all holes, use them to create loops
  2276. std::vector<std::vector<BrimLoop>> loops;
  2277. ExPolygons bigger_islands;
  2278. //grow a half of spacing, to go to the first extrusion polyline.
  2279. Polygons unbrimmable_polygons;
  2280. for (ExPolygon &expoly : islands) {
  2281. unbrimmable_polygons.push_back(expoly.contour);
  2282. //do it separately because we don't want to union them
  2283. for (ExPolygon &big_expoly : offset_ex(expoly, double(scaled_spacing) * 0.5, jtSquare)) {
  2284. bigger_islands.emplace_back(big_expoly);
  2285. unbrimmable_polygons.insert(unbrimmable_polygons.end(), big_expoly.holes.begin(), big_expoly.holes.end());
  2286. }
  2287. }
  2288. islands = bigger_islands;
  2289. for (size_t i = 0; i < num_loops; ++i) {
  2290. loops.emplace_back();
  2291. this->throw_if_canceled();
  2292. // only grow the contour, not holes
  2293. bigger_islands.clear();
  2294. if (i > 0) {
  2295. for (ExPolygon &expoly : islands) {
  2296. for (Polygon &big_contour : offset(expoly.contour, double(scaled_spacing) * i, jtSquare)) {
  2297. bigger_islands.emplace_back(expoly);
  2298. bigger_islands.back().contour = big_contour;
  2299. }
  2300. }
  2301. } else bigger_islands = islands;
  2302. bigger_islands = union_ex(bigger_islands);
  2303. for (ExPolygon &expoly : bigger_islands) {
  2304. loops[i].emplace_back(expoly.contour);
  2305. // buggy
  2306. ////also add hole, in case of it's merged with a contour. <= HOW? if there's an island inside a hole! (in the same object)
  2307. //for (Polygon &hole : expoly.holes)
  2308. // //but remove the points that are inside the holes of islands
  2309. // for (Polyline& pl : diff_pl(Polygons{ hole }, unbrimmable_polygons, true))
  2310. // loops[i].emplace_back(pl);
  2311. }
  2312. }
  2313. std::reverse(loops.begin(), loops.end());
  2314. //intersection
  2315. Polygons frontiers;
  2316. //use contour from brimmable_areas (external frontier)
  2317. for (ExPolygon &expoly : brimmable_areas) {
  2318. frontiers.push_back(expoly.contour);
  2319. frontiers.back().make_counter_clockwise();
  2320. }
  2321. // add internal frontier
  2322. frontiers.insert(frontiers.begin(), unbrimmable_polygons.begin(), unbrimmable_polygons.end());
  2323. _extrude_brim_from_tree(loops, frontiers, flow, out);
  2324. unbrimmable.insert(unbrimmable.end(), brimmable_areas.begin(), brimmable_areas.end());
  2325. }
  2326. void Print::_make_brim_ears(const Flow &flow, const PrintObjectPtrs &objects, ExPolygons &unbrimmable, ExtrusionEntityCollection &out) {
  2327. const PrintObjectConfig &brim_config = objects.front()->config();
  2328. Points pt_ears;
  2329. coord_t brim_offset = scale_(brim_config.brim_offset.value);
  2330. ExPolygons islands;
  2331. ExPolygons unbrimmable_with_support = unbrimmable;
  2332. for (PrintObject *object : objects) {
  2333. ExPolygons object_islands;
  2334. ExPolygons support_island;
  2335. for (const ExPolygon &expoly : object->m_layers.front()->lslices)
  2336. if (brim_config.brim_inside_holes || brim_config.brim_width_interior > 0)
  2337. object_islands.push_back(brim_offset==0?expoly:offset_ex(expoly, brim_offset)[0]);
  2338. else
  2339. object_islands.emplace_back(brim_offset == 0 ? to_expolygon(expoly.contour) : offset_ex(to_expolygon(expoly.contour), brim_offset)[0]);
  2340. if (!object->support_layers().empty()) {
  2341. Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(flow.spacing_ratio, float(SCALED_EPSILON));
  2342. //put ears over supports unless it's 100% fill
  2343. if (object->config().support_material_solid_first_layer) {
  2344. for (Polygon poly : polys) {
  2345. object_islands.push_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]);
  2346. }
  2347. } else {
  2348. // offset2 to avoid bits of brim inside the raft
  2349. append(support_island, offset2_ex(polys, flow.scaled_width() * 2, -flow.scaled_width() * 2));
  2350. }
  2351. }
  2352. islands.reserve(islands.size() + object_islands.size() * object->m_instances.size());
  2353. coord_t ear_detection_length = scale_t(object->config().brim_ears_detection_length.value);
  2354. // duplicate & translate for each instance
  2355. for (const PrintInstance& copy_pt : object->m_instances) {
  2356. for (const ExPolygon& poly : object_islands) {
  2357. islands.push_back(poly);
  2358. islands.back().translate(copy_pt.shift.x(), copy_pt.shift.y());
  2359. Polygon decimated_polygon = poly.contour;
  2360. // brim_ears_detection_length codepath
  2361. if (ear_detection_length > 0) {
  2362. //decimate polygon
  2363. Points points = poly.contour.points;
  2364. points.push_back(points.front());
  2365. points = MultiPoint::_douglas_peucker(points, ear_detection_length);
  2366. if (points.size() > 4) { //don't decimate if it's going to be below 4 points, as it's surely enough to fill everything anyway
  2367. points.erase(points.end() - 1);
  2368. decimated_polygon.points = points;
  2369. }
  2370. }
  2371. for (const Point& p : decimated_polygon.convex_points(brim_config.brim_ears_max_angle.value* PI / 180.0)) {
  2372. pt_ears.push_back(p);
  2373. pt_ears.back() += (copy_pt.shift);
  2374. }
  2375. }
  2376. // also for support-fobidden area
  2377. for (const ExPolygon& poly : support_island) {
  2378. unbrimmable_with_support.push_back(poly);
  2379. unbrimmable_with_support.back().translate(copy_pt.shift.x(), copy_pt.shift.y());
  2380. }
  2381. }
  2382. }
  2383. islands = union_ex(islands, true);
  2384. //get the brimmable area (for the return value only)
  2385. const size_t num_loops = size_t(floor((brim_config.brim_width.value - brim_config.brim_offset.value) / flow.spacing()));
  2386. ExPolygons brimmable_areas;
  2387. Polygons contours;
  2388. Polygons holes;
  2389. for (ExPolygon &expoly : islands) {
  2390. for (Polygon poly : offset(expoly.contour, num_loops * flow.scaled_width(), jtSquare)) {
  2391. contours.push_back(poly);
  2392. }
  2393. holes.push_back(expoly.contour);
  2394. }
  2395. brimmable_areas = diff_ex(union_(contours), union_(holes));
  2396. brimmable_areas = diff_ex(brimmable_areas, unbrimmable_with_support, true);
  2397. this->throw_if_canceled();
  2398. if (brim_config.brim_ears_pattern.value == InfillPattern::ipConcentric) {
  2399. //create loops (same as standard brim)
  2400. Polygons loops;
  2401. islands = offset_ex(islands, -0.5f * double(flow.scaled_spacing()));
  2402. for (size_t i = 0; i < num_loops; ++i) {
  2403. this->throw_if_canceled();
  2404. islands = offset_ex(islands, double(flow.scaled_spacing()), jtSquare);
  2405. for (ExPolygon &expoly : islands) {
  2406. Polygon poly = expoly.contour;
  2407. poly.points.push_back(poly.points.front());
  2408. Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION);
  2409. p.pop_back();
  2410. poly.points = std::move(p);
  2411. loops.push_back(poly);
  2412. }
  2413. }
  2414. //order path with least travel possible
  2415. loops = union_pt_chained_outside_in(loops, false);
  2416. //create ear pattern
  2417. coord_t size_ear = (scale_((brim_config.brim_width.value - brim_config.brim_offset.value)) - flow.scaled_spacing());
  2418. Polygon point_round;
  2419. for (size_t i = 0; i < POLY_SIDES; i++) {
  2420. double angle = (2.0 * PI * i) / POLY_SIDES;
  2421. point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
  2422. }
  2423. //create ears
  2424. ExPolygons mouse_ears_ex;
  2425. for (Point pt : pt_ears) {
  2426. mouse_ears_ex.emplace_back();
  2427. mouse_ears_ex.back().contour = point_round;
  2428. mouse_ears_ex.back().contour.translate(pt);
  2429. }
  2430. //intersection
  2431. ExPolygons mouse_ears_area = intersection_ex(mouse_ears_ex, brimmable_areas);
  2432. Polylines lines = intersection_pl(loops, to_polygons(mouse_ears_area));
  2433. this->throw_if_canceled();
  2434. //reorder & extrude them
  2435. Polylines lines_sorted = _reorder_brim_polyline(lines, out, flow);
  2436. //push into extrusions
  2437. extrusion_entities_append_paths(
  2438. out.entities,
  2439. lines_sorted,
  2440. erSkirt,
  2441. float(flow.mm3_per_mm()),
  2442. float(flow.width),
  2443. float(get_first_layer_height())
  2444. );
  2445. unbrimmable = union_ex(unbrimmable, offset_ex(mouse_ears_ex, flow.scaled_spacing()/2));
  2446. } else /* brim_config.brim_ears_pattern.value == InfillPattern::ipRectilinear */{
  2447. //create ear pattern
  2448. coord_t size_ear = (scale_((brim_config.brim_width.value - brim_config.brim_offset.value)) - flow.scaled_spacing());
  2449. Polygon point_round;
  2450. for (size_t i = 0; i < POLY_SIDES; i++) {
  2451. double angle = (2.0 * PI * i) / POLY_SIDES;
  2452. point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
  2453. }
  2454. //create ears
  2455. ExPolygons mouse_ears_ex;
  2456. for (Point pt : pt_ears) {
  2457. mouse_ears_ex.emplace_back();
  2458. mouse_ears_ex.back().contour = point_round;
  2459. mouse_ears_ex.back().contour.translate(pt);
  2460. }
  2461. ExPolygons new_brim_area = intersection_ex(brimmable_areas, mouse_ears_ex);
  2462. std::unique_ptr<Fill> filler = std::unique_ptr<Fill>(Fill::new_from_type(ipRectiWithPerimeter));
  2463. filler->angle = 0;
  2464. FillParams fill_params;
  2465. fill_params.density = 1.f;
  2466. fill_params.fill_exactly = true;
  2467. fill_params.flow = flow;
  2468. fill_params.role = erSkirt;
  2469. filler->init_spacing(flow.spacing(), fill_params);
  2470. for (const ExPolygon &expoly : new_brim_area) {
  2471. Surface surface(stPosInternal | stDensSparse, expoly);
  2472. filler->fill_surface_extrusion(&surface, fill_params, out.entities);
  2473. }
  2474. unbrimmable.insert(unbrimmable.end(), new_brim_area.begin(), new_brim_area.end());
  2475. }
  2476. }
  2477. void Print::_make_brim_interior(const Flow &flow, const PrintObjectPtrs &objects, ExPolygons &unbrimmable_areas, ExtrusionEntityCollection &out) {
  2478. // Brim is only printed on first layer and uses perimeter extruder.
  2479. const PrintObjectConfig &brim_config = objects.front()->config();
  2480. coord_t brim_offset = scale_(brim_config.brim_offset.value);
  2481. ExPolygons islands;
  2482. coordf_t spacing;
  2483. for (PrintObject *object : objects) {
  2484. ExPolygons object_islands;
  2485. for (ExPolygon &expoly : object->m_layers.front()->lslices)
  2486. object_islands.push_back(brim_offset == 0 ? expoly : offset_ex(expoly, brim_offset)[0]);
  2487. if (!object->support_layers().empty()) {
  2488. spacing = scaled(object->config().support_material_interface_spacing.value) + support_material_flow(object, float(get_first_layer_height())).scaled_width() * 1.5;
  2489. Polygons polys = offset2(object->support_layers().front()->support_fills.polygons_covered_by_spacing(flow.spacing_ratio, float(SCALED_EPSILON)), spacing, -spacing);
  2490. for (Polygon poly : polys) {
  2491. object_islands.push_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]);
  2492. }
  2493. }
  2494. islands.reserve(islands.size() + object_islands.size() * object->instances().size());
  2495. for (const PrintInstance &instance : object->instances())
  2496. for (ExPolygon &poly : object_islands) {
  2497. islands.push_back(poly);
  2498. islands.back().translate(instance.shift.x(), instance.shift.y());
  2499. }
  2500. }
  2501. islands = union_ex(islands);
  2502. //to have the brimmable areas, get all holes, use them as contour , add smaller hole inside and make a diff with unbrimmable
  2503. const size_t num_loops = size_t(floor((brim_config.brim_width_interior.value - brim_config.brim_offset.value) / flow.spacing()));
  2504. ExPolygons brimmable_areas;
  2505. Polygons islands_to_loops;
  2506. for (const ExPolygon &expoly : islands) {
  2507. for (const Polygon &hole : expoly.holes) {
  2508. brimmable_areas.emplace_back();
  2509. brimmable_areas.back().contour = hole;
  2510. brimmable_areas.back().contour.make_counter_clockwise();
  2511. for (Polygon poly : offset(brimmable_areas.back().contour, -flow.scaled_width() * (double)num_loops, jtSquare)) {
  2512. brimmable_areas.back().holes.push_back(poly);
  2513. brimmable_areas.back().holes.back().make_clockwise();
  2514. }
  2515. islands_to_loops.insert(islands_to_loops.begin(), brimmable_areas.back().contour);
  2516. }
  2517. }
  2518. brimmable_areas = diff_ex(brimmable_areas, islands, true);
  2519. brimmable_areas = diff_ex(brimmable_areas, unbrimmable_areas, true);
  2520. //now get all holes, use them to create loops
  2521. std::vector<std::vector<BrimLoop>> loops;
  2522. for (size_t i = 0; i < num_loops; ++i) {
  2523. this->throw_if_canceled();
  2524. loops.emplace_back();
  2525. Polygons islands_to_loops_offseted;
  2526. for (Polygon& poly : islands_to_loops) {
  2527. Polygons temp = offset(poly, double(-flow.scaled_spacing()), jtSquare);
  2528. for (Polygon& poly : temp) {
  2529. poly.points.push_back(poly.points.front());
  2530. Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION);
  2531. p.pop_back();
  2532. poly.points = std::move(p);
  2533. }
  2534. for (Polygon& poly : offset(temp, 0.5f * double(flow.scaled_spacing())))
  2535. loops[i].emplace_back(poly);
  2536. islands_to_loops_offseted.insert(islands_to_loops_offseted.end(), temp.begin(), temp.end());
  2537. }
  2538. islands_to_loops = islands_to_loops_offseted;
  2539. }
  2540. //loops = union_pt_chained_outside_in(loops, false);
  2541. std::reverse(loops.begin(), loops.end());
  2542. //intersection
  2543. Polygons frontiers;
  2544. for (ExPolygon &expoly : brimmable_areas) {
  2545. for (Polygon &big_contour : offset(expoly.contour, 0.1f * flow.scaled_width())) {
  2546. frontiers.push_back(big_contour);
  2547. for (Polygon &hole : expoly.holes) {
  2548. frontiers.push_back(hole);
  2549. //don't reverse it! back! or it will be ignored by intersection_pl.
  2550. //frontiers.back().reverse();
  2551. }
  2552. }
  2553. }
  2554. _extrude_brim_from_tree(loops, frontiers, flow, out, true);
  2555. unbrimmable_areas.insert(unbrimmable_areas.end(), brimmable_areas.begin(), brimmable_areas.end());
  2556. }
  2557. /// reorder & join polyline if their ending are near enough, then extrude the brim from the polyline into 'out'.
  2558. Polylines Print::_reorder_brim_polyline(Polylines lines, ExtrusionEntityCollection &out, const Flow &flow) {
  2559. //reorder them
  2560. std::sort(lines.begin(), lines.end(), [](const Polyline &a, const Polyline &b)->bool { return a.closest_point(Point(0, 0))->y() < b.closest_point(Point(0, 0))->y(); });
  2561. Polylines lines_sorted;
  2562. Polyline* previous = NULL;
  2563. Polyline* best = NULL;
  2564. double best_dist = -1;
  2565. size_t best_idx = 0;
  2566. while (lines.size() > 0) {
  2567. if (previous == NULL) {
  2568. lines_sorted.push_back(lines.back());
  2569. previous = &lines_sorted.back();
  2570. lines.erase(lines.end() - 1);
  2571. } else {
  2572. best = NULL;
  2573. best_dist = -1;
  2574. best_idx = 0;
  2575. for (size_t i = 0; i < lines.size(); ++i) {
  2576. Polyline &viewed_line = lines[i];
  2577. double dist = viewed_line.points.front().distance_to(previous->points.front());
  2578. dist = std::min(dist, viewed_line.points.front().distance_to(previous->points.back()));
  2579. dist = std::min(dist, viewed_line.points.back().distance_to(previous->points.front()));
  2580. dist = std::min(dist, viewed_line.points.back().distance_to(previous->points.back()));
  2581. if (dist < best_dist || best == NULL) {
  2582. best = &viewed_line;
  2583. best_dist = dist;
  2584. best_idx = i;
  2585. }
  2586. }
  2587. if (best != NULL) {
  2588. //copy new line inside the sorted array.
  2589. lines_sorted.push_back(lines[best_idx]);
  2590. lines.erase(lines.begin() + best_idx);
  2591. //connect if near enough
  2592. if (lines_sorted.size() > 1) {
  2593. size_t idx = lines_sorted.size() - 2;
  2594. bool connect = false;
  2595. if (lines_sorted[idx].points.back().distance_to(lines_sorted[idx + 1].points.front()) < flow.scaled_spacing() * 2) {
  2596. connect = true;
  2597. } else if (lines_sorted[idx].points.back().distance_to(lines_sorted[idx + 1].points.back()) < flow.scaled_spacing() * 2) {
  2598. lines_sorted[idx + 1].reverse();
  2599. connect = true;
  2600. } else if (lines_sorted[idx].points.front().distance_to(lines_sorted[idx + 1].points.front()) < flow.scaled_spacing() * 2) {
  2601. lines_sorted[idx].reverse();
  2602. connect = true;
  2603. } else if (lines_sorted[idx].points.front().distance_to(lines_sorted[idx + 1].points.back()) < flow.scaled_spacing() * 2) {
  2604. lines_sorted[idx].reverse();
  2605. lines_sorted[idx + 1].reverse();
  2606. connect = true;
  2607. }
  2608. if (connect) {
  2609. //connect them
  2610. lines_sorted[idx].points.insert(
  2611. lines_sorted[idx].points.end(),
  2612. lines_sorted[idx + 1].points.begin(),
  2613. lines_sorted[idx + 1].points.end());
  2614. lines_sorted.erase(lines_sorted.begin() + idx + 1);
  2615. idx--;
  2616. }
  2617. }
  2618. //update last position
  2619. previous = &lines_sorted.back();
  2620. }
  2621. }
  2622. }
  2623. return lines_sorted;
  2624. }
  2625. Polygons Print::first_layer_islands() const
  2626. {
  2627. Polygons islands;
  2628. for (PrintObject *object : m_objects) {
  2629. Polygons object_islands;
  2630. for (ExPolygon &expoly : object->m_layers.front()->lslices)
  2631. object_islands.push_back(expoly.contour);
  2632. if (! object->support_layers().empty())
  2633. //was polygons_covered_by_spacing, but is it really important?
  2634. object->support_layers().front()->support_fills.polygons_covered_by_width(object_islands, float(SCALED_EPSILON));
  2635. islands.reserve(islands.size() + object_islands.size() * object->instances().size());
  2636. for (const PrintInstance &instance : object->instances())
  2637. for (Polygon &poly : object_islands) {
  2638. islands.push_back(poly);
  2639. islands.back().translate(instance.shift);
  2640. }
  2641. }
  2642. return islands;
  2643. }
  2644. std::vector<Point> Print::first_layer_wipe_tower_corners() const
  2645. {
  2646. std::vector<Point> corners;
  2647. if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) {
  2648. double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;
  2649. double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width;
  2650. Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width);
  2651. for (Vec2d pt : {
  2652. pt0,
  2653. Vec2d(pt0.x()+width, pt0.y() ),
  2654. Vec2d(pt0.x()+width, pt0.y()+depth),
  2655. Vec2d(pt0.x(), pt0.y()+depth)
  2656. }) {
  2657. pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt;
  2658. pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value);
  2659. corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
  2660. }
  2661. }
  2662. return corners;
  2663. }
  2664. void Print::finalize_first_layer_convex_hull()
  2665. {
  2666. append(m_first_layer_convex_hull.points, m_skirt_convex_hull);
  2667. if (m_first_layer_convex_hull.empty()) {
  2668. // Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer.
  2669. for (Polygon &poly : this->first_layer_islands())
  2670. append(m_first_layer_convex_hull.points, std::move(poly.points));
  2671. }
  2672. append(m_first_layer_convex_hull.points, this->first_layer_wipe_tower_corners());
  2673. m_first_layer_convex_hull = Geometry::convex_hull(m_first_layer_convex_hull.points);
  2674. }
  2675. // Wipe tower support.
  2676. bool Print::has_wipe_tower() const
  2677. {
  2678. return
  2679. ! m_config.spiral_vase.value &&
  2680. m_config.wipe_tower.value &&
  2681. m_config.nozzle_diameter.values.size() > 1;
  2682. }
  2683. const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_layer_height, double nozzle_diameter) const
  2684. {
  2685. // If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
  2686. if (! is_step_done(psWipeTower) && extruders_cnt !=0) {
  2687. float width = float(m_config.wipe_tower_width);
  2688. float unscaled_brim_width = m_config.wipe_tower_brim.get_abs_value(nozzle_diameter);
  2689. const_cast<Print*>(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1);
  2690. const_cast<Print*>(this)->m_wipe_tower_data.brim_width = unscaled_brim_width;
  2691. }
  2692. return m_wipe_tower_data;
  2693. }
  2694. void Print::_make_wipe_tower()
  2695. {
  2696. m_wipe_tower_data.clear();
  2697. if (! this->has_wipe_tower())
  2698. return;
  2699. // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
  2700. std::vector<float> wiping_matrix(cast<float>(m_config.wiping_volumes_matrix.values));
  2701. // Extract purging volumes for each extruder pair:
  2702. std::vector<std::vector<float>> wipe_volumes;
  2703. const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
  2704. for (unsigned int i = 0; i<number_of_extruders; ++i)
  2705. wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
  2706. // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
  2707. m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
  2708. if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
  2709. // Don't generate any wipe tower.
  2710. return;
  2711. // Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower,
  2712. // they print neither object, nor support. These layers are above the raft and below the object, and they
  2713. // shall be added to the support layers to be printed.
  2714. // see https://github.com/prusa3d/PrusaSlicer/issues/607
  2715. {
  2716. size_t idx_begin = size_t(-1);
  2717. size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size();
  2718. // Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
  2719. for (size_t i = 0; i < idx_end; ++ i) {
  2720. const LayerTools &lt = m_wipe_tower_data.tool_ordering.layer_tools()[i];
  2721. if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
  2722. idx_begin = i;
  2723. break;
  2724. }
  2725. }
  2726. if (idx_begin != size_t(-1)) {
  2727. // Find the position in m_objects.first()->support_layers to insert these new support layers.
  2728. double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z;
  2729. SupportLayerPtrs::const_iterator it_layer = m_objects.front()->support_layers().begin();
  2730. SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end();
  2731. for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
  2732. // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
  2733. for (size_t i = idx_begin; i < idx_end; ++ i) {
  2734. LayerTools &lt = const_cast<LayerTools&>(m_wipe_tower_data.tool_ordering.layer_tools()[i]);
  2735. if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
  2736. break;
  2737. lt.has_support = true;
  2738. // Insert the new support layer.
  2739. double height = lt.print_z - (i == 0 ? 0. : m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z);
  2740. //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
  2741. it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
  2742. ++ it_layer;
  2743. }
  2744. }
  2745. }
  2746. this->throw_if_canceled();
  2747. // Initialize the wipe tower.
  2748. WipeTower wipe_tower(m_config, m_default_object_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder());
  2749. //wipe_tower.set_retract();
  2750. //wipe_tower.set_zhop();
  2751. // Set the extruder & material properties at the wipe tower object.
  2752. for (size_t i = 0; i < number_of_extruders; ++i)
  2753. wipe_tower.set_extruder(i);
  2754. m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
  2755. wipe_tower.prime((float)get_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
  2756. // Lets go through the wipe tower layers and determine pairs of extruder changes for each
  2757. // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
  2758. {
  2759. unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
  2760. for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
  2761. if (!layer_tools.has_wipe_tower) continue;
  2762. bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
  2763. wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
  2764. for (const auto extruder_id : layer_tools.extruders) {
  2765. if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
  2766. double volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
  2767. if (m_config.wipe_advanced) {
  2768. volume_to_wipe = m_config.wipe_advanced_nozzle_melted_volume;
  2769. float pigmentBef = m_config.filament_wipe_advanced_pigment.get_at(current_extruder_id);
  2770. float pigmentAft = m_config.filament_wipe_advanced_pigment.get_at(extruder_id);
  2771. if (m_config.wipe_advanced_algo.value == waLinear) {
  2772. volume_to_wipe += m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft);
  2773. BOOST_LOG_TRIVIAL(info) << "advanced wiping (lin) ";
  2774. BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n";
  2775. BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value
  2776. << " * ( " << pigmentBef << " - " << pigmentAft << " )\n";
  2777. BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft)) << "\n";
  2778. } else if (m_config.wipe_advanced_algo.value == waQuadra) {
  2779. volume_to_wipe += m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft)
  2780. + m_config.wipe_advanced_multiplier.value * (pigmentBef - pigmentAft) * (pigmentBef - pigmentAft) * (pigmentBef - pigmentAft);
  2781. BOOST_LOG_TRIVIAL(info) << "advanced wiping (quadra) ";
  2782. BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n";
  2783. BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value
  2784. << " * ( " << pigmentBef << " - " << pigmentAft << " ) + " << m_config.wipe_advanced_multiplier.value
  2785. << " * ( " << pigmentBef << " - " << pigmentAft << " ) ^3 \n";
  2786. BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value* (pigmentBef - pigmentAft))
  2787. << " + " << (m_config.wipe_advanced_multiplier.value*(pigmentBef - pigmentAft)*(pigmentBef - pigmentAft)*(pigmentBef - pigmentAft))<<"\n";
  2788. } else if (m_config.wipe_advanced_algo.value == waHyper) {
  2789. volume_to_wipe += m_config.wipe_advanced_multiplier.value * (0.5 + pigmentBef) / (0.5 + pigmentAft);
  2790. BOOST_LOG_TRIVIAL(info) << "advanced wiping (hyper) ";
  2791. BOOST_LOG_TRIVIAL(info) << current_extruder_id << " -> " << extruder_id << " will use " << volume_to_wipe << " mm3\n";
  2792. BOOST_LOG_TRIVIAL(info) << " calculus : " << m_config.wipe_advanced_nozzle_melted_volume << " + " << m_config.wipe_advanced_multiplier.value
  2793. << " * ( 0.5 + " << pigmentBef << " ) / ( 0.5 + " << pigmentAft << " )\n";
  2794. BOOST_LOG_TRIVIAL(info) << " = " << m_config.wipe_advanced_nozzle_melted_volume << " + " << (m_config.wipe_advanced_multiplier.value * (0.5 + pigmentBef) / (0.5 + pigmentAft)) << "\n";
  2795. }
  2796. }
  2797. //filament_wipe_advanced_pigment
  2798. // Not all of that can be used for infill purging:
  2799. volume_to_wipe -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
  2800. // try to assign some infills/objects for the wiping:
  2801. volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
  2802. // add back the minimal amount toforce on the wipe tower:
  2803. volume_to_wipe += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
  2804. // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
  2805. wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
  2806. first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
  2807. current_extruder_id = extruder_id;
  2808. }
  2809. }
  2810. layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
  2811. if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
  2812. break;
  2813. }
  2814. }
  2815. // Generate the wipe tower layers.
  2816. m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
  2817. wipe_tower.generate(m_wipe_tower_data.tool_changes);
  2818. m_wipe_tower_data.depth = wipe_tower.get_depth();
  2819. m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
  2820. // Unload the current filament over the purge tower.
  2821. coordf_t layer_height = m_objects.front()->config().layer_height.value;
  2822. if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
  2823. // The wipe tower goes up to the last layer of the print.
  2824. if (wipe_tower.layer_finished()) {
  2825. // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
  2826. // Lift Z to the next layer.
  2827. wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false, true);
  2828. } else {
  2829. // There is yet enough space at this layer of the wipe tower for the final purge.
  2830. }
  2831. } else {
  2832. // The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
  2833. assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
  2834. wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
  2835. }
  2836. m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
  2837. wipe_tower.tool_change((unsigned int)(-1)));
  2838. m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
  2839. m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
  2840. }
  2841. // Generate a recommended G-code output file name based on the format template, default extension, and template parameters
  2842. // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
  2843. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).
  2844. std::string Print::output_filename(const std::string &filename_base) const
  2845. {
  2846. // Set the placeholders for the data know first after the G-code export is finished.
  2847. // These values will be just propagated into the output file name.
  2848. DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
  2849. config.set_key_value("num_extruders", new ConfigOptionInt((int)m_config.nozzle_diameter.size()));
  2850. return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config);
  2851. }
  2852. DynamicConfig PrintStatistics::config() const
  2853. {
  2854. DynamicConfig config;
  2855. std::string normal_print_time = short_time(this->estimated_normal_print_time);
  2856. std::string silent_print_time = short_time(this->estimated_silent_print_time);
  2857. config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
  2858. config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
  2859. config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
  2860. config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.));
  2861. config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume));
  2862. config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
  2863. config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
  2864. config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
  2865. config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
  2866. config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
  2867. return config;
  2868. }
  2869. DynamicConfig PrintStatistics::placeholders()
  2870. {
  2871. DynamicConfig config;
  2872. for (const std::string &key : {
  2873. "print_time", "normal_print_time", "silent_print_time",
  2874. "used_filament", "extruded_volume", "total_cost", "total_weight",
  2875. "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
  2876. config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
  2877. return config;
  2878. }
  2879. std::string PrintStatistics::finalize_output_path(const std::string &path_in) const
  2880. {
  2881. std::string final_path;
  2882. try {
  2883. boost::filesystem::path path(path_in);
  2884. DynamicConfig cfg = this->config();
  2885. PlaceholderParser pp;
  2886. std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
  2887. final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
  2888. } catch (const std::exception &ex) {
  2889. BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
  2890. final_path = path_in;
  2891. }
  2892. return final_path;
  2893. }
  2894. } // namespace Slic3r