Comparar commits

..

1068 Commits

Autor SHA1 Mensagem Data
Tim Kuipers 4a13deedad feat: show at which settingsbase a settings is initially retrieved from 2016-06-03 14:05:22 +02:00
Tim Kuipers 0e4b64b8f2 fix: record settingsbase name string 2016-06-03 14:04:48 +02:00
Tim Kuipers 8318f290fa fix: better GCodePlannerTest (CURA-1560) 2016-06-03 13:13:28 +02:00
Tim Kuipers 191f699309 fix: testcase GCodePlannerTest restored (CURA-1560) 2016-06-03 13:09:32 +02:00
Tim Kuipers 2dac16b44f refactor: remove unnecessary argument parent_file from SettingRegistry::getDefinitionFile (CURA-1574) 2016-06-03 12:47:32 +02:00
Tim Kuipers 9db735e2e5 fix: record search paths for extruder train defs (CURA-1574) 2016-06-03 12:44:18 +02:00
Tim Kuipers ee087f23de fix: wall_line_width/count not retrieved per extruder anymore (CURA-1560) 2016-06-03 00:02:50 +02:00
Tim Kuipers 5878916b03 fix: retrieve travel_avoid_distance per extruder and use the highest value (CURA-1560) 2016-06-02 23:24:24 +02:00
Tim Kuipers 7e7dbf34be refactor: rename some vars from storage ==> mesh (CURA-1560) 2016-06-02 23:02:14 +02:00
Tim Kuipers c3d535d88f fix: adhesion_type was retrieved per mesh (CURA-1560) 2016-06-02 22:59:49 +02:00
Tim Kuipers 2b987a9a73 DEBUG: commented a whole testcase (CURA-1560) 2016-06-02 22:53:18 +02:00
Tim Kuipers 315f01daef refactor: removed global retrieval of travel config (CURA-1560) 2016-06-02 22:50:26 +02:00
Tim Kuipers fdc756a22b refactor: moved sliceDataStorage initialization to header file (CURA-1560) 2016-06-02 22:28:46 +02:00
Tim Kuipers 497c969048 refactor: removed global travel config (CURA-1560) 2016-06-02 22:23:07 +02:00
Tim Kuipers e22d4d30e7 refactor: removed global retraction config (CURA-1560) 2016-06-02 21:40:22 +02:00
Tim Kuipers 432761690b fix: removed (globally retrieved) travel speed dependancy from finalize (CURA-1560) 2016-06-02 18:25:23 +02:00
Tim Kuipers 39422e6dbc fix: platform adhesion options now all retrieved from extruder train (CURA-1560) 2016-06-02 18:22:23 +02:00
Tim Kuipers 0f9c14e42a fix: temps set multiple times and temps retrieved per mesh (CURA-1560) 2016-06-02 17:29:22 +02:00
Tim Kuipers d9374270cf fix: magic_spiralize was retrieved globally (CURA-1560) 2016-06-02 17:10:28 +02:00
Tim Kuipers 9f8cdf69f0 fix: layer_height was retrieved per mesh (CURA-1560) 2016-06-02 17:04:15 +02:00
Tim Kuipers 492e4f7b29 fix: machine_name was never actually loaded in the settings (CURA-1574) 2016-06-01 17:19:59 +02:00
Tim Kuipers b50bc938df lil refactor 2016-06-01 15:58:40 +02:00
Tim Kuipers aa36ad175e fix: overlaps were compensated twice when layer would start within an overlap zone (CURA-1635) 2016-06-01 15:56:58 +02:00
Tim Kuipers 0d50fe50b4 fix: heuristic solution to circumvent iterator invalidation (CURA-1635)
complex models with almost everywhere pieces smaller than twice the nozzle width could break the heuristic, when there are a lot of 3-way intersections of thin walls
2016-06-01 15:54:52 +02:00
Tim Kuipers 8bf31f771c refactor: moved WallOverlapComputation constructor to cpp file (CURA-1635) 2016-06-01 13:47:25 +02:00
Tim Kuipers 634fddd908 lil doc 2016-06-01 13:32:24 +02:00
Tim Kuipers 7f64494dec lil refactor (CURA-1635) 2016-06-01 13:27:10 +02:00
Tim Kuipers dffb1ef459 doc: lil doc for walloverlap comp (CURA-1635) 2016-06-01 13:22:43 +02:00
Tim Kuipers 9b74c6c0a2 better debugging for wallOverlapComp 2016-06-01 13:21:44 +02:00
Tim Kuipers b23ede94f3 lil debug fix for WallOverlapComputation 2016-06-01 11:53:27 +02:00
Tim Kuipers 1837d658dd fix: infill_area_per_combine wasn't always filled (CURA-1255) 2016-05-31 15:27:30 +02:00
Tim Kuipers 7086762216 refactor: early out for polygon settings (which are unsupported) (CURA-1574) 2016-05-31 12:43:04 +02:00
Tim Kuipers bc78163d70 fix: settings default values got deleted when overriden by json object which didn't define a default_value (CURA-1574) 2016-05-31 12:41:56 +02:00
Tim Kuipers 81c2091ddb lil fix in help (CURA-1574) 2016-05-30 17:05:07 +02:00
Tim Kuipers 6e3d71e39b fix: load machine_extruder_trains ids from machine definition after the recursion, so that the parent json file doesn't overload the current (CURA-1574) 2016-05-30 17:02:27 +02:00
Tim Kuipers 35112600bc fix: dont warn for duplicated for non-base files and for extruder trains (CURA-1574)
extruder train base definition file gets loaded for each extruder, and we don't want to get duplicate errors every time we load the second extruder
2016-05-30 17:00:29 +02:00
Tim Kuipers 0e58fb960f feat: load extruder train defaults (and definitions) on creation of new extruder train (CURA-1574) 2016-05-30 16:44:42 +02:00
Tim Kuipers a36c90d076 clenaup: removed unused function in settingsRegistry (CURA-1574) 2016-05-30 16:36:10 +02:00
Tim Kuipers b72625b28f feat: settingsRewgistry::loadExtruderJSONsettings (CURA-1574) 2016-05-30 16:35:44 +02:00
Tim Kuipers 8ead13d2be cleanup: removed old extruder_trains unused default saving mechanism (CURA-1574) 2016-05-30 16:27:31 +02:00
Tim Kuipers d30d2af4f0 feat: remember extruder train ids from json (CURA-1574) 2016-05-30 16:20:46 +02:00
Tim Kuipers 2f46c0e473 fix: make path delimiter depend on OS (CURA-1574) 2016-05-30 11:27:54 +02:00
Tim Kuipers 34f60e6616 lil codestyle 2016-05-30 11:08:41 +02:00
Tim Kuipers 303246b39c fix: generateMultipleVolumesOverlap was broken (CURA-833) 2016-05-27 13:45:10 +02:00
Tim Kuipers 3b80ac93ea lil prime tower fix 2016-05-27 13:30:16 +02:00
Tim Kuipers 6b7b5a7ea9 refactor: load setting defaults into settings at the moment they get loaded (CURA-1574) 2016-05-27 13:27:48 +02:00
Tim Kuipers 231eee1e46 lil prime tower fix 2016-05-27 13:26:25 +02:00
Tim Kuipers 81424528f3 refactor: no more overload_defaults_only (CURA-1574) 2016-05-27 13:00:17 +02:00
Tim Kuipers c78808b69d lil 2016-05-27 12:59:37 +02:00
Tim Kuipers 1c347be3be fix: don't overload defaults only when loading extruder json files (CURA-1574) 2016-05-27 12:53:38 +02:00
Tim Kuipers c79c503621 fix: setting loading always segfaulted (CURA-1574) 2016-05-26 21:01:49 +02:00
Tim Kuipers e6fb617f3f refactor: simplified SettingRegistry::handleSetting (CURA-1574) 2016-05-26 08:46:37 +02:00
Tim Kuipers ca1799efc6 remove unneeded function (CURA-1574) 2016-05-26 08:46:13 +02:00
Tim Kuipers 749e85b15b doc: setting registry stuff (CURA-1574) 2016-05-26 08:45:50 +02:00
Tim Kuipers 82c7bfaf7a fix: don't retrieve settings from registry (CURA-1574)
The settings are set during the loading of the json file, not during retrieval of settings
2016-05-25 17:08:54 +02:00
Tim Kuipers d5df34b3cf fix: don't set extruder train defaults anymore via some special casing for extruder trains (CURA-1574) 2016-05-25 17:07:35 +02:00
Tim Kuipers 59774e7f14 lil: less warnings when handling json (CURA-1574)
warnings were given twice and were given for overriding frontend settings
2016-05-25 17:05:36 +02:00
Tim Kuipers bf8e73a4ae feat: load json setting default directly into a given settings base (CURA-1574)
Instead of keeping the defaults in the global settings registry, load the defaults immediately into a settings base, so that we can override the extruder train defaults.
2016-05-25 16:58:41 +02:00
Tim Kuipers 8b778b82be fix: support_extruder_nr used instead of support_infill_extruder_nr 2016-05-25 15:28:25 +02:00
Tim Kuipers d42b0ac9eb fix: SettingsToGv for new setting json files (CURA 1574) 2016-05-25 15:27:11 +02:00
Tim Kuipers cda656d43d lil: made etDefinitionFile static (CURA-1574) 2016-05-25 15:02:55 +02:00
Tim Kuipers 8c18b2ca89 fix doc: help and README show new way of handling settings and the CURA_ENGINE_SEARCH_PATH env var (CURA-1574) 2016-05-25 15:01:56 +02:00
Tim Kuipers eeb69776de feat: load json settings recursively and check in paths provided in CURA_ENGINE_SEARCH_PATH (CURA-1574) 2016-05-25 14:46:08 +02:00
Tim Kuipers cee01abe16 refactor: pass down warn_duplicates via setting calls (CURA-1574)
Extruder train definition .def.json files shouldn't warn for duplicates; only the base file should.
2016-05-25 12:30:49 +02:00
Tim Kuipers 8df1562d7b cleanup imports (CURA-1574) 2016-05-25 12:23:18 +02:00
Tim Kuipers 3bbc4a1d72 refactor: split settingRegistry into separate class and header files (CURA-1574) 2016-05-25 12:19:23 +02:00
Tim Kuipers 5a9eaa29ea refactor: move settings related files into separate folder (CURA-1574) 2016-05-25 12:07:59 +02:00
Tim Kuipers 6faeaf8c0b fix: accidental __cxx11:: in type (CURA-1574) 2016-05-20 14:04:46 +02:00
Tim Kuipers f9b15a2f47 refactor: settingsResistry.settings ==> setting_key_to_config (CURA-1574) 2016-05-20 12:58:02 +02:00
Tim Kuipers c1eb1fdd85 fix: forgotten newline in SettingContainer debug out (CURA-1574) 2016-05-20 12:57:27 +02:00
Tim Kuipers 324f424e69 refactor: simplified machine name loading (CURA-1574) 2016-05-20 12:51:04 +02:00
Tim Kuipers 3dfb35d73e fix: a settings container is now used only for the global settings base and for the separate extruder train settings default bases (CURA-1574) 2016-05-20 12:50:38 +02:00
Tim Kuipers e72789e3cb fix: read new setting .def.json files (CURA-1574)
Simplified the recursion of reading settings
Made the settings registry into a flat list, rather than a tree
Made each setting record a path instead (to keep the same information as the tree)
Moved extruder trains into a separate object, rather than it being a normal category
warn_duplicates strategy simplified: always after the first file
(rather than when applying overrides)
temporarily removed reading of machine_extruder_trains
made label attribute obligatory
Always add a setting to the global setting config (cause reading machine_extruder_trains is currently not implemented)
2016-05-20 12:42:14 +02:00
Tim Kuipers d6ac9e69c1 lil 2016-05-19 17:08:02 +02:00
Tim Kuipers 358d2e26c2 lil 2016-05-19 17:07:04 +02:00
Tim Kuipers e9fb973a05 fix: machine_settings became just another category, so doesnt need special handling (CURA-1574) 2016-05-19 14:29:55 +02:00
Tim Kuipers 22b86f81b7 fix: don't load inheriting json document (CURA-1574)
The inherit property now refers to an id instead of a filename, so I cannot simply know which file I have to load from a given json file
2016-05-19 14:28:59 +02:00
Tim Kuipers cf05c65061 lil: don't wait after priming second nozzle 2016-05-18 17:25:45 +02:00
Tim Kuipers 5f8f0110cf fix: switch infill direction every X layers (CURA-1569 CURA-943)
infill combine always prints infill of any thickness on infill of the first fully combined layer below
2016-05-18 16:35:10 +02:00
Tim Kuipers d3715e8e76 refactor: moved getSettingBoolean(cool_lift_head) inside gcodePlanner::writeGCode(.) (CURA-1568) 2016-05-18 13:36:05 +02:00
Tim Kuipers 31e2996104 refactor: removed superfluous layerThickness parameter to gcodePlanner::writeGCode(.) (CURA-1568) 2016-05-18 13:32:13 +02:00
Tim Kuipers 3ff329033a lil doc 2016-05-18 10:24:42 +02:00
Tim Kuipers f7d72623d2 fix: better naming for infill/skin overlap for support (CURA-967) 2016-05-13 13:22:34 +02:00
Tim Kuipers fee8867855 feat: skin_overlap separated from infill_overlap (CURA-967) 2016-05-13 13:22:04 +02:00
Tim Kuipers 62a4db8632 fix: initial layer thickness stays the same even when having a raft 2016-05-12 23:42:06 +02:00
Tim Kuipers efb20afc76 fix: only process if there's any non-infill meshes (CURA-833) 2016-05-12 20:49:46 +02:00
Tim Kuipers 683c887e53 fix: made generateMultipleVolumesOverlap ignore infill meshes and made overlap changable per object (CURA-883) 2016-05-12 20:11:00 +02:00
Tim Kuipers 4d9daccb5b fix: don't generateMultipleVolumesOverlap for infill meshes (CURA-833) 2016-05-12 20:06:25 +02:00
Tim Kuipers c0611904bb refactor: compute generateMultipleVolumesOverlap differently (CURA-833)
instead of for each layer computing overlap for all volumes, go through all volumes computing overlap for each layer
2016-05-12 20:04:03 +02:00
Tim Kuipers cecac0fe90 lil: code conventions (CURA-833) 2016-05-12 19:56:00 +02:00
Tim Kuipers f48c858ec4 fix: syntax fix of last merge 2016-05-12 19:55:34 +02:00
Tim Kuipers fc4d24fb01 optimization: carveMultipleVolumes uses aabb of meshes and discards infill meshes (CURA-1551 CURA-833) 2016-05-12 19:49:42 +02:00
Tim Kuipers 529301f950 Merge branch '2.1' 2016-05-12 19:25:10 +02:00
Tim Kuipers 9c47644e55 cleanup: removed fill_perimeter_gaps setting (CURA-996) 2016-05-12 16:30:11 +02:00
Tim Kuipers 2949f89b29 cleanup: removed perimeterGaps and functionality of fill_perimeter_gaps (CURA-996) 2016-05-12 16:28:39 +02:00
Tim Kuipers 1793961094 cleanup: removed avoidOverlappingPerimeters as parameters to functions and as member variables (CURA-996) 2016-05-12 16:22:34 +02:00
Tim Kuipers 590795921e cleanup: removed avoidOverlappingPerimeters and in_between from Infill factory (CURA-996) 2016-05-12 16:17:43 +02:00
Tim Kuipers b79c404dc3 remove: removed offsetSafe functions used for 'remove overlapping wall parts' (CURA-996) 2016-05-12 16:05:37 +02:00
Tim Kuipers 0065532d6d lil doc (CURA-996) 2016-05-12 14:00:09 +02:00
Tim Kuipers b89c8fd1fa fix: infill meshes didn't update bounding boxes (CURA-833) 2016-05-11 23:25:56 +02:00
Tim Kuipers d409c4d245 feat: infill_mesh_order to determine the order of infill meshes in eachother (CURA-833) 2016-05-11 23:25:28 +02:00
Tim Kuipers fd64b5ce60 fix: infill meshes updated infill_area, but not infill_are_per_combine (CURA-833) 2016-05-11 22:35:06 +02:00
Tim Kuipers 5bc3b86dc4 fix: infill meshes relied on old infill_area implementation (CURA-833) 2016-05-11 21:47:41 +02:00
Tim Kuipers 9ec92fa33f Merge branch 'feature_infill_mesh_correct_progress' 2016-05-11 21:43:58 +02:00
Tim Kuipers 03b654af3e feat: layer_0_z_overlap (CURA-1549) 2016-05-11 17:15:28 +02:00
Tim Kuipers e3c03e6c04 feat: switch_extruder_retraction_hop (CURA-1061) 2016-05-11 16:41:08 +02:00
Tim Kuipers 33c40f3398 Merge branch '2.1' 2016-05-11 13:27:16 +02:00
Tim Kuipers c79a7f1819 fix: spiralize would leave the z at the next layer for the next polygon on the same layer (CURA-1541) 2016-05-11 12:57:47 +02:00
Tim Kuipers 7843a68d7c lil: doc 2016-05-11 12:13:18 +02:00
Tim Kuipers 8f912835cf lil: doc indent 2016-05-11 12:11:37 +02:00
Tim Kuipers 36de33b735 fix: some small fixes of second last refactor (CURA-1367) 2016-05-11 12:07:24 +02:00
Tim Kuipers 01884663c3 refactor: rename files insets ==> WallsComputation (CURA-1367) 2016-05-11 12:04:11 +02:00
Tim Kuipers 4c46dd37a7 refactor: put generateInsets functions into a class (CURA-1367) 2016-05-11 12:01:16 +02:00
Tim Kuipers 9245a4fa41 fix: only compute print_outline when the mesh uses support (CURA-1367) 2016-05-11 11:50:31 +02:00
Tim Kuipers 4b240e8057 fix: let getLayerOutlines use an outline approximation based on the outer wall (CURA-1367)
Thin walls may be unprintable, so getLayerOutlines returns pieces of polygons which aren't actually printed.
This caused an edge overhang to remove support due to the XY distance of unprinted parts of the edge overhang.
When two parts are connected via an unprintably thin wall, also combing would be affected, but not anymore.
2016-05-11 11:36:38 +02:00
Tim Kuipers fff8195d51 Merge branch '2.1' 2016-05-11 08:52:55 +02:00
Tim Kuipers e01f18c7d4 lil: doc 2016-05-11 08:52:06 +02:00
Tim Kuipers 42891874f4 fix: multiple polygons & spiralize resulted in extrusions where travels should have been (CURA-1513) 2016-05-10 17:37:16 +02:00
Tim Kuipers 257d6a6635 fix: spiralize each polygon of a spiralized mesh even though that might cause a print head crash (CURA-1443) 2016-05-10 15:58:34 +02:00
Tim Kuipers bec8bef455 fix: consecutive spiralize paths are handled as one (CURA-1443 CURA-1541) 2016-05-10 15:32:09 +02:00
Tim Kuipers 4efeaa7083 fix: let bool spiralize bubble down from FffGcodeWriter into GCodePath instead of going via the GCodePathConfig (CURA-1443)
Because of the LayerPlanBuffer the GCodePathConfig changed a couple of layers too early, resulting in spiralization in the first layers which include the bottom skin.
2016-05-10 15:14:50 +02:00
Tim Kuipers 95fd9d6685 lil: doc 2016-05-10 13:25:11 +02:00
Tim Kuipers 6d54a31bcd Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-05-06 15:31:04 +02:00
Tim Kuipers 18df36ca06 refactor: moved AABB3D out of AABB (CURA-1436) 2016-05-06 15:30:22 +02:00
Tim Kuipers 55f47523b2 refactor: moved AABB implementation out of header file (CURA-1436) 2016-05-06 15:24:46 +02:00
Tim Kuipers 4130af0ad6 feat: boundingbox.expand (CURA-1436) 2016-05-06 15:06:19 +02:00
Tim Kuipers c08a0221c8 fix: combing basic comb path had wrong offsets to beginning and end of poly (CURA-1436) 2016-05-06 15:05:19 +02:00
Tim Kuipers 31f8459a0f Merge pull request #335 from sean041/patch-2
Compare in PolygonRef::shorterThan is strange.
2016-05-05 11:11:51 +02:00
Peng Liu 4642076fdc Compare in PolygonRef::shorterThan is strange.
PolygonRef::shorterThan will always return false?
2016-05-05 15:27:46 +08:00
Tim Kuipers 13b3c715bf feat: compensate overlapping inner wlal parts (CURA-995) 2016-05-04 15:09:33 +02:00
Tim Kuipers 3583d71dcd lil: indent (CURA-1479) 2016-05-04 11:40:38 +02:00
Tim Kuipers c5ab004ece feat: option to apply different xy distance at overhang places (CURA-1479) 2016-05-04 11:40:15 +02:00
Tim Kuipers b8fe70ee74 feat: SupportDistPriority setting enum (CURA-1479) 2016-05-04 11:38:30 +02:00
Tim Kuipers 6db88290c2 lil debug 2016-05-03 15:57:55 +02:00
Tim Kuipers be6fd8cc7c fix: skirt was processed sometimes twice for the first extruder sometimes not at all for the second extruder (CURA-1446) 2016-05-03 15:57:40 +02:00
Tim Kuipers baeb736705 fix: if all layers got removed slicing didn't quit (CURA-1465) 2016-05-03 14:15:57 +02:00
Tim Kuipers 05fa05bb5a Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-05-03 13:51:33 +02:00
Tim Kuipers 5a7c2e5ef1 fix codestyle (tabs) 2016-05-03 13:50:33 +02:00
Tim Kuipers c9de58ceba fix: material used was logged on the wrong extruder (CURA-520) 2016-05-03 13:15:53 +02:00
Ghostkeeper 1d60079220 Merge branch 'feature_slicer_refactor_rebased' 2016-05-03 11:06:09 +02:00
Tim Kuipers 77d40fb0e9 fix: griffin header micron ==> mm (CURA-520) 2016-05-03 09:34:03 +02:00
Tim Kuipers 72e9906bb8 Merge branch '2.1' 2016-05-02 18:29:30 +02:00
Tim Kuipers 2db37c6018 fix: erase with impossible iterator when trying to simplify an empty polygon (CURA-1430) 2016-05-02 18:28:00 +02:00
Tim Kuipers 41b0966d26 fix: constructor prime tower called incorrectly 2016-05-02 17:33:00 +02:00
Ghostkeeper d5bff03a1c Codestyle: Space around binary operators
Contributes to issue CURA-520.
2016-04-29 16:18:59 +02:00
Tim Kuipers 0d5cc686c9 fix: exclude enum values from setting reflection 2016-04-29 14:32:07 +02:00
Tim Kuipers 3103acb7b0 fix: curaEngine analyse 2016-04-29 14:28:04 +02:00
Tim Kuipers bcce1bd8a4 feat:setting inheritance diagram 2016-04-29 14:27:50 +02:00
Tim Kuipers cda16c4429 Merge branch 'reflection_setting_inheritance_visualization' 2016-04-29 14:19:54 +02:00
Tim Kuipers 21a8afb895 fix: extruder train initialization M227 ==> G280 (CURA-520) 2016-04-28 17:05:32 +02:00
Tim Kuipers 0d7074ee8b doc: documented stuff in gcodePlanner.h (CURA-537) 2016-04-28 17:01:48 +02:00
Tim Kuipers 8bb91cecb9 fix: dont switch extruder if there is no priontable support (CURA-1437) 2016-04-28 15:49:30 +02:00
Tim Kuipers 7aaf3b9bae fix: dont switch extruder if there is no priontable model (CURA-1437) 2016-04-28 15:48:57 +02:00
Tim Kuipers a806a27836 fix: sometimes the first layer could be totally empty / no walls (CURA-1465) 2016-04-26 18:28:05 +02:00
Tim Kuipers d556999aed fix: jedi header: save json machine name as if it is a setting and retrieve it for the header (CURA-520) 2016-04-25 17:57:45 +02:00
Tim Kuipers 9da99b67fc fix: jedi header: PRINT.SIZE obtained from machine dimensions (CURA-520) 2016-04-25 17:15:55 +02:00
Tim Kuipers 4b02912ab6 fix: jedi header: BED ==> BUILD_PLATE (CURA-520) 2016-04-25 17:00:25 +02:00
Tim Kuipers 58234e4125 doc: Date documentation (CURA-520) 2016-04-25 16:45:21 +02:00
Tim Kuipers e825637f14 feat: jedi header includes date stamp (CURA-520) 2016-04-25 16:41:31 +02:00
Tim Kuipers 242384bd28 Merge branch 'feature_jedi_cura_starting_state' 2016-04-25 13:21:46 +02:00
Tim Kuipers b0cb94aeca Merge branch 'feature_slicer_refactor_rebased' of https://github.com/Ultimaker/CuraEngine into feature_slicer_refactor_rebased 2016-04-22 17:21:58 +02:00
Tim Kuipers 95fc4695d2 fix: polylines got copied, const correctness and documentation in slicer.cpp (CURA-738) 2016-04-22 17:19:51 +02:00
Tim Kuipers 1f6f847b51 lil: inlined one line of code 2016-04-22 16:19:03 +02:00
Tim Kuipers 3e1b5128bb lil: made polygon.shorterThan const (CURA-738) 2016-04-22 16:17:43 +02:00
Tim Kuipers b88ee700fc refactor: moved implementation to cpp file for polygon.shorterThan (CURA-738) 2016-04-22 16:16:15 +02:00
Tim Kuipers b43b98da25 bugfix: polygon.shorterThan didn't take last line segment into account (CURA-738) 2016-04-22 16:07:43 +02:00
Ghostkeeper ecdb4f7879 Codestyle: Space after control statement
Contributes to issue CURA-738.
2016-04-22 15:24:21 +02:00
Ghostkeeper 20ef9ce1c0 Codestyle: Space after control statement, always use brackets
Contributes to issue CURA-738.
2016-04-22 14:33:32 +02:00
Ghostkeeper aec58f7e00 Codestyle: Space around operators and after control statement
Contributes to issue CURA-738.
2016-04-22 14:06:01 +02:00
Tim Kuipers 7dca18fe6a removed debugging code 2016-04-21 14:35:39 +02:00
Tim Kuipers dc761c2f57 lil: removed commented code 2016-04-21 13:47:18 +02:00
Tim Kuipers 059c97b2cd refactor: moved implementation in Meshgroup.h to cpp file (CURA-520) 2016-04-21 13:46:01 +02:00
Tim Kuipers d14e05f318 refactor: jedi ==> griffin (CURA-520) 2016-04-21 13:31:39 +02:00
Tim Kuipers a533559918 fix: combing always went to closest point to origin, instead of crossing_1_in_or_mid (CURA-579) 2016-04-21 10:42:47 +02:00
Tim Kuipers 78ca299380 quickfix: set temperature during priming (CURA-520) 2016-04-20 17:13:26 +02:00
Tim Kuipers 1d581c0fec fix: only reset E-value after toolswitch (CURA-520)
Given that having an G92 E0 at the start of the first use of a toolhead is no problem in any printer, we can just reset the E-value every start of a toolhead
2016-04-20 14:53:06 +02:00
Tim Kuipers d0c58acfcf rebase fixes 2016-04-20 13:13:57 +02:00
Tim Kuipers 7b90354033 refactor: renaming local vars and rewrite of Remove all the tiny polygons 2016-04-20 13:10:30 +02:00
Tim Kuipers 5c4fdfdd0b refactor: renamed clipper polygon to path 2016-04-20 13:10:30 +02:00
Tim Kuipers c8051f5b37 refactor: renaming local vars 2016-04-20 13:10:30 +02:00
Tim Kuipers fa203bd976 refactor: some functions moved and extensive stitching moved to its own function 2016-04-20 13:10:30 +02:00
Tim Kuipers 4d2e544be0 refactor: renamed clipper polygon to path 2016-04-20 13:10:30 +02:00
Tim Kuipers bd27011107 bugfix: lil) 2016-04-20 13:08:04 +02:00
Tim Kuipers fb3c99ebe0 changed stitching poly decision 2016-04-20 13:08:03 +02:00
Tim Kuipers 2a8a86aac4 refactor: moved first stitching out of slicer 2016-04-20 13:08:03 +02:00
Tim Kuipers 4fa497ee8c bugfix: slicer didn't connect anything 2016-04-20 13:08:03 +02:00
Tim Kuipers 76eaeeb196 refactored first part of slicer: connect segments 2016-04-20 13:08:03 +02:00
Tim Kuipers 9cebeb770a Merge branch 'feature_combing_refactor' 2016-04-20 10:35:35 +02:00
Tim Kuipers a0200f1548 Merge branch 'feature_combing_refactor' 2016-04-20 10:32:46 +02:00
Tim Kuipers 83164fe1e7 fix: don't recalculate line distance in the engine (CURA-1317) 2016-04-19 17:30:43 +02:00
Tim Kuipers f9f162383b removed old test stls and gcode files 2016-04-19 16:41:59 +02:00
Tim Kuipers 403f7515b8 fix: forgot ProgressStageEstimator.h (CURA-873) 2016-04-18 17:38:38 +02:00
Tim Kuipers 969ed87600 refactor: moved ProgressStageEstimator implementation to cpp file (CURA-873) 2016-04-18 17:30:22 +02:00
Tim Kuipers a2dccb118c Merge branch 'feature_progress_refactor_folder_refactor' 2016-04-18 17:23:38 +02:00
Tim Kuipers 64e5c5b8bf Merge branch 'feature_progress_refactor' 2016-04-18 17:22:46 +02:00
Tim Kuipers 8bb2a6ba7f refactor: moved progress files into separate folder (CURA-873) 2016-04-18 17:21:53 +02:00
Tim Kuipers da38e958ac Merge branch 'feature_rework_fffPolygonProcessor_per_mesh' 2016-04-18 17:02:49 +02:00
Tim Kuipers 081a46118c refactor: merged writeRetraction and writeRetraction_extruderSwitch (CURA-1431) 2016-04-18 15:45:00 +02:00
Tim Kuipers bd006e676b feat: zHop during extruder switch (CURA-1431) 2016-04-18 15:28:26 +02:00
Tim Kuipers afc40e1c70 refactor: moved extruder switch member values into normal RetractionConfig (CURA-1431) 2016-04-18 15:27:26 +02:00
Tim Kuipers cc6583b214 fix: writeRetraction did early exit sometimes when going from normal retracted to extruder switch retracted (CURA-1431) 2016-04-18 15:25:26 +02:00
Tim Kuipers 55fbd2ba54 refactor: extruder_attr[current_extruder] ==> extr_attr (CURA-1431) 2016-04-18 14:55:02 +02:00
Tim Kuipers 916d26417f fix: unretraction speeds were wrong when going from exdtruder switch retraction state to normal retraction state (CURA-1431) 2016-04-18 14:54:11 +02:00
Tim Kuipers 258a7e6f37 fix: let LayerplanBuffer decide the starting temperature (CURA-520)
That way flow dependent temperature decides on the starting temperature when using the command sucket
2016-04-18 13:39:43 +02:00
Tim Kuipers d5f13616c2 refactor: moved meshgroup_number to FffProcessor (CURA-520) 2016-04-18 11:44:57 +02:00
Tim Kuipers 54c7f942c4 fix: no more temp commands at start for Jedi gcode (CURA-520) 2016-04-18 11:07:57 +02:00
Tim Kuipers bd565ab000 fix: don't zero extruder value after toolchange (CURA-959) 2016-04-14 18:09:29 +02:00
Tim Kuipers fc24ce974d feat: jedi prime gcode commands (CURA-520)
priming is now perfored by moving to a location, perform the prime gcode, retract, switch extruder, same for other extruder
2016-04-14 15:50:23 +02:00
Tim Kuipers 7a7c824b0d refactor: moved preSetup implementation to cpp file (CURA-520)| 2016-04-14 14:24:14 +02:00
Tim Kuipers a4227db5b1 fix: also set initial temps when doing wireprinting (CURA-520) 2016-04-14 14:21:58 +02:00
Tim Kuipers 7fdc77c74c Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-04-14 14:10:09 +02:00
Tim Kuipers 508b1b2933 machine extruder trains now can't be an array anymore (CURA-494) 2016-04-14 14:09:58 +02:00
Tim Kuipers 387ef30ca1 machine extruder trains now can't be an array anymore (CURA-494) 2016-04-14 13:21:00 +02:00
Tim Kuipers 1b6df75591 fix: jedi header (CURA-520) 2016-04-14 11:26:02 +02:00
Tim Kuipers 53ccadbf91 feat: const preserving getExtruderTrain function (CURA-520) 2016-04-14 11:23:05 +02:00
Tim Kuipers 612f6cac3d fix: extruder trains didn't get properly initialized when one-at-a-time printing with command line slicing 2016-04-14 11:16:45 +02:00
Tim Kuipers 166601492b fix: always prepend bogus header to fool firmware into accepting the print (CURA-520)
this was done so that Jedi gcode also always includes a header
it also means that UMO gcode always gets a header, which is ok, as it is ignored by that firmware
2016-04-13 16:13:43 +02:00
Ghostkeeper e0de929c5b Codestyle: Spaces around operators
Contributes to issue CURA-494.
2016-04-13 14:54:07 +02:00
Tim Kuipers 7ac4738435 feat: JEDI gcode flavor (CURA-520) 2016-04-13 13:07:11 +02:00
Tim Kuipers 4c547b9a66 fix const problem in BucketGrid2D (CURA-893) 2016-04-12 17:09:06 +02:00
Tim Kuipers 7ca184fb78 fix merge conflicts (CURA-893) 2016-04-12 16:53:00 +02:00
Tim Kuipers 080663a653 moved polygonUtilsTest (CURA-893) 2016-04-12 16:51:55 +02:00
Tim Kuipers cc54c8be08 Merge branch 'feature_skin_and_dual_combing' into feature_skin_and_dual_combing_merge 2016-04-12 16:49:38 +02:00
Tim Kuipers c19f35ce13 fix: writeRetraction didn't unretract if already retracted more (CURA-959) 2016-04-12 15:25:06 +02:00
Tim Kuipers 983720cfc0 fix: G92 E0 wasn't performed after the nozzle switch (CURA-959) 2016-04-12 15:24:16 +02:00
Tim Kuipers cad745f0b5 fix: infill_overlap ==> infill_overlap_mm (CURA-786) 2016-04-11 19:06:55 +02:00
Tim Kuipers 2a10954df2 lil fix: __cxx11::string ==> string 2016-04-09 01:55:11 +02:00
Tim Kuipers 7eded0ba3c Merge branch 'bugfix_support_z_xy_fight' 2016-04-08 14:39:13 +02:00
Tim Kuipers ca963d5da0 Merge branch 'bugfix_getSetting_calls' 2016-04-08 14:12:51 +02:00
Tim Kuipers 7091650876 Merge branch '2.1' 2016-04-08 13:36:26 +02:00
Tim Kuipers 93485cd0df Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-04-06 17:09:16 +02:00
Tim Kuipers 2d3382874a fix: polygon smoothing removed vertices which were connected to a small as well as a large line segment (CURA-1361) 2016-04-06 17:09:05 +02:00
Tim Kuipers 1e78397e18 fix: support max layer was set after each object (CURA-1360) 2016-04-06 17:05:33 +02:00
Ghostkeeper 6bcdd94f7e Copy protocol file from front-end
This synchronises the whitespace and comments from the front-end. The typeids are not necessary any more.

Contributes to issue CURA-1210.
2016-04-05 17:14:09 +02:00
Ghostkeeper accd28db64 Update documentation of sendLayerData
Contributes to issue CURA-1210.
2016-04-05 17:13:57 +02:00
Ghostkeeper 1c0f4c42d9 Remove superfluous set to nullptr
Shared pointers are already set to nullptr by default.

Contributes to issue CURA-1210.
2016-04-05 17:13:47 +02:00
Ghostkeeper 5da1632d9f Remove sliced object lists from protocol
Each layer is now sent individually, instead of grouped by object and grouping those objects in a sliced object list. The list was sometimes too large to send in one message. The objects weren't used by the front-end anyway.

Contributes to issue CURA-1210.
2016-04-05 17:13:37 +02:00
Ghostkeeper cbf1152f56 Remove object ID from layer
Turns out that it is not needed in the front-end either any more. If we need it in the future, we'll add it again.

Contributes to issue CURA-1210.
2016-04-05 17:13:22 +02:00
Ghostkeeper 2273c5aefe Alter protocol to no longer group sliced objects
They will be sent with one message per layer from now on.

Contributes to issue CURA-1210.
2016-04-05 17:13:09 +02:00
Tim Kuipers 991adf19a1 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-04-05 11:49:07 +02:00
Tim Kuipers 9a4e1b52ed fix: always zero E value before AND after nozzle switch (CURA-959) 2016-04-05 10:53:25 +02:00
Ghostkeeper 98d2786dd0 Merge branch '2.1'
Conflicts:
	src/gcodeExport.h
	src/gcodePlanner.cpp
	src/utils/LinearAlg2D.cpp
2016-04-04 19:03:33 +02:00
Tim Kuipers 46c793e73d fix: lil string vs c_str bug (CURA-1231) 2016-04-04 14:15:14 +02:00
Tim Kuipers fd4969887b refactor: replaced all gcode output \n by new_line (CURA-1231) 2016-04-04 14:13:32 +02:00
Tim Kuipers 6620a050a5 feat: new_line string used for BFB machines (CURA-1231) 2016-04-04 14:13:10 +02:00
Tim Kuipers 6325197fce feat/refactor: moved file header generation to gcodeExport and introduced the NOZZLE_SIZE header comment (CURA-1231) 2016-04-04 14:03:38 +02:00
Tim Kuipers f828d44365 Merge branch 'feature_better_time_estimates' into 2.1 2016-04-04 12:14:19 +02:00
Tim Kuipers 5072995a66 documentation (CURA-537) 2016-04-04 12:06:28 +02:00
Tim Kuipers a8ab0c12aa code style: space after keyword 2016-03-30 17:44:36 +02:00
Tim Kuipers 09c989a019 code style: spaces around operators 2016-03-30 17:17:12 +02:00
Tim Kuipers 29564a23e0 refactor: introduced mm, mm3, E value conversion functions instead of inline is_volumetric checks (CURA-1293) 2016-03-30 12:35:11 +02:00
Tim Kuipers 1fdda3319f fix: volumetric time estimation was bugged (CURA-1293) 2016-03-30 12:24:27 +02:00
sean041 be113eceb4 Fix typo. downSkinCount -> upSkinCount (CURA-1299)
This typo causes flaky crash when downSkinCount < upSkinCount and ignore small z gaps is disabled.
2016-03-30 10:24:58 +02:00
Tim Kuipers 0b3b8ea33b Merge pull request #323 from sean041/patch-1
Fix typo. downSkinCount -> upSkinCount (CURA-1299)

this caused a crash when Ignore small z gaps was disabled and bottom was smaller than top thickness
2016-03-30 10:22:29 +02:00
Tim Kuipers 0c42ff9bfa fix: inner (2nd) wall was refered to even if it wasn't generated (CURA-1294) 2016-03-29 15:18:47 +02:00
sean041 a5bd599ec7 Fix typo. downSkinCount -> upSkinCount
This typo causes flaky crash when downSkinCount < upSkinCount.
2016-03-26 08:07:36 +08:00
Ghostkeeper 7548c41d7b Simplify test case
This test case now reduces to the normal test cases since there is only one option again. We can re-use the function that was made for normal test cases then.

Contributes to issue CURA-579.
2016-03-25 16:41:08 +01:00
Tim Kuipers cd033ef6ab doc: autobrief for first line (CURA-537) 2016-03-24 17:50:12 +01:00
Tim Kuipers e909af9abd documentation for fffProcessor (CURA-537) 2016-03-24 17:34:02 +01:00
Tim Kuipers dd5fbf14e4 document FffGcodeWriter (CURA-537) 2016-03-24 16:59:09 +01:00
Tim Kuipers 168e041c42 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-03-24 15:58:16 +01:00
Ghostkeeper 670ae6dd8c Spaces around minus operator
Conforming to code style.

Contributes to issue CURA-863.
2016-03-24 15:58:11 +01:00
Tim Kuipers 20adfa751f doc+refactor: fan speed calc more clear (CURA-863) 2016-03-24 15:57:54 +01:00
Tim Kuipers bf8776b112 optimization: removed superluous recalculation of line direction in LineOrderOptimizer (CURA-1170) 2016-03-23 16:25:10 +01:00
Tim Kuipers 1d0f3f519a fix syntax mistake (CURA-1170) 2016-03-23 16:25:01 +01:00
Tim Kuipers 07fef8668c calculate incoming_perpundicular_normal in end stage of line order optimizer (CURA-1170) 2016-03-23 16:24:51 +01:00
Tim Kuipers 1c06fc49fc refactor: factor out getAngleScore from line order optimizer (CURA-1170) 2016-03-23 16:24:39 +01:00
Tim Kuipers beb9422d9b refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-23 16:24:27 +01:00
Tim Kuipers a8359b9a68 refactor: small optimization of line order optimizer dot score (CURA-1170) 2016-03-23 16:24:17 +01:00
Tim Kuipers 5ccfe2d1aa refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-23 16:24:07 +01:00
Tim Kuipers 74577759b4 refactor: expand complicated code in pathOrderOptimizer (CURA-1170) 2016-03-23 16:23:54 +01:00
Tim Kuipers 45eb026777 refactor: rewrite line order optimizer dot score stuff (CURA-1170) 2016-03-23 16:23:42 +01:00
Tim Kuipers aabb07fd81 refactor: simple renaming of incoming_perpendicular_normal (CURA-1170)
Conflicts:
	src/pathOrderOptimizer.cpp
2016-03-23 16:23:21 +01:00
Ghostkeeper 6377ec63e1 Add edge-case tests for getAngleLeft
These test what happens when two or more points are equal. There is nothing about this in the function specification, so it allows any output, but at least it shouldn't give like a divide by zero error.

Contributes to issue CURA-1170.
2016-03-23 16:14:11 +01:00
Tim Kuipers eab2d8e667 fix: improved dot-score for preferring the z-seam on inside corners (CURA-1170) 2016-03-23 16:13:58 +01:00
Tim Kuipers 8d41003c67 feat: linearAlg2D::getAngleLeft CMAKE (CURA-1170) 2016-03-23 16:13:48 +01:00
Tim Kuipers ecfae4d75c feat: linearAlg2D::getAngleLeft (CURA-1170) 2016-03-23 16:13:37 +01:00
Tim Kuipers a2208f6b69 fix: pathOrderOptimizer was bugged (CURA-1170)
polyStart indices used wrongly
2016-03-23 16:13:26 +01:00
Tim Kuipers bacacb01dc refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-23 16:13:15 +01:00
Tim Kuipers 94c9399f2c refactor: intpoint::crossZ ==> turn90CCW (CURA-1170) 2016-03-23 16:13:03 +01:00
Tim Kuipers 168dc3c12b refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-23 16:12:53 +01:00
Tim Kuipers 235af65b00 refactor: code cleanup helper functions of PathOrderOptimizer (CURA-1170) 2016-03-23 16:12:43 +01:00
Tim Kuipers f3f3be74cc refactor: code cleanup of PathOrderOptimizer - lines (CURA-1170) 2016-03-23 16:12:32 +01:00
Tim Kuipers dca0bc80b5 fix: PathOrderOptimizar::polyStart had wrong indexing (CURA-1170) 2016-03-23 16:12:18 +01:00
Tim Kuipers c0e57622d0 cleanup: more clarification of pathOrderOptimizer for polygons code (CURA-1170) 2016-03-23 16:12:04 +01:00
Tim Kuipers b7a8fbe798 cleanup: pathOrderOptimizer got cleaned up (CURA-1170) 2016-03-23 16:11:49 +01:00
Ghostkeeper 0985b97c54 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-03-23 16:08:58 +01:00
Tim Kuipers 06521eef8b lil 2016-03-23 12:32:44 +01:00
Tim Kuipers 47a6f0dc36 fix: made it possible to include warning and error functions in the graph; changed the way you should call the tool 2016-03-23 11:50:39 +01:00
Tim Kuipers 4353980e78 refactor: made code more explicit: after smoothing speed (towards bottom layer speed) the speeds are set to their iconic speeds once and for all (CURA-1248) 2016-03-22 17:20:38 +01:00
Tim Kuipers 18ae9cf41d bugfix: alternate extra wall had skin overlapping with inner wall (CURA-1233)
To see if there was infill above, we looked at the innermost wall instead of the wall with index [wall line count]
2016-03-22 13:42:32 +01:00
Tim Kuipers 04edf35331 bugfix: bottom layer speed influenced all layers (CURA-1248)
smoothSpeed never got called for the final speed
2016-03-22 13:21:03 +01:00
Tim Kuipers ce4d34adb2 unit test fix: moveInside(polys, points, dist) may leave inside points as they are (CURA-579) 2016-03-21 17:51:38 +01:00
Tim Kuipers d42be2a22c removed old unused code 2016-03-21 17:29:22 +01:00
Tim Kuipers dbcbcae2e3 codestyle: closestHere ==> closest_here (CURA-893) 2016-03-21 17:26:04 +01:00
Tim Kuipers ff9cb24d99 refactor: auto ==> ClipperLib::Path (CURA-893) 2016-03-21 17:23:19 +01:00
Tim Kuipers b8ff36651e fix: tests failed due to changed GcodePlanner constructor (CURA-893)
combing changed from boolean to an enum
boolean is_inside_mesh was added
2016-03-21 17:16:24 +01:00
Tim Kuipers 98a78e1844 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-03-21 12:30:52 +01:00
Tim Kuipers 37c8ad3061 lil (CURA-1217) 2016-03-21 12:28:56 +01:00
Tim Kuipers d2187fedbc optimization: removed superluous recalculation of line direction in LineOrderOptimizer (CURA-1170) 2016-03-21 12:17:20 +01:00
Tim Kuipers 5b0a50456f fix syntax mistake (CURA-1170) 2016-03-21 11:55:01 +01:00
Tim Kuipers 66f4b51a3e calculate incoming_perpundicular_normal in end stage of line order optimizer (CURA-1170) 2016-03-21 11:53:33 +01:00
Tim Kuipers 3ac6ee1b37 refactor: factor out getAngleScore from line order optimizer (CURA-1170) 2016-03-21 11:43:12 +01:00
Tim Kuipers 4d8b22a224 refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-21 11:36:08 +01:00
Tim Kuipers 3a0143ff4c refactor: small optimization of line order optimizer dot score (CURA-1170) 2016-03-21 11:33:26 +01:00
Tim Kuipers c6a4945469 refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-21 11:21:08 +01:00
Tim Kuipers abc6514b6d refactor: expand complicated code in pathOrderOptimizer (CURA-1170) 2016-03-21 11:05:01 +01:00
Tim Kuipers dc26358747 refactor: rewrite line order optimizer dot score stuff (CURA-1170) 2016-03-21 10:52:23 +01:00
Tim Kuipers 3485e5a4ad refactor: simple renaming of incoming_perpendicular_normal (CURA-1170) 2016-03-21 10:34:55 +01:00
Tim Kuipers bd47fd2c67 removed unused old commented code 2016-03-21 10:24:42 +01:00
Tim Kuipers d7d957d8f7 Removed unused code 2016-03-19 16:17:02 +01:00
Ghostkeeper bec8c235ea Add edge-case tests for getAngleLeft
These test what happens when two or more points are equal. There is nothing about this in the function specification, so it allows any output, but at least it shouldn't give like a divide by zero error.

Contributes to issue CURA-1170.
2016-03-18 14:12:51 +01:00
Ghostkeeper eb1bbd41b0 Codestyle: Spaces around binary operators
Contributes to issue 1170.
2016-03-18 13:07:28 +01:00
Tim Kuipers 49f2f21c08 feat: linearAlg2D::getAngleLeft (CURA-1170) 2016-03-17 17:40:53 +01:00
Tim Kuipers 9ed25b95ee fix: improved dot-score for preferring the z-seam on inside corners (CURA-1170) 2016-03-17 17:39:38 +01:00
Tim Kuipers c8b8abd4c6 feat: linearAlg2D::getAngleLeft CMAKE (CURA-1170) 2016-03-17 17:39:38 +01:00
Ghostkeeper 37c461fa86 Merge branch '2.1'
Conflicts:
	src/FffGcodeWriter.cpp
	src/inset.cpp
	src/pathOrderOptimizer.cpp
2016-03-17 15:43:03 +01:00
Tim Kuipers 7d9c8ee1b1 fix: pathOrderOptimizer was bugged (CURA-1170)
polyStart indices used wrongly
2016-03-17 09:45:28 +01:00
Tim Kuipers 5df73e0e30 refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-16 16:59:40 +01:00
Tim Kuipers 5d8926e3e7 refactor: intpoint::crossZ ==> turn90CCW (CURA-1170) 2016-03-16 15:43:49 +01:00
Tim Kuipers 277c478e3b refactor: more cleanup of pathOrderOptimizer (CURA-1170) 2016-03-16 15:38:59 +01:00
Tim Kuipers 6f654241f7 refactor: code cleanup helper functions of PathOrderOptimizer (CURA-1170) 2016-03-16 15:17:17 +01:00
Tim Kuipers 62910c9d0a refactor: code cleanup of PathOrderOptimizer - lines (CURA-1170) 2016-03-16 15:13:36 +01:00
Tim Kuipers f92ee17577 fix: PathOrderOptimizar::polyStart had wrong indexing (CURA-1170) 2016-03-16 15:02:15 +01:00
Tim Kuipers dc3d94e0a8 cleanup: more clarification of pathOrderOptimizer for polygons code (CURA-1170) 2016-03-16 15:01:25 +01:00
Tim Kuipers 6c9db68573 cleanup: pathOrderOptimizer got cleaned up (CURA-1170) 2016-03-16 14:53:54 +01:00
Tim Kuipers fc046d5978 fixed visualization stuff for only doing parent-child or inheritance 2016-03-16 13:48:22 +01:00
Tim Kuipers 2bd8657050 vizualize dependence of child on parent via inheritance function as normal parent-child relation 2016-03-16 13:34:21 +01:00
Tim Kuipers 6718a8b2f3 fix: made engine not depend on support_z_distance, which is a parent setting value (CURA-1171) 2016-03-16 09:36:05 +01:00
Tim Kuipers 08c69ca589 child-parent arrow color overrides inheritance 2016-03-16 09:29:35 +01:00
Tim Kuipers a3d17d217a DSg 2016-03-15 18:23:11 +01:00
Tim Kuipers e47fcea2bf sdg 2016-03-15 18:21:11 +01:00
Tim Kuipers ad3903037c how to use this branch 2016-03-15 18:15:34 +01:00
Ghostkeeper f7cdd63f1d Codestyle: Whitespace around operators
Space around binary operators. No space around unary operators.

Contributes to issue CURA-1098.
2016-03-15 12:18:54 +01:00
Tim Kuipers 3400439f5d Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-03-15 10:18:07 +01:00
Tim Kuipers 5fbf9a8907 made outer wall offset a setting instead of being calculated from the difference between nozzle size and wall_line_width_0 (CURA-1098) 2016-03-15 10:00:01 +01:00
Ghostkeeper df6d2fc592 Codestyle: Whitespace around binary operators
Contributes to issue CURA-1097.
2016-03-14 16:50:10 +01:00
Ghostkeeper 08a5ec7dee Codestyle: Whitespace around binary operators
Contributes to issue CURA-1097.
2016-03-14 16:45:56 +01:00
Ghostkeeper 3de01763c1 Codestyle: Whitespace around binary operators.
Contributes to issue CURA-1097.
2016-03-14 16:41:34 +01:00
Tim Kuipers 7fc18c2057 made combing depend on inner wall line width instead of nozzle_size (CURA-1097) 2016-03-14 13:21:05 +01:00
Tim Kuipers c7bf1e087a made rafts' combing not depend on nozzle_size (CURA-1097) 2016-03-14 13:16:04 +01:00
Tim Kuipers bc7ee74d19 made combing depend on inner wall line width instead of nozzle_size (CURA-1097) 2016-03-14 11:02:32 +01:00
Tim Kuipers fc20a6661b made rafts' combing not depend on nozzle_size (CURA-1097) 2016-03-14 10:37:46 +01:00
Ghostkeeper cffd6ac860 Spaces around minus operator
Conforming to code style.

Contributes to issue CURA-863.
2016-03-10 12:13:26 +01:00
Tim Kuipers 81d521a58b quickfix: remove outer wall offset based on nozzle size (CURA-1097) 2016-03-10 09:56:18 +01:00
Ghostkeeper 205c4f8cc9 Add test for new filter function
Contributes to issue CURA-590.
2016-03-09 14:38:23 +01:00
Ghostkeeper c797163536 Add more tests for findNearestObject
Contributes to issue CURA-590.
2016-03-09 14:28:14 +01:00
Ghostkeeper d31acdb244 Add first test for findNearestObject
Contributes to issue CURA-590.
2016-03-09 14:07:55 +01:00
Ghostkeeper 810f689418 Add tests for a group of points on a line
Contributes to issue CURA-590.
2016-03-09 13:02:41 +01:00
Ghostkeeper 7270290fe3 Improve findNearbyObjects test template
It now allows for some points for which the answer (near or far) is indeterminate.

Contributes to issue CURA-590.
2016-03-09 12:28:39 +01:00
Ghostkeeper 37e3114311 Add two simple tests for BucketGrid2D::findNearbyObjects
Contributes to issue CURA-590.
2016-03-09 12:16:16 +01:00
Ghostkeeper c3ef64fe18 Add initial test case for BucketGrid2D::findNearbyObjects
This is just an initial test and the testing framework for this unit.

Contributes to issue CURA-590.
2016-03-09 12:09:03 +01:00
Ghostkeeper 5d592553a6 Add test directory for infill
To make it complete for future tests that might appear there.
2016-03-09 11:00:03 +01:00
Ghostkeeper 38fad10453 Move utils tests to subdirectory
This keeps the same directory structure in the tests directory as in the source directory, as is common.
2016-03-09 10:50:10 +01:00
Tim Kuipers c4eb1d9f27 bugfix: uninitialised infill (CURA-1084)
not always did the basic infill_area get initialised, notably when there was no infill, which should be an empty polygon instead of no polygon
2016-03-08 17:52:12 +01:00
Ghostkeeper 1c16c77d56 Add test cases for moveInside
One of the edge cases currently fails.

Contributes to issue CURA-579.
2016-03-08 17:28:09 +01:00
Tim Kuipers 883f0c7419 current setting inheritance structure 2016-03-07 13:50:51 +01:00
Tim Kuipers bc11121a2e feat: ouput json inheritance structure 2016-03-07 13:50:51 +01:00
Tim Kuipers 0dbf80587b Merge branch '2.1' 2016-03-07 09:50:02 +01:00
Tim Kuipers 32d1bb6d75 bugfix: one-at-a-time printing went to z height of last position planned on the previous object (CURA-988) 2016-03-02 13:55:46 +01:00
Tim Kuipers afdb552f63 bugfix: retraction limitation segfaulted when retraction count == 1, cause it handled retraction count as if it was [retraction count - 1] (CURA-977) 2016-03-01 13:18:53 +01:00
Tim Kuipers a400ba28f2 fix: supportOnByuildplateOnly didn't account for conical support (CURA-914) 2016-03-01 12:06:54 +01:00
Tim Kuipers d18843abe3 fix: all settings now implicitly retrieved from meshgroup instead of explicitly (CURA-927) 2016-02-29 13:58:15 +01:00
Tim Kuipers ea12d310b0 fix: all settings retrieved from meshgroup instead of globally (CURA-927) 2016-02-29 13:54:36 +01:00
Tim Kuipers b993e4aff1 fix: support_roof_height was retrieved globally and per mesh - now only per mesh (CURA-956) 2016-02-29 13:39:35 +01:00
Tim Kuipers e0a7818d9e fix: infill wipe distance also on skin (CURA-964) 2016-02-29 13:03:42 +01:00
Tim Kuipers 277b5dce75 fix: get infill sparse combine per mesh (CURA-949) 2016-02-29 12:21:11 +01:00
Tim Kuipers 462a6e8c16 bugfix: skin didn't overlap with walls (CURA-941) 2016-02-29 12:16:02 +01:00
Tim Kuipers 20701117fb fix: support line width retrieved from same settings base everywhere (globally) (CURA-956) 2016-02-29 11:29:40 +01:00
Tim Kuipers a40d48c1be fix: initial layer speed for support requested from extruder train (CURA-956) 2016-02-29 11:18:20 +01:00
Tim Kuipers d7e966ad83 Merge branch 'analog10-master' 2016-02-29 10:40:58 +01:00
Tim Kuipers 593dd03987 Merge branch 'master' of https://github.com/analog10/CuraEngine into analog10-master 2016-02-29 10:37:19 +01:00
Tim Kuipers 07203c9d91 lil: removed unused const-incorrect function 2016-02-29 09:25:31 +01:00
David Bender 59e6a075e8 made a few intpoint methods const 2016-02-26 11:37:57 -05:00
David Bender 3f348ab1ba more const correctness for MeshGroup 2016-02-26 11:34:05 -05:00
David Bender 44fedbae7a make fpoint and fmatrix methods const 2016-02-26 11:33:29 -05:00
David Bender 77a378ba1c const cleanups in the Mesh class 2016-02-26 11:24:57 -05:00
David Bender 4f408847fb make members of settingRegistry.h const 2016-02-26 11:20:27 -05:00
Tim Kuipers 8d941063c1 fix: get raft settings from extruder train settingsbase; get extruder_nr settings with getSettingAsIndex instead of getSettingAsCount (CURA-956) 2016-02-25 20:04:56 +01:00
Tim Kuipers 471d20ff75 fix: print temp at startingCode based on extrudertrain settingsbase & lil bugfix '->' ==> '.' (CURA-956) 2016-02-25 19:55:59 +01:00
Tim Kuipers b82d6f9aff fix: retrieve machine_nozzle_size from extruder (CURA-956) 2016-02-25 18:41:24 +01:00
Tim Kuipers 277e1581d0 fix: retrieve all layer_height and layer_height_0 settings from meshgroup or global settings (CURA-956) 2016-02-25 18:32:35 +01:00
Tim Kuipers d4b128e0f3 fix: retrieve all layer_height settings from meshgroup or global settings (CURA-956) 2016-02-25 18:30:40 +01:00
Tim Kuipers eec65df83e refactored SliceMeshStorage storage ==> mesh (CURA-956) 2016-02-25 18:15:08 +01:00
Tim Kuipers 78e1c3114d refactor: introduce getSettingInMillimeters (CURA-956) 2016-02-25 18:07:39 +01:00
Tim Kuipers 5941d2252c fix: get infill sparse combine per mesh (CURA-949) 2016-02-25 10:21:44 +01:00
Tim Kuipers 6897a87584 fix: infill direction switch every two layers when infill_sparse_thickness is 2 layers and pattern is lines (CURA-943) 2016-02-25 09:49:51 +01:00
David Bender cc2bb36fb4 getSetting functions should be const 2016-02-24 11:01:57 -05:00
David Bender 0ea387a6f0 min/max functions should be const 2016-02-24 11:01:16 -05:00
Tim Kuipers 14a01a6253 fix: supportOnByuildplateOnly didn't account for conical support (CURA-914) 2016-02-24 16:07:32 +01:00
Tim Kuipers c20d35e293 fix: used adress comparison on ListPolygons instead of in depth comparison (CURA-934) 2016-02-23 16:23:09 +01:00
Tim Kuipers 9e56841cd2 fix: explicitly deleted polygon comparison, cause it is inefficient, and you most likely need to compare either the memory adress or need to account for the fact that two different polygons might have a different starting point (CURA-934) 2016-02-23 16:21:30 +01:00
Tim Kuipers c39e43c161 bugfix: combing shortcuts to end of comb move (CURA-893) 2016-02-23 15:38:33 +01:00
Tim Kuipers 421a6d4095 removed line from wrong cherry-pick (CURA-894) 2016-02-23 15:38:23 +01:00
Tim Kuipers 8497e46542 bugfix: moveInside always moved in the positive direction for the corner case (CURA-579) 2016-02-23 15:33:04 +01:00
Tim Kuipers 6510ebbd92 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-02-22 17:27:06 +01:00
Tim Kuipers c1b4a5398b bugfix: small zgaps no-heuristic bugfixes (CURA-921)
It didn't create the right amount of top and bottom layers and didn't work correctly on the topmost and bottom most layers.
2016-02-22 17:26:57 +01:00
Arjen Hiemstra 11c4b9339a Store layer messages in a hash map to speed up lookups for layers 2016-02-22 17:23:17 +01:00
Tim Kuipers 75efbac68e fix: cone angle sign inverted: more sensible fix (CURA-869) 2016-02-22 17:08:04 +01:00
Tim Kuipers cd199dc43e fix: cone angle sign inverted (CURA-869) 2016-02-22 16:50:27 +01:00
Tim Kuipers e85eec54ec bugfix: combing shortcuts to end of comb move (CURA-893) 2016-02-22 15:57:29 +01:00
Tim Kuipers dcc4d956b2 feat: limit inside boundary to infill (CURA-694) 2016-02-22 15:35:12 +01:00
Tim Kuipers 91249dd012 refactor: infill_area ==> infill_area_per_combine; introduced infill_area for the whole infill area (CURA-694) 2016-02-22 15:34:39 +01:00
Tim Kuipers 4de1f1abdf removed old unused function (CURA-694) 2016-02-22 15:32:48 +01:00
Tim Kuipers 59abad0197 feat: refactor combing from bool into enum; introduced noskin (CURA-694) 2016-02-22 15:31:54 +01:00
Tim Kuipers 0dc7e326c9 refactor: made combPath.throughAir a property of all combPaths (CURA-893) 2016-02-22 11:27:56 +01:00
Tim Kuipers 3271bde77e lil 2016-02-19 16:13:03 +01:00
Tim Kuipers 7b71426839 refactor: const correctness loosely related to combing (CURA-893) 2016-02-19 15:30:29 +01:00
Tim Kuipers 27e3df2fbc fix: one combing calc changed avoid_other_parts for the next (CURA-893) 2016-02-19 15:06:50 +01:00
Tim Kuipers 2e915039e6 fix: is_inside info is retained after layer switch (CURA-893) 2016-02-19 14:38:37 +01:00
Tim Kuipers 8262ff3ac6 refactor/fix: call setIsInside only in addMeshLayerToGcode (CURA-893) 2016-02-19 12:19:57 +01:00
Tim Kuipers 5cc0eb24ef optimization: faster moveOutside - if possible (CURA-893) 2016-02-19 10:59:31 +01:00
Tim Kuipers dad74e1cd6 fix: crossing_2_in_or_mid was based on itself instead of closest to crossing_1 (CURA-893) 2016-02-18 18:33:05 +01:00
Tim Kuipers a878a7f091 fix: setIsInside bugs (CURA-893) 2016-02-18 18:27:48 +01:00
Tim Kuipers 53a543c548 lil 2016-02-18 17:26:15 +01:00
Tim Kuipers 0366b9df71 feat: skip crossing computation when boundary to boundary is already closer than twice the avoid distance (CURA-893) 2016-02-18 17:23:51 +01:00
Tim Kuipers 9c1f74fff1 fix: moveInside registered wrongly whether the point was already on the correct side of the boudary (CURA-893) 2016-02-18 16:49:52 +01:00
Tim Kuipers 1e0d416a5b lil fix: wrong move outside distance for crossings for combing (CURA-893) 2016-02-18 16:46:39 +01:00
Tim Kuipers 4612c69ae3 feat: moveInside utility function; directly calls moveInside (CURA-893) 2016-02-18 15:57:17 +01:00
Tim Kuipers 916e0b221d fix: larger admitted crossing dist for combing (CURA-893) 2016-02-18 15:51:16 +01:00
Tim Kuipers a05d31456d feat: also use dist from start to crossing when evaluating which crossing is the best (CURA-893) 2016-02-18 13:28:06 +01:00
Tim Kuipers 8d82b8b943 bugfix: lil mistake in combing second crossing (CURA-893) 2016-02-18 13:16:18 +01:00
Tim Kuipers 948c9b7054 refactor & fix: made findBestCrossing return ClosestPolygonPoints themselves which makes it easy to move inside/outisde with the offset_dist_to_get_from_on_the_polygon_to_outside (CURA-893) 2016-02-18 12:09:49 +01:00
Tim Kuipers 28d13b4aa1 bugfix: crossing dists were based on wrong value (CURA-893) 2016-02-18 12:07:59 +01:00
Tim Kuipers 0b0f85f9ff bugfix: moveInside couldn't perform moving outside well (CURA-579) 2016-02-17 17:59:33 +01:00
Tim Kuipers ef55f2ea11 bugfix: combing crossing computation refered to wrong crossing (CURA-579) 2016-02-17 17:23:56 +01:00
Tim Kuipers 692180e185 bugfix: combing crossing computation refered to wrong crossing (CURA-579) 2016-02-17 17:23:27 +01:00
Tim Kuipers e25a681201 lil refactor (CURA-893) 2016-02-17 17:13:29 +01:00
Tim Kuipers dca1beeba2 feat: combing: better finding of good crossing between inside and outide (CURA-893) 2016-02-17 16:45:22 +01:00
Tim Kuipers d10fea3ee6 refactor: modified findClose(Polygon ,...) such that it returns pairs of points on the first and points on the second polygon (CURA-893) 2016-02-17 16:04:51 +01:00
Tim Kuipers 1e93ffd83b feat: made Comb have an optional bucketGrid mapping from locations to line segments in the outside boundary (computed when needed) (CURA-893) 2016-02-17 15:56:44 +01:00
Tim Kuipers d41b842b39 refactor: introduced some const correctness for BucketGrid (CURA-893) 2016-02-17 15:43:30 +01:00
Tim Kuipers 59c3047543 feat: introduced findClose for a whole polygon (CURA-893) 2016-02-17 15:41:15 +01:00
Tim Kuipers b53f147b14 doc: findClosest & createLocToLineGrid (CURA-893) 2016-02-17 14:59:11 +01:00
Tim Kuipers 1768072cec unittest: findClose (CURA-893) 2016-02-17 14:45:01 +01:00
Tim Kuipers b2b4847b85 bugfix: findClose: end point of line segment was inserted instead of start point (CURA-893) 2016-02-17 14:43:09 +01:00
Tim Kuipers 3a832ef492 feat: findClose based on BucketGrid (CURA-893) 2016-02-17 12:33:00 +01:00
Tim Kuipers 03749e98ea fix: BucketGrid.insert expected reference instead of an object (CURA-893) 2016-02-17 12:15:03 +01:00
Tim Kuipers 9e410e7007 lil include reorder 2016-02-17 12:01:23 +01:00
Tim Kuipers beab605308 lil (CURA-579) 2016-02-16 17:35:46 +01:00
Tim Kuipers 9de4b7e939 fix: combing now uses correctly offsetted points from in_between (CURA-579) 2016-02-16 17:31:15 +01:00
Tim Kuipers 0d7710ecd1 refactor: made combing make use of new moveInside (CURA-579) 2016-02-16 17:21:22 +01:00
Tim Kuipers fe7364c146 refactor: simplified moveInside code (CURA-579)
make use of getBoundaryPointWithOffset
2016-02-16 17:16:24 +01:00
Tim Kuipers 98b1fda1f1 lil (CURA-579) 2016-02-16 16:56:11 +01:00
Tim Kuipers 151bef23c9 tests: better moveInside tests for both moveInside functions (CURA-579) 2016-02-16 16:51:14 +01:00
Tim Kuipers b9a411df10 bugfix: moveInside always moved in the positive direction for the corner case (CURA-579) 2016-02-16 16:43:43 +01:00
Tim Kuipers 0f53a7a231 tests of PolygonUtils::moveInside(ClosestPolygonPoint) (CURA-579) 2016-02-16 16:42:11 +01:00
Tim Kuipers 57ac6aa926 refactor: make new moveInside function work on ClosestPolygonPoint (CURA-579) 2016-02-16 14:47:00 +01:00
Tim Kuipers 2ba03ff1dc fixes: improved moveInside function for when you already found the nearest polygon point (CURA-579) 2016-02-16 14:25:33 +01:00
Tim Kuipers f59ca9c33e fixes: moveInside function for when you already found the nearest polygon point (CURA-579) 2016-02-16 13:39:46 +01:00
Tim Kuipers 8fc6ee87ee introduced moveInside function for when you already found the nearest polygon point (CURA-579) 2016-02-16 13:32:24 +01:00
Tim Kuipers 48707d95f7 refactor: some more renaming in combing (CURA-579) 2016-02-16 10:52:53 +01:00
Tim Kuipers 7a1a900d78 refactor: better naming for stuff like inside_middle_from (CURA-579) 2016-02-15 18:07:24 +01:00
Tim Kuipers 38c8941b59 Revert "fix: no move outside if it's a detour (CURA-579)"
This reverts commit f28cf53651.
2016-02-15 17:18:34 +01:00
Tim Kuipers df47cf8b2d refactor: typo iddle ==> middle (CURA-579) 2016-02-15 17:13:51 +01:00
Tim Kuipers f28cf53651 fix: no move outside if it's a detour (CURA-579) 2016-02-15 17:03:33 +01:00
Tim Kuipers 9d063d885c refactor: code indentation for readability (CURA-579) 2016-02-15 16:57:07 +01:00
Tim Kuipers 3ec63017cc refactor: getBoundaryOutside now returns ref instead of pointer, cause it ensures the return is instantiated (CURA-579) 2016-02-15 16:55:28 +01:00
Tim Kuipers 26dfd02b26 refactor: separate inside_middle_from computation from inside_middle_to comp (CURA-579) 2016-02-15 16:42:53 +01:00
Tim Kuipers e177303c4b Merge branch '2.1' 2016-02-15 15:20:30 +01:00
Ghostkeeper 4c1c43649d Fix setting-crash test with function evaluation
The minimum value, maximum value, minimum warning value and maximum warning value of each setting is now evaluated as a function, preventing casting errors. Runtest.py runs without errors or even test failures again now.

Contributes to issue CURA-814.
2016-02-15 14:05:27 +01:00
Ghostkeeper 2372a78c9b Fix setting-crash test with function evaluation
The minimum value, maximum value, minimum warning value and maximum warning value of each setting is now evaluated as a function, preventing casting errors. Runtest.py runs without errors or even test failures again now.

Contributes to issue CURA-814.
2016-02-15 12:54:44 +01:00
Tim Kuipers d3f0a06ee0 lil TODO (CURA-833) 2016-02-12 12:09:37 +01:00
Ghostkeeper 4f524613fd Merge pull request #304 from soyersoyer/master
fix build warnings
2016-02-12 09:51:34 +01:00
Tim Kuipers f94c95dd97 bugfix: total layers got updated by infill mehses CURA-833 2016-02-11 21:21:23 +01:00
Tim Kuipers 07b7d84df2 bugfixes new progress system (CURA-873) 2016-02-11 20:56:35 +01:00
Tim Kuipers 97ee04c12b lil fixes better progress estimator structure (CURA-873) 2016-02-11 20:13:21 +01:00
Tim Kuipers 18c7c6bdb9 fix: more secure layer indexing in infill mehses (CURA-833) 2016-02-11 20:01:38 +01:00
Tim Kuipers e93770017f refactor+cleanup for infill mehses (CURA-833) 2016-02-11 19:56:15 +01:00
Tim Kuipers 60e1f30c60 lil optimization: less splitIntoParts in processInfillMesh (CURA-833) 2016-02-11 19:47:56 +01:00
Tim Kuipers d4844d08f2 lil refactor: processDerivedWallSkinInfill doesn't have argument TimeKeeper anymore (CURA-833) 2016-02-11 19:46:06 +01:00
Tim Kuipers 30df9853e4 bugfix: infill meshes had too much infill overlap (CURA-833) 2016-02-11 18:59:46 +01:00
Tim Kuipers 4ff00a8f73 first approximation infill meshes (CURA-833) 2016-02-11 18:25:45 +01:00
Tim Kuipers f5b7cadcb5 refactor progress for better progress during skin and insets (CURA-873) 2016-02-11 17:44:24 +01:00
Tim Kuipers 17ca8fce0a fix: progress fixed for fffPolygonProcessor refactor
CURA-872

there is no progress information during processing insets and skin of a single mesh
2016-02-11 16:44:48 +01:00
Tim Kuipers dbdbec44cc refactor: split up slices2polygons in functions which work on each mesh separately (CURA-872) 2016-02-11 16:21:50 +01:00
soyer a9fdad71b4 SettingConfig’s parent is unused 2016-02-11 16:17:55 +01:00
soyer 9fb6a217a4 use the std::abs, it has a proper overload for the long long 2016-02-11 16:15:35 +01:00
soyer c480c96066 this is a function declaration, and this is useless here 2016-02-11 16:13:38 +01:00
Tim Kuipers 1bcb38dcb6 Merge pull request #303 from soyersoyer/master
fix build in the without arcus case
2016-02-11 15:22:34 +01:00
soyer 0e7b164532 without arcus the code can’t send SlicingFinished message, so do nothing. 2016-02-11 12:11:34 +01:00
soyer 0f6bdfd36e the static CommandSocket::instance should be set without arcus too 2016-02-11 12:08:22 +01:00
Tim Kuipers 4d79ea3e9e doc+refactor: fan speed calc more clear (CURA-863) 2016-02-10 17:00:57 +01:00
Tim Kuipers 199fa070d6 feat: findNearestObject with extra conditions (CURA-590) 2016-02-10 11:43:46 +01:00
Tim Kuipers ef1dece5d2 fix: BucketGrid2D: disregard bucket-collision items in findNearbyObjects (CURA-590) 2016-02-10 11:19:19 +01:00
Tim Kuipers 87e42fd9bd fix: BucketGrid2D: enhancement of hashing protocols (CURA-590) 2016-02-10 11:12:54 +01:00
Tim Kuipers 8d0a75779d doc; refactor: BucketGrid2D (CURA-590)
introduced typedef CellIdx to make clear which Point variables are used to signify a cell index
2016-02-10 11:11:47 +01:00
Tim Kuipers e882b23d76 Merge branch '2.1' 2016-02-09 17:08:34 +01:00
Tim Kuipers 142f4d519f Merge branch 'bugfix_coasting_prime' into 2.1 2016-02-08 16:28:50 +01:00
Arjen Hiemstra 9ea43e7fc1 Move addListener call to before message registration
This makes it possible to receive debug information about message registration
2016-02-07 18:56:28 +01:00
Arjen Hiemstra 9b92de9b8b Add an Arcus::SocketListener subclass so we can log all errors and debug 2016-02-04 12:16:30 +01:00
Tim Kuipers 4361dbf8fb better print time output 2016-02-03 18:02:34 +01:00
Tim Kuipers 81ae074b86 Merge branch '2.1' 2016-02-03 17:45:04 +01:00
Ghostkeeper 6826581497 Fix compile error
That needs to be a C-string.
2016-02-02 13:08:25 +01:00
Jaime van Kessel 4fead4612b Merge branch 'new_arcus_api' of github.com:Ultimaker/CuraEngine into 2.1 2016-02-02 11:25:45 +01:00
Ghostkeeper f763edfb05 Send FinishedSlicing message later
The FinishedSlicing message was being sent after the mesh group is done slicing, but it should've been sent after all slicing is done (also other mesh groups and after sending the metadata). This indicates that the slicer is really really done.

Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Ghostkeeper 0e662d7d67 Register SlicingFinished message type
The message type of this message is 8 (as documented in the proto file).

Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Ghostkeeper 94d8c3ff32 Document protobuf message typeid
Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Arjen Hiemstra 8e91753afc Ignore ObjectList messages with no objects and Objects with no vertices 2016-01-28 18:17:24 +01:00
Tim Kuipers 3171bd4dcb fix: infill overlap in mm (CURA-786) 2016-01-28 16:30:07 +01:00
Tim Kuipers 199007fa76 Merge branch '2.1' 2016-01-28 15:07:26 +01:00
Tim Kuipers 203eb05d7c bugfix: support overlapping with model for xy_distance=0 (CURA-795) 2016-01-28 13:22:23 +01:00
Tim Kuipers 9e75f8c70c fix: z distance used at overhang instead of xy-distance (CURA-795)
factored computeBasicAndFullOverhang out of generateSupportAreas
keep overhang areas in a rolling deque so that they are available for the current layer and the layer supportZDistanceTop above
implemented some polygon magic to ensure that the distance of support to overhang areas is less than from non-overhang areas
2016-01-28 13:12:24 +01:00
Tim Kuipers 93b3d2e46e refactor: made some functions const in sliceDataStorage (CURA-795) 2016-01-28 13:03:49 +01:00
Arjen Hiemstra 21e59cc1e2 Update CommandSocket with the new Arcus API 2016-01-28 03:24:12 +01:00
Tim Kuipers f0f14b0be3 fix: prime too slow after coasting due to unset speed (CURA-796) 2016-01-27 17:40:04 +01:00
Tim Kuipers a82c00bead fix: prime too much after coasting (CURA-796) 2016-01-27 17:39:37 +01:00
Tim Kuipers 14b1c5333a fix: retraction was prevented for non-direct support to support moves which did avoid other parts (CURA-777)
most notably I introduced path[0] == lastPosition && path[1] == p to the condistion; rest is just restruction
2016-01-27 12:16:20 +01:00
Tim Kuipers bbe809dabc fix: sendPolygons in coasting had a lil bug (CURA-778)
sendPolygons was called for lines toward the starting point instead of the new point

also a line was sent for the coasted path, which is actually just a move path
2016-01-25 17:08:45 +01:00
Tim Kuipers ed581a92b2 fix: reintroduced infill_overlap for the right features (CURA-765) 2016-01-25 16:38:47 +01:00
Tim Kuipers 9b68305851 refactor: matrix ==> rotation_matrix (CURA-401) 2016-01-25 12:01:09 +01:00
Tim Kuipers 940d3a86bd refactor: moved some implementation comments and renamed 'uneven' to 'odd' (CURA-401) 2016-01-25 11:27:52 +01:00
Tim Kuipers 55047120d8 refactor: split ZigZagConnectorProcessor into separate files (CURA-401) 2016-01-25 11:05:58 +01:00
Tim Kuipers e50b00fd73 fix: introduced new socket msg SlicingFinished (CURA-427) 2016-01-22 10:57:27 +01:00
Tim Kuipers 7377f30fd0 lil (remove unused var) 2016-01-21 10:26:24 +01:00
Tim Kuipers c9245e0926 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 14:55:28 +01:00
Tim Kuipers 7149b7dbf1 fix: infill was overlapping with wall with incorrect amount; skin was incorrect when using a single wall and the outer wall line width differs from the inner walls (CURA-729) 2016-01-20 14:37:13 +01:00
Ghostkeeper daa3927a99 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 14:34:13 +01:00
Ghostkeeper 074d1125e6 Fix STL header size
This was actually causing errors with loading STL files.

Sort of contributes to issue CURA-605.
2016-01-20 14:33:52 +01:00
Tim Kuipers a92274678d Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 11:22:36 +01:00
Tim Kuipers 081393d7f0 fix: offset issues (CURA-401) 2016-01-20 11:22:10 +01:00
Tim Kuipers 30da3097d0 lil code convention refactor (CURA-401) 2016-01-19 17:33:46 +01:00
Tim Kuipers d830fc515b lil 2016-01-19 17:28:47 +01:00
Tim Kuipers 5f2d924771 refactor: let fill_angle and line_distance be retrieved from Infill members (CURA-401) 2016-01-19 17:23:01 +01:00
Tim Kuipers b1ab1cae5a refactor: renamed variables to lower_case (code convention) (CURA-401) 2016-01-19 17:19:49 +01:00
Tim Kuipers 1fb6b20d90 refactor: let in_outline and outlineOffset be retrieved from Infill members (CURA-401) 2016-01-19 17:14:29 +01:00
Tim Kuipers dfb4c98e35 doc: infill documentation (CURA-401) 2016-01-19 13:40:21 +01:00
Tim Kuipers e455d63ad2 refactor: made infill generation functions public and let them use Infill member variables; also a bugfix where a PointMatrix was requested but a double was given (CURA-401) 2016-01-19 13:07:45 +01:00
Tim Kuipers cb024c73b1 refactor: made infill generation functions non-static (CURA-401) 2016-01-19 12:45:59 +01:00
Tim Kuipers d2d25058cb more comments for infill generation (CURA-401) 2016-01-19 12:29:12 +01:00
Tim Kuipers e85362370b more comments for (zigzag) infill generation (CURA-401) 2016-01-19 12:24:18 +01:00
Tim Kuipers 00c6f5c092 refactor: extrusionWidth ==> infill_line_width (CURA-401) 2016-01-18 17:29:14 +01:00
Tim Kuipers 818c7da951 lil comments (CURA-401) 2016-01-18 17:28:58 +01:00
Tim Kuipers 41723c8b38 removed unused registerPolyStart (CURA-401) 2016-01-18 17:22:10 +01:00
Tim Kuipers c8d75dd913 refactor: cleaned up inheritance structure of ZigZagConnectorProcessor classes (CURA-401)
removed code duplication by introducing another class called ActualZigzagConnectorProcessor.

explained the inheritance structure in the base class description.

made some things protected instead of public.
2016-01-18 16:49:39 +01:00
Tim Kuipers faa60c408f doc: documentation cleanup and expansion (CURA-401) 2016-01-18 15:27:40 +01:00
Tim Kuipers 7e322bc57c refactor: correction of tabs and spaces only (CURA-401) 2016-01-18 14:55:07 +01:00
Tim Kuipers 888fc54660 refactor: made all global infill functions private static (CURA-401) 2016-01-18 14:53:33 +01:00
Tim Kuipers fd660fcc11 refactor: connect_zigzags ==> connected_zigzags (cura-401) 2016-01-18 14:18:56 +01:00
Tim Kuipers e8080422a4 refactor: generateLineInfill_alt ==> generateLinearBasedInfill (CURA-401) 2016-01-18 14:16:33 +01:00
Tim Kuipers 5fecf2cd17 removed all old unused generateLine/ZigZagInfill code (CURA-401) 2016-01-18 14:15:26 +01:00
Tim Kuipers b4bf17c6be refactor: let rotation_matrix bubble down from Infill class; introduce some appropriate consts (CURA-401) 2016-01-18 14:07:38 +01:00
Ghostkeeper fabd658b53 STL file binary vs. ASCII detection robust against whitespace
The ASCII file may contain whitespace at the start of the file. This causes the STL file to be flagged as binary (since it doesn't start with "solid"). This fix first skips all whitespace before trying to find "solid".

Contributes to issue CURA-605.
2016-01-17 04:42:48 +01:00
Ghostkeeper a2050de513 Binary STL load use file size instead of face count
To determine the amount of memory allocated and the amount of faces to try to load, use the size of the file rather than the reported face count in the file. The reported face count may be missing or corrupt, in which case the engine would crash. If that happens now, it might miss one face. It will also give a warning if the reported face count does not match.

Contributes to issue CURA-605.
2016-01-17 04:09:14 +01:00
Tim Kuipers 9bfb5b17e9 refactor: made ZigzagConnectedEndpieces and ZigzagDisconnectgedEndpieces inherit from virtual ZigzagEndpieces; fix: several bugfixes (CURA-401)
notably the disconnected endpieces now disconnects all endpieces instead of half
2016-01-15 17:37:03 +01:00
Tim Kuipers e93d39d6d5 fix: disconnected endpieces (zigzag); reordered some of the connected endpieces code (CURA-401) 2016-01-15 17:16:46 +01:00
Tim Kuipers 175a65415a fix: support zigzag connected endpieces bugfixes (CURA-401)
before, only half of the endpieces were drawn

some problems had slipped into the translation, such as the resetting after each polygon
2016-01-15 16:53:39 +01:00
Tim Kuipers 74986dd95c lil refactor: changed last_var to this_var at the moment it was set for conceptual clarity (CURA-401) 2016-01-15 16:49:55 +01:00
Tim Kuipers 55dd35ae5e refactor: logic simplifications; fix: reintroduce skipping of scanline if poly is simple (CURA-401)
rewrote some for-if combinations which could be made simpler

small polygons which only cover a single scanline which are printed in total don't need the scanline...
2016-01-15 15:39:25 +01:00
Tim Kuipers 8caf810642 refactor: code conventions etc for generate(Lines/Zigzag)Infill (CURA-401) 2016-01-15 15:23:41 +01:00
Tim Kuipers 76efc41407 refactor: merge of generate(Lines/ZigZag)Infill (CURA-401) 2016-01-15 14:50:24 +01:00
Tim Kuipers b0a0fe8f30 refactor: code conventions in generate infill paths (CURA-401) 2016-01-14 11:17:06 +01:00
Tim Kuipers 653b631ffd lil 2016-01-14 11:16:36 +01:00
Tim Kuipers b9a01ed031 bugfix: made MargeInfill.isConvertible not static, cause it needs the nozzle_size class member (CURA-581) 2016-01-13 12:10:37 +01:00
Tim Kuipers de9d3bb447 lil fix: MergeInfill.nozzle_size used instead of line_width (CURA-581)
MergeInfillLines already had a member nozzle_size, though still it had a line defining a local variable 'int nozzle_size = line_width; // TODO'

Also some minor code convention brackets...
2016-01-13 11:50:47 +01:00
Arjen Hiemstra 8f5a46f77d Ensure we use libc++ as stdlib on OSX 2016-01-06 16:56:04 +01:00
Tim Kuipers d0858bbdb6 merge of 2.1 2016-01-06 15:03:29 +01:00
Tim Kuipers c22793b5d7 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-05 18:22:52 +01:00
Tim Kuipers 1e684f23a7 refactor: removed old sendPolygons functions (CURA-379) 2016-01-05 18:22:34 +01:00
Tim Kuipers 7d0025975b refactor: removed old sendPolygons functions (CURa-379) 2016-01-05 18:20:44 +01:00
Tim Kuipers 5c892b564b fix: send all polygons at end of slicing process instead of during (CURA-379) 2016-01-05 18:17:03 +01:00
Tim Kuipers d5327ec3f1 refactor: let all CommandSocket calls go throught CommandSocket.getInstance() (CURA-379) 2016-01-05 17:35:12 +01:00
Tim Kuipers c48104bc86 refactor: made CommandSocket a kind of singleton class (CURA-379) 2016-01-05 17:09:08 +01:00
Tim Kuipers f58f1daec3 refactor: commandSocket ==> command_socket (CURA-379) 2016-01-05 17:08:24 +01:00
Tim Kuipers 669bb523a3 bugfix: commandSocket depends on there being a NoneType (CURA-581) 2016-01-05 13:07:26 +01:00
Tim Kuipers fa74f672ac lil 2016-01-05 12:23:44 +01:00
Tim Kuipers e224d9c853 MergeInfillLines early abort if space filling type is not Lines (CURA-581) 2016-01-05 12:23:10 +01:00
Tim Kuipers 338d80a7a9 lil fix in test (CURA-581) 2016-01-05 12:20:46 +01:00
Tim Kuipers 87123dac31 introduction of GCOdePath.space_fill_type (Polygon,Line or PolyLine) (CURA-581) 2016-01-05 12:20:09 +01:00
Tim Kuipers 73de288f44 lil cleanup of unused commented out code (CURA-581) 2016-01-05 11:30:43 +01:00
Tim Kuipers 9f5b5f405d lil enhancements mergeInfillLines (CURA-581) 2016-01-05 11:27:06 +01:00
Tim Kuipers 2850b34227 fix: mergeInfillLines aborts if applied to a print feature other than infill or skin (CURA-581) 2016-01-05 11:23:51 +01:00
Tim Kuipers 072d320f1d refactor: FeatureType ==> PrintFeatureType (CURA-581) 2016-01-05 11:11:39 +01:00
Tim Kuipers 8e4b61d2a5 merge of 4c139d6441 (CURA-581) 2016-01-05 11:10:56 +01:00
Tim Kuipers ba4e26f801 bugfix: concentric infill overlapped with skin; extra offset for concentric infill moved inside infill.cpp (CURA-650) 2016-01-05 10:04:33 +01:00
Tim Kuipers cea0a0a98f bugfix: no draft shield or concentric infill sendPolygons (CURA-639) 2016-01-04 16:55:41 +01:00
Ghostkeeper 2b51a11739 Fix new command socket not propagating to LayerPlanBuffer
This caused the command socket in LayerPlanBuffer to remain null, thus never flushing the g-code until the very end, thus causing the front-end to interpret the entire g-code as one layer, thus causing the post processing plugin to inject g-code at the wrong places.

Contributes to issue CURA-443.
2015-12-31 11:22:37 +01:00
Tim Kuipers cc23d73532 refactor: FeatureType ==> PrintFeatureType (CURA-606) 2015-12-18 16:32:13 +01:00
Tim Kuipers 4c139d6441 refactor: PolygonType ==> FeatureType in PrintFeature.h (CURA-606) 2015-12-18 16:32:02 +01:00
Tim Kuipers 13a18549bf Merge branch '2.1' 2015-12-18 14:32:47 +01:00
Tim Kuipers d32443ca9b bugfixes: merge infill (CURA-581) 2015-12-18 14:25:07 +01:00
Tim Kuipers 792aa9e8e8 merge infill improvements (CURA-581) 2015-12-18 12:50:50 +01:00
Tim Kuipers a63abb4e88 merge infill improvements maybe (CURA-581) 2015-12-18 10:49:12 +01:00
Tim Kuipers 9f82f58f03 bugfix: moveInside called with max_dist instead of max_dist_squared (CURA-522) 2015-12-15 16:33:51 +01:00
Tim Kuipers bd2f66e2eb bugfix: child settings got duplicated in extruder trains... (CURA-494) 2015-12-14 15:29:26 +01:00
Tim Kuipers 267eb7aef0 bugfix: made engine work both both extruder trains being an array as it being an object (CURA-494) 2015-12-14 14:14:08 +01:00
Tim Kuipers df04467ab1 better doc (CURA-522) 2015-12-09 17:24:05 +01:00
Tim Kuipers bbd9412b5f bugfixes moveInside (CURA-522) 2015-12-09 17:23:50 +01:00
Tim Kuipers c476acd522 bugfix: moveInside distance was BS (CURA-522) 2015-12-09 17:22:50 +01:00
Tim Kuipers 278efdeb39 merge of cherry-pick of 6115bde (CURA-522) 2015-12-09 17:22:09 +01:00
Tim Kuipers bb8a9dacba removed Comb.moveInside and Comb.isInside and inlined the implementation (CURA-522)
Since comb_boundary_inside is now a field of gcodeplanner these utility functions were at an awkward place.

Conflicts:
	src/gcodePlanner.cpp
	src/gcodePlanner.h
2015-12-09 17:18:55 +01:00
Tim Kuipers 42420edeed merge of cherry-pick of 5c3727d (CURA-522) 2015-12-09 17:11:56 +01:00
Tim Kuipers be2e96f3fd getLayerSecondOrInnermostWalls functions (CURA-522) 2015-12-09 17:04:50 +01:00
Tim Kuipers 18adc0bbc9 merge of cherry-pick of c7ce43e (CURA-522) 2015-12-09 17:02:37 +01:00
Tim Kuipers e7824faefe merge of cherry-pick of c7ce43e (CURA-522) 2015-12-09 17:01:37 +01:00
Tim Kuipers 7199be1b4f merge of cherry-pick of d9e2b39 (CURA-522) 2015-12-09 16:54:13 +01:00
Tim Kuipers 8cd48bf5a2 settingsRegistry reorganized and made more robust; more stuff will override if possible; extruder trains can now be overridden (CURA-494) 2015-12-09 16:27:31 +01:00
Tim Kuipers a766455ec8 lil (CURA-494) 2015-12-09 16:24:47 +01:00
Tim Kuipers af08b57799 makefile now copies command_line_settings.json to binary dir (CURA-566) 2015-12-09 09:37:29 +01:00
Tim Kuipers dbecb29dc8 command line settings json (CURA-566) 2015-12-09 09:06:29 +01:00
Tim Kuipers 7df0a34464 lil bugfix settingsRegistry (CURA-566) 2015-12-09 09:05:39 +01:00
Tim Kuipers 925d50fc5d removed prime_tower_distance (was already unused) (CURA-566) 2015-12-09 09:05:11 +01:00
Tim Kuipers e3163586af Merge of branch 2.1 2015-12-08 16:24:53 +01:00
Tim Kuipers 727c863f1a optimization: handle socket object list after listening for messages (CURA-441) 2015-12-08 15:58:00 +01:00
Tim Kuipers d732c49dd7 bugfix: close socket before exiting the program (CURA-441) 2015-12-08 15:57:26 +01:00
Tim Kuipers 781fc5ed7b lil doc (CURA-524) 2015-12-08 14:48:19 +01:00
Tim Kuipers 7243cf6da4 renamed commandSocket.d to commandSocket.private_data (CURA-524) 2015-12-08 14:44:15 +01:00
Tim Kuipers 1cdcce4205 revert of 6224713998 (CURA-524) 2015-12-08 14:42:00 +01:00
Tim Kuipers acf381c008 bugfix: no vizualization of last layers trvel moves (CURA-524) 2015-12-08 14:29:44 +01:00
Tim Kuipers 4b7df9ddc0 lil refac + doc (CURA*524) 2015-12-08 14:28:52 +01:00
Tim Kuipers 7788a4a234 refactor: sendGCodeLayer ==> flushGcode (CURA-524) 2015-12-08 14:28:20 +01:00
Tim Kuipers 6224713998 renamed commandSocket.d to commandSocket.private_data (CURA-524) 2015-12-08 13:53:22 +01:00
Tim Kuipers db8b30d77a refactor: Min Volume Before Coasting doesn't include Coasting Volume anymore (CURA-528) 2015-12-08 13:32:31 +01:00
Tim Kuipers f58441a6ad refactor: added braces for checks of command_socket (CURA-499) 2015-12-08 13:16:03 +01:00
Tim Kuipers 050b9c88f2 bugfix: coasting volume more than min volume (CURA-528) 2015-12-07 18:03:40 +01:00
Ghostkeeper 8ac63fca6e Log a warning when trying to override nonexistent setting
If there's a problem in the front-end this should expose it.

Contributes to issue CURA-544.
2015-12-07 15:38:21 +01:00
Ghostkeeper 1383882bc5 Fixed segfault when provided with unknown settings
When an override of a setting is provided via the JSON the engine would crash. This ignores those settings.

Contributes to issue CURA-544.
2015-12-07 15:18:39 +01:00
Tim Kuipers 04b4b2c057 merge 2015-12-07 13:42:31 +01:00
Tim Kuipers 3a773d3c0f bugfix: layer plan buffer flush did flush water, but left the turd (CURA-463) 2015-12-07 12:39:25 +01:00
Tim Kuipers 734ddce3c8 lil (CURA-463) 2015-12-07 12:38:14 +01:00
Tim Kuipers 0b19936299 main didn't allow for next meshgroup to be processed (CURA-463) 2015-12-07 12:37:02 +01:00
Tim Kuipers 691d5de591 lil doc (CURA-499) 2015-12-04 17:04:45 +01:00
Tim Kuipers 490cef1a5c Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2015-12-04 16:57:00 +01:00
Tim Kuipers b777b55935 bugfix: output command line call (CURA-499) 2015-12-04 16:56:49 +01:00
Ghostkeeper 0f5fd8d6ca Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-12-04 16:47:16 +01:00
Ghostkeeper 645b06271d Remove support-specific test
Support is already tested somewhat in the default test model. This makes the testing a bit more efficient.

Contributes to issue CURA-349.
2015-12-04 16:46:59 +01:00
Ghostkeeper 0428c08152 Fix indenting
The main function of runtest.py was indented with tabs instead of spaces.
2015-12-04 15:58:53 +01:00
Tim Kuipers ef61337ef8 better doc (CURA-522) 2015-12-04 13:55:08 +01:00
Tim Kuipers 20c74dd22d lil bugfix CURA-486 (stiekum gekoppeld) 2015-12-03 18:47:12 +01:00
Tim Kuipers 188b190d21 inlined writePathWithCoasting (CURA-486) 2015-12-03 18:05:29 +01:00
Tim Kuipers 1cd128decd coasting collapsed: no more diff between move-coasting and retract-coasting (CURA-486) 2015-12-03 18:01:34 +01:00
Tim Kuipers 925247a54d bugfix: don't perform the retraction *during* coasting (CURA-486) 2015-12-03 18:00:34 +01:00
Tim Kuipers b8d5474811 merge 2015-12-03 14:57:13 +01:00
Ghostkeeper 4c60339695 Merge pull request #281 from Rhoban/master
Adding ENABLE_ARCUS option in CMakeLists

This allows compiling CuraEngine for command-line use only.
2015-12-03 11:01:57 +01:00
Gregwar e1a594ad3e ENABLE_ARCUS: Adding message(STATUS) line 2015-12-03 10:22:28 +01:00
Tim Kuipers 94d9a948ae bugfix: a zero length line has no 'beyond' (CURA-359) 2015-12-02 16:32:43 +01:00
Tim Kuipers 176bf2c887 bugfixes getDist2FromLineSegment (CURA-359) 2015-12-02 16:31:40 +01:00
Tim Kuipers 3cf208f8ed Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2015-12-02 14:54:32 +01:00
Tim Kuipers 08eec2ad1f bugfixes moveInside (CURA-522) 2015-12-02 14:54:21 +01:00
Tim Kuipers 41e012be5f bugfix: initialize comb_boundary_inside (CURA-522) 2015-12-02 14:53:39 +01:00
Tim Kuipers 8102f7a246 bugfix: moveInside distance was BS (CURA-522) 2015-12-02 14:32:18 +01:00
Tim Kuipers d527358a82 lil documentation cleanup (CURA-522) 2015-12-02 14:20:35 +01:00
Tim Kuipers 6115bdec2a bugfix: when combing was off, we didn't move inside before doing a retract (CURA-522) 2015-12-02 14:19:45 +01:00
Tim Kuipers 9f29cbd3fb removed Comb.moveInside and Comb.isInside and inlined the implementation (CURA-522)
Since comb_boundary_inside is now a field of gcodeplanner these utility functions were at an awkward place.
2015-12-02 14:18:46 +01:00
Tim Kuipers 5c3727d532 refactor: moved comb_boundary_inside outside of Comb to gcodePlanner (CURA-522) 2015-12-02 14:05:48 +01:00
Tim Kuipers 73152d84c7 getLayerSecondOrInnermostWalls functions (CURA-522) 2015-12-02 14:03:18 +01:00
Ghostkeeper 2b3f22872e Fix codestyle and superfluous commented code
Contributes to CURA-359.
2015-12-02 13:32:48 +01:00
Tim Kuipers c7ce43eb6c refactor: is_combing ==> is_inside (CURA-522) 2015-12-02 13:01:44 +01:00
Tim Kuipers d9e2b39baf bugfix: min travel dist was ignored (CURA-522) 2015-12-02 12:54:07 +01:00
Ghostkeeper fa03c3823f Merge branch '2.1'
Merging recent fixes to master, mostly to be able to get Jenkins to report the updated tests.
2015-12-02 12:15:39 +01:00
Ghostkeeper 34e86195e9 Correct getDist2FromLineSegmentZeroNearTest ground truth
20^2 is 400, not 40.

Contributes to CURA-359.
2015-12-02 12:13:24 +01:00
Ghostkeeper bad780c54b Add existing tests for zero-length lines to test suite
Think I forgot to add these two tests earlier.

Contributes to CURA-359.
2015-12-02 12:13:24 +01:00
Ghostkeeper 3ddbc1d53b Added test for large lines to point distance
This should test multiplication of rounding errors more properly.

Contributes to CURA-359.
2015-12-02 12:13:24 +01:00
Tim Kuipers 51757353b4 bugfix: skin orientation separate from infill orientation (CURA-363) 2015-12-02 11:47:08 +01:00
Tim Kuipers d931baa788 fix: reserve [extruder_count] extruder plans, so that it's quite unlikely for the vector to get resized (CURA-298) 2015-12-01 18:04:14 +01:00
Tim Kuipers 2cf0d40775 fix: reserve [extruder_count] extruder plans, so that it's quite unlikely for the vector to get resized (CURA-298) 2015-12-01 17:23:16 +01:00
Tim Kuipers d20df39d5f Merge branch '2.1' 2015-12-01 16:31:58 +01:00
Tim Kuipers 57bc411bc6 refactor: speed_iconic ==> speed_base, speed ==> speed_current (CURA-496) 2015-12-01 16:31:34 +01:00
Gregwar 0c30d27892 Adding ENABLE_ARCUS option in CMakeLists 2015-12-01 16:27:14 +01:00
Tim Kuipers 0c9562b273 rename: support_extruder_nr ==> support_infill_extruder_nr 2015-12-01 16:07:04 +01:00
Tim Kuipers 321a9c11e0 bugfix: dynamically sized array initialized as if constant (CURA-499) 2015-12-01 15:03:21 +01:00
Tim Kuipers 389950ed90 fix: made all huge classes non-copyable (meshgroup and weaver needed some fixing) (CURA-298) 2015-12-01 14:23:55 +01:00
Tim Kuipers 61e5187267 ducomentation stuff (CURA-414) 2015-12-01 14:04:07 +01:00
Tim Kuipers 3859d58571 indent 2015-12-01 13:41:23 +01:00
Tim Kuipers 345c599062 bugfix: retraction_count_max ignored when < 0, retractions not occuring when ==0 (CURA-414) 2015-12-01 13:39:44 +01:00
Tim Kuipers 73625b949f bugfix: retraction_count_max was retrieved as if it was in MM instead of a count (CURA-414) 2015-12-01 13:22:59 +01:00
Tim Kuipers bf1cf334e6 bugfix: retraction_extrusion_distance_window also in MM for wireprinting (CURA-414) 2015-12-01 13:18:33 +01:00
Tim Kuipers 13ec686608 bugfix: triangles infill (and others) rotated every layer (CURA-363) 2015-12-01 13:14:05 +01:00
Tim Kuipers 742eedda06 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-12-01 12:30:37 +01:00
Tim Kuipers bc6a139d1f lil optimization (CURA-480) 2015-12-01 10:59:13 +01:00
Tim Kuipers 9770e91961 bugfix: first extruder plan might be empty, so then it should be removed (CURA-480) 2015-12-01 10:57:58 +01:00
daid 9e3dfb5b12 Allow running of test with just the default settings. 2015-11-30 15:22:13 +01:00
Ghostkeeper 8b244c1738 Fix order of initialisation
Turns out this was important. When an actually filled mesh was given, this was giving errors.

Contributes to CURA-510.
2015-11-30 14:59:24 +01:00
Tim Kuipers c05c3ddaed bugfix: always use safest distance for combing when a single distance is used for inside combing (CURA-480) 2015-11-27 16:32:41 +01:00
Tim Kuipers 804c288353 storage.getExtrudersUsed (CURA-480) 2015-11-27 16:30:04 +01:00
Ghostkeeper 22c1ff4efe Properly define travel speed in GCodePlannerTests
It has to make travel moves, and if the travel move has speed 0 this messes up the estimates, so make it not 0.
2015-11-27 16:12:41 +01:00
Ghostkeeper 811be991c1 Define some lenience in time material estimates test
They are estimates, after all. The ground truth too is estimated.
2015-11-27 16:12:41 +01:00
Ghostkeeper 684da831b2 Properly set the retraction speed and distance
The test currently fails due to a floating point rounding error. I'll give it some lenience, especially since both the ground truth and the supposed output are estimates.
2015-11-27 16:12:41 +01:00
Ghostkeeper a9ff4a3826 Update naive time estimates retraction test
It now actually performs a retraction. The test fails at the moment. It seems the planner is not properly constructed yet.
2015-11-27 16:12:41 +01:00
Ghostkeeper 6d2ecc7811 Add "-" operator for TimeMaterialEstimates
This allows you to get the estimates of segments of the g-code, such as what I want to do when making unit tests.
2015-11-27 16:12:41 +01:00
Ghostkeeper e77ae7362b Use checked meshgroup in constructing SliceDataStorage
The rest of this constructor was still using the input meshgroup, which might be nullptr. Make it use the field, which was checked for being nullptr and initialised if it was nullptr.
2015-11-27 16:12:41 +01:00
Ghostkeeper 571d592136 Add a check for nullptr when creating SliceDataStorage
For this I moved the constructor properly to the cpp file. This prevents the constructor from having to be compiled a million times, and having to compile a million other files each time you modify this constructor, and prevents having to compile the FffProcessor header each time too. Also, since meshgroup must be defined before initializeRetractionConfigs and initializeSkirtConfigs, they are now in the body of the constructor to properly define the order.
2015-11-27 16:12:41 +01:00
Ghostkeeper 4d1ebb33e6 Add test for naive time estimates
It will set up a very basic g-code planner and let it estimate the time and material it needs. Currently it only tests with empty g-code, but I mean to add a test with retraction once this works. Currently the setup gives a segfault.

Contributes to issue CURA-415.
2015-11-27 16:12:41 +01:00
Tim Kuipers 995f9786a4 bugfix: no more temp commands when using single extrusion and UM2 and no auto temp (CURA-468) 2015-11-27 13:40:50 +01:00
Tim Kuipers 3467593aa2 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-11-27 11:06:09 +01:00
Tim Kuipers ef4b8abaf5 bugfix: speed was always set to speed_layer_0 (CURA-496) 2015-11-27 11:05:59 +01:00
daid f460eeef57 Fix the CPackConfig file for both cross compiling and local compiling of packages. Final filename of the deb file was wrong. 2015-11-27 09:40:10 +01:00
Tim Kuipers 488f82ccbc bugfix: no prime tower sendPolygons called (CURA-326) 2015-11-26 19:42:19 +01:00
Tim Kuipers db3e9ac5e6 bugfix: do make infill and skin between INNER walls (fix of bug introduced in last commit) (CURA-481) 2015-11-26 18:29:52 +01:00
Tim Kuipers 8529924bc4 bugfix: no more infill or skin between OUTER walls (CURA-481) 2015-11-26 13:59:14 +01:00
Tim Kuipers 03ff861904 fix: no more bs check; it is never checked when the first Z position is obtained from the initial currentPosition, which is an approximation of where the nozzle starts 2015-11-25 16:58:29 +01:00
Tim Kuipers b3752a43b4 bugfix: gcodeExport got copiedgit diff solves temp commands missing issue (CURA-298) 2015-11-24 18:27:45 +01:00
Tim Kuipers 7e47664a51 bugfixes: uninitialized extruderMM3perMM used ==> replaced by gcodePath.isTravelPath; speed increased by extruderSpeedFactor instead of travelSpeedFactor (CURA-298) 2015-11-24 17:43:47 +01:00
Tim Kuipers 1b6b5cb843 removed commented out premature gcode writing (before planning) 2015-11-24 17:04:02 +01:00
Tim Kuipers 0d98c6eaef bugfix: remove first extruder plan temp warm up commands (since they are handled by the start gcode already) (CURA-468) 2015-11-24 16:43:06 +01:00
Tim Kuipers 518a528596 bugfix: remove first extruder plan temp commands (since they are handled by the start gcode already) (CURA-468) 2015-11-24 16:05:28 +01:00
Tim Kuipers c9ea663a1b enforce single slicing by commandSocket. 2015-11-20 15:57:51 +01:00
Tim Kuipers 0a7355d25c remainder of refactor split: getExtruderTrain => createExtruderTrain (CURA-465) 2015-11-20 11:34:22 +01:00
Tim Kuipers fc6cad48af CuraEngine settings conversion 2015-11-20 09:00:03 +01:00
Tim Kuipers d4c7abf7ef refactor: commandSocket: object_to_slice ==> mehgroup (CURA-465) 2015-11-19 13:59:18 +01:00
Tim Kuipers c04274401a bugfix: commandSocket: objects inherited from global settings instead of from the extruder train (CURA-465) 2015-11-19 13:57:00 +01:00
Tim Kuipers ee8dd68046 revert of polygon constructor change (667644eee4) (CURA-349) 2015-11-18 11:37:48 +01:00
Tim Kuipers afcacd00a7 bugfix: primeTower memory issues (officially unrelated to CURA-414) 2015-11-18 11:31:55 +01:00
Tim Kuipers a5b31de404 early out when meshgroup is empty (CURA-349) 2015-11-18 11:20:15 +01:00
Tim Kuipers c8f1922966 early out when meshgroup is empty (CURA-349) 2015-11-18 10:29:17 +01:00
Tim Kuipers ac24325715 relaxed tests of LinAlg2D (CURA-359) 2015-11-17 18:03:11 +01:00
Tim Kuipers 4807e13de6 lil (CURA-359) 2015-11-17 18:02:10 +01:00
Tim Kuipers b389b24d9b better cmake lists 2015-11-17 18:00:21 +01:00
Tim Kuipers 121d13b57e removed stray references to gcode instead of gcode_layer
and removed a stray fan command - why was it even there?! it should have been in finalize and/or processNextMeshgroupGcode
CURA-299
2015-11-17 17:08:02 +01:00
Tim Kuipers b1dc67be71 factored out maxObjectHeight from finalize (CURA-405) 2015-11-17 16:51:26 +01:00
Tim Kuipers 2584c7b38c removed move upward before end gcode insertion (CURA-405) 2015-11-17 16:39:26 +01:00
Tim Kuipers ac8b0e149a bugfix: retraction dist used as if it was volume (CURA-415) 2015-11-17 14:25:34 +01:00
Tim Kuipers 03141f505d refactor: extruderSwitchRetraction ==> extruder_switch_retraction_distance; prime_amount ==> prime_volume (CURA-415) 2015-11-17 14:24:51 +01:00
Tim Kuipers e18659669e refactor: retraction_amount ==> retraction_e_amount , to reflect that its unit is either mm or mm^3 (CURA-415) 2015-11-17 13:58:12 +01:00
Tim Kuipers 903f3b11d1 lil: mm3 ==> mm^3 in comments (CURA-415) 2015-11-17 13:55:55 +01:00
Tim Kuipers 51f27fd0ca bugfixes retraction_amount vs retraction_distance (CURA-415) 2015-11-17 13:52:08 +01:00
Tim Kuipers b7d1e53992 lil: doc: units of several speed variables (mm/s) (CURA-415) 2015-11-17 13:45:16 +01:00
Tim Kuipers 461a90a319 refactor: retractionPrimeSpeed ==> extruder_attr[e].last_retraction_prime_speed (CURA-414) 2015-11-17 13:22:35 +01:00
Tim Kuipers b19c6751c5 neglected prime amount in naive time estimate cause it is in a different unit 2015-11-17 13:13:06 +01:00
Tim Kuipers e2479e12b0 reffactor: retraction.amount ==> retraction.distance 2015-11-17 13:11:38 +01:00
Tim Kuipers c67f691237 bugfix naive time estimate (CURA-415) 2015-11-17 12:14:03 +01:00
Tim Kuipers 9c8e32f37b PR style fixes 2015-11-17 10:52:51 +01:00
Tim Kuipers 124b116b0c Merge pull request #266 from markwal/master
Omit start temp if already in starting gcode
2015-11-17 10:43:47 +01:00
Ghostkeeper 0b8fd8209c Initialise variable to shut up compiler warning
This variable is always initialised by LinearAlg2D::getDist2FromLineSegment before it is used. But the compiler can't detect that it is, because it noticed that the variable is not changed in some cases (namely if the pointer is null). So this just shuts up the compiler.
2015-11-16 15:39:00 +01:00
Mark Walker 1534ff0361 Add new settings to control temperature prepend
material_bed_temp_prepend - if true, prepend the bed temperature gcode
material_bed_temp_wait - if true, use the set and wait variant gcode
material_print_temp_prepend - if true, prepend the extruder temperature gcode
material_print_temp_wait - if true, also prepend the wait for temperature gcode
2015-11-16 02:26:02 -08:00
Ghostkeeper b6268d8dc9 Initialise variable to shut up compiler warning
This variable is always initialised by LinearAlg2D::getDist2FromLineSegment before it is used. But the compiler can't detect that it is, because it noticed that the variable is not changed in some cases (namely if the pointer is null). So this just shuts up the compiler.
2015-11-13 16:55:00 +01:00
Ghostkeeper 3413f3abaa Initialise variable in case a polygon is empty
If an empty polygon was passed here in the outline, firstBoundarySegmentEndsInEven would be uninitialised. This initialises it by default to true so that the if statement after the loop exhibits predictably correct behaviour.
2015-11-13 16:50:52 +01:00
Ghostkeeper cb5c69d064 Continue running wireframe script if zero layers to initialise fields
Some fields of Weaver were not properly initialised if the script was cut short. It now checks at several places where zero layers would be a problem and just continues as best as it can.

Contributes to issue CURA-349.
2015-11-13 16:45:04 +01:00
Ghostkeeper afa7c5a0c0 Properly initialise retraction settings in wireframe
The retraction settings were not initialised and this caused wireframe printing to access uninitialised memory. This initialises it properly by loading them from settings. At one place the rest of the settings were still hardcoded with the todo that it should be loaded from settings, but I left that as-is for now.

Contributes to issue CURA-349.
2015-11-13 16:43:05 +01:00
Ghostkeeper 7cbfd42b8f Initialised variable that could be uninitialised in if statement
This variable was possibly uninitialised by the time it is first used in an if statement. This causes undefined behaviour. This now initialises it to false, which is correct behaviour if the variable is not changed (zero outlines).
2015-11-13 13:24:42 +01:00
Ghostkeeper 515bf4592e Set CMake debug version message to status type
Makes it more coherent with other messages in CMake.
2015-11-13 13:12:59 +01:00
Ghostkeeper 667644eee4 Properly initialise Polygons
The polygons vector of the Polygons class was in some cases not initialised while the size of the vector was requested, causing Valgrind to report a conditional jump depending on uninitialised memory.

Contributes to issue CURA-349.
2015-11-13 12:04:06 +01:00
Ghostkeeper 17e00733a4 Make flow temp graph parsing more robust
It should try to get as much information from the string as possible, regardless of whether the string is correctly formatted or not. At many places it used to iterate through the characters indefinitely until the next separator was reached. Now it stops at the end of string and returns the information it gathered up to then.

Contributes to issue CURA-349.
2015-11-13 11:36:24 +01:00
Tim Kuipers 5142f8c1ba Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-11-13 11:12:41 +01:00
Tim Kuipers 432abf11d5 allround print tests (CURA-426) 2015-11-13 11:12:23 +01:00
Arjen Hiemstra 77361417a4 Merge branch '15.10'
* 15.10:
  Add support for setting CuraEngine version from CMake
  Spelling fix
2015-11-13 11:06:20 +01:00
Ghostkeeper 83da6b2072 Fix maximum object height for wireframe if wireframe empty
It can't request the height of the heighest layer if there are no layers. If there are no layers, the height is now 0. Fixes the wireframe printing issue.

Contributes to issue CURA-349.
2015-11-13 10:18:20 +01:00
Ghostkeeper e567f8265a Wireframe don't crash if meshgroup is empty
If the mesh group is empty, the wireframe slicing crashed since it tried to access slicerList[0], which was filled with a slicer for each meshgroup.

Contributes to issue CURA-349.
2015-11-13 10:18:20 +01:00
Tim Kuipers c20b07fe0d lil:indent 2015-11-13 09:56:14 +01:00
Tim Kuipers 10815b4f93 bugfix: extruder start/end position didn't account for the extruder offset (CURA-351) 2015-11-13 09:55:53 +01:00
Arjen Hiemstra a119be9021 Add support for setting CuraEngine version from CMake 2015-11-12 14:53:59 +01:00
Tim Kuipers 002692fe18 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-11-12 13:16:22 +01:00
Tim Kuipers 99e9054f54 renaming: isRetracted==>retraction_amount_current & retraction_offset_from_zero==>retraction_amount_e_start (CURA-414) 2015-11-12 13:15:28 +01:00
Tim Kuipers eedd4adee3 bugfixes: unset prime_amount and wrong currentExtrudedVolume computation (CURA-414) 2015-11-12 13:03:38 +01:00
Tim Kuipers e110b864ce introduced firmware_retract (CURA-414) 2015-11-12 12:02:04 +01:00
Tim Kuipers 1109f97ae6 refactor getCurrentExtrudedVolume to only owrk on current_extruder (CURA-414) 2015-11-12 12:01:38 +01:00
Tim Kuipers 469ab32b5c bugfix: isRetracted was used instead of prime_amount (CURA-414) 2015-11-12 11:17:18 +01:00
Tim Kuipers e1390582ed bugfixes extruded_volume_at_previous_n_retractions (CURA-414) 2015-11-12 10:56:24 +01:00
Tim Kuipers 6b1b11b47b refactored out writeMoveBFB: fixed indentation 2015-11-12 09:29:48 +01:00
Tim Kuipers 3526edd04a refactored out writeMoveBFB 2015-11-12 09:28:39 +01:00
Tim Kuipers a101be9d2f bugfix: register extruded volume before extruder switch (CURA-414) 2015-11-12 09:21:05 +01:00
Tim Kuipers 52aedafc13 bugfixes extrusion_amount_at_previous_n_retractions (CURA-414) 2015-11-12 08:52:25 +01:00
Tim Kuipers afd1e2659e refactor: getExtrusionAmountMM3 ==> getCurrentExtrudedVolume (CURA-414) 2015-11-12 08:51:38 +01:00
Tim Kuipers 93896a6960 refactored out getFilamentArea (CURA-414) 2015-11-11 17:34:54 +01:00
Tim Kuipers 954a396d96 bugfix: retraction extrusion window was interpreted in mm instead of in micron (CURA-414) 2015-11-11 17:31:33 +01:00
Tim Kuipers 610880737f refactor: extrusion_amount ==> current_e_value (no more implicit storing of retracted amount); last_coasted_amount_mm3 + retraction_config.primeAmount ==> extruder_attr[.].prime_amount; (CURA-414) 2015-11-11 16:49:01 +01:00
Tim Kuipers 36ed9904b5 refactor: made retraction prime amount have mm3 unit (CURA-415) 2015-11-11 16:37:32 +01:00
Tim Kuipers 996fc2cbc1 refactor: isRetracted now tracks how far it is actually retracted (CURA-414) 2015-11-11 15:20:16 +01:00
Tim Kuipers 7af12e5526 lil 2015-11-11 14:25:50 +01:00
Tim Kuipers 0323179b4a bugfix?: long retraction 'overwrites' short retraction (CURA-414) instead of doing a long+short retraction 2015-11-11 14:25:35 +01:00
Tim Kuipers 91956ad0e6 bugfix: zhop created Z position gcode commands on every move in a z hop (If I understand correctly) (CURA-414) 2015-11-11 14:23:30 +01:00
Tim Kuipers ff9dbf9a51 refactor: isRetracted per extruder (CURA-414) 2015-11-11 14:22:15 +01:00
Ghostkeeper bbff888e23 Fix license header
I don't know how I keep getting this wrong.
2015-11-11 13:41:16 +01:00
Ghostkeeper 4460b9a1d0 Fix license header
I don't know how I keep getting this wrong.
2015-11-11 13:39:34 +01:00
Ghostkeeper dd161ef770 Rename libCuraEngineLibrary to lib_CuraEngine
It can't be named libCuraEngine, sadly, since that produces a naming conflict with the CuraEngine executable target itself. This is a better compromise than it was initially.
2015-11-11 13:39:34 +01:00
Ghostkeeper 30050349fa Git ignore test output file
This should not be committed along.
2015-11-11 13:39:34 +01:00
Ghostkeeper bafcb804f4 Streamline building with unit tests
It is no longer necessary to build CuraEngine twice if building unit tests. It now simply builds all of CuraEngine (except main.cpp) as a library, then builds main.cpp as separate application and links the CuraEngine library to it. Then it does the same with the tests: Just build the test suite as application and link the already-built library to it.
2015-11-11 13:39:34 +01:00
Tim Kuipers a7320a2d14 bugfix: nozzle switch retraction prime speed was ignored 2015-11-11 12:59:03 +01:00
Tim Kuipers a7e72ea917 bugfix: overriding json document can now add new settings to existing category 2015-11-11 12:18:26 +01:00
Tim Kuipers 61c9cb5a07 debug out functions for settings registry 2015-11-11 12:17:54 +01:00
Tim Kuipers d029d7294f lil 2015-11-11 11:08:49 +01:00
Tim Kuipers 4c2aaed215 merge from 15.10 2015-11-10 13:10:14 +01:00
Ghostkeeper 68602df6ee Spelling fix
This would prevent the g-code flavour from being recognised in RepRap machines.
2015-11-09 13:52:40 +01:00
Tim Kuipers 017fb03391 failsafe: extruder train defaults to global settings if it is not specified 2015-11-06 17:30:37 +01:00
Tim Kuipers 92f5311aaf failsafe: extruder train defaults to global settings if it is not specified 2015-11-06 17:30:15 +01:00
Tim Kuipers 00abb3ab4c lil: removed debug code 2015-11-06 17:23:09 +01:00
Tim Kuipers 4f9f55ab06 lil: removed debug code 2015-11-06 17:22:45 +01:00
Tim Kuipers dfc91d811f merge of bugfix for combing 2015-11-06 17:12:58 +01:00
Tim Kuipers 2bfdb2d612 bugfix: outside combing was skipped sometimes 2015-11-06 17:10:31 +01:00
Tim Kuipers 026f82540f bugfix: outside combing was skipped sometimes 2015-11-06 16:53:33 +01:00
Tim Kuipers 835424121f bugfix: outside combing was skipped sometimes 2015-11-06 15:46:33 +01:00
Tim Kuipers d87072f761 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-06 15:44:51 +01:00
Tim Kuipers f89a747836 bugfix: outside combing was skipped sometimes 2015-11-06 15:44:42 +01:00
Ghostkeeper 767803d2bf Fix more indenting
I think this is on me. I merged this code once.
2015-11-06 11:10:55 +01:00
Ghostkeeper a808e9e8ed Fix indenting
At one place there was an accidental extra four spaces. At another place the alignment of a comment was off since it was aligned to a comment above it which had moved to the left.
2015-11-06 11:10:55 +01:00
Tim Kuipers 087efefc16 bugfix: new speed wasn't set; fixes CURA-396 2015-11-06 10:42:59 +01:00
Tim Kuipers 09afe2264a bugfix: new speed wasn't set; fixes CURA-396 2015-11-06 10:40:49 +01:00
Tim Kuipers 6866925a05 bugfix: gcode flavor prepended for UM2 was incorrect 2015-11-05 21:00:55 +01:00
Tim Kuipers 5ede92fb1b bugfix: gcode flavor prepended for UM2 was incorrect 2015-11-05 21:00:29 +01:00
Ghostkeeper 321ef643a4 Use static cast instead of c-style cast
The static cast has more predictable behaviour across compilers.
2015-11-05 17:53:17 +01:00
Ghostkeeper c43c876cbc Offset PolyCrossings in combing after iteration of basicpath
The offset could sometimes make the basicpath loop get stuck.
2015-11-05 17:53:17 +01:00
Ghostkeeper 3d596f1765 Report error if SVG file couldn't be opened for writing
If for some reason the opening of the file failed, an error is given that indicates where the file was opened. It still continues writing because the nature of this class requires it to fail fast rather than continue silently, but it should be easier to debug now. If silent fail behaviour is desired for this class then we could decide on that later. It involves checking for if(out) at every write function.
2015-11-05 17:53:17 +01:00
Ghostkeeper ff747fa88f SVG filewriter to use const points
This way they can be directly filled in as points into a function call without making a variable for them and passing that variable by reference.
2015-11-05 17:53:17 +01:00
Tim Kuipers 950441493c Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-11-05 17:49:01 +01:00
Tim Kuipers 0c7cca6f17 feature: always show gcode flavor 2015-11-05 17:48:45 +01:00
Tim Kuipers 41a1db1e55 feature: always show gcode flavor 2015-11-05 17:48:21 +01:00
Ghostkeeper c058a2b12b Separate test per assert in getDist2FromLineSegmentAssert
Rather than failing at the first assert, there are now only 2 asserts in these test cases. Also it allows for some leniency of 10 microns error in the distance, which was the actual reason I did this.
2015-11-05 17:34:51 +01:00
Tim Kuipers adc8341e49 bugfix: walls were visualized when wall_line_count==0 2015-11-05 12:06:31 +01:00
Tim Kuipers 0717594a15 bugfix: walls were visualized when wall_line_count==0 2015-11-05 12:05:42 +01:00
Ghostkeeper d6ed95ed81 Merge branch 15.10
Conflicts:
	src/settingRegistry.cpp
	src/skin.cpp
2015-11-04 14:44:12 +01:00
Ghostkeeper c72b05463a Combing path-polygon intersection only offset along path
This is slightly more efficient to compute, though it allows for combing closer to the polygon than the specified combing distance if the edge approaches parallel with the desired combing path.

Contributes to issue CURA-286.
2015-11-04 14:04:50 +01:00
Ghostkeeper eb1acd02a2 Fixed combing offset bug
A point was being moved inside the polygon to obtain waypoints for combing through the inside of the polygon. Then it was being moved outside the polygon with a larger offset, but the second move used the inside point of the first move, causing the resulting offset to be a bit too small. This keeps a copy of the original waypoint before moving it inside, which it uses only for computing the outside waypoint.

Contributes to issue CURA-286.
2015-11-04 14:04:50 +01:00
Tim Kuipers 102b9f9c75 setting inheritance diagram 2015-11-04 13:25:51 +01:00
Tim Kuipers abb0b9a763 refactor: extruder plan insert Inserts code moved to ExtruderPlan 2015-11-04 12:39:00 +01:00
Tim Kuipers 63eda27a50 no more error when no extruder trains are present 2015-11-04 11:17:25 +01:00
Tim Kuipers 5b1eeecf76 fix for support roof triangles infill 2015-11-04 11:16:50 +01:00
Tim Kuipers 41ede0727b Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-04 11:02:32 +01:00
Ghostkeeper 7ffec2e617 Remove unnecessary parallel line collision detection
Now that a collision is also triggered when the line crosses an endpoint of another line, the detection of collisions when the two lines are parallel is no longer needed.

Contributes to issue CURA-286.
2015-11-03 17:40:46 +01:00
Ghostkeeper 57ebcf0163 combineInfillLayers to stop if combining 1 or 0 layers
Combining 1 layer has no effect. Combining 0 layers is invalid. Therefore, if this function is made to combine 1 or 0 layers, it doesn't combine anything, and immediately returns.
2015-11-03 16:48:23 +01:00
Tim Kuipers 71ae42a949 lil: error=>warning 2015-11-03 16:45:07 +01:00
Tim Kuipers 49a1ed7f3b fix for support roof triangles infill 2015-11-03 16:20:56 +01:00
Tim Kuipers 190b5b789b no more error when no extruder trains are present 2015-11-03 15:17:58 +01:00
Tim Kuipers ad29876617 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-03 09:17:42 +01:00
Tim Kuipers 07481a548c bugfix: settings overrides weren't working
Conflicts:
	src/settingRegistry.cpp
2015-11-03 09:17:32 +01:00
Ghostkeeper a6be90d3b4 Link libraries to CuraEngineLibrary rather than tests
It's CuraEngineLibrary that uses these libraries, not the tests themselves. It's better to link it to CuraEngineLibrary itself rather than to the test executables.
2015-11-02 16:56:41 +01:00
Ghostkeeper 3231d75d94 Use static cast instead of c-style cast
The behaviour of this cast is better defined.

Contributes to issue CURA-285.
2015-11-02 16:48:53 +01:00
daid 5e4080e0bb Add clipper and Arcus libraries to link targets in an attempt to fix the build. 2015-11-02 16:17:16 +01:00
Ghostkeeper 6432215ad9 Merge branch '15.10' of http://github.com/Ultimaker/CuraEngine
Conflicts:
	CMakeLists.txt
	src/FffGcodeWriter.cpp
	src/FffPolygonGenerator.cpp
	src/comb.cpp
	src/gcodeExport.cpp
	src/gcodeExport.h
	src/gcodePlanner.cpp
	src/gcodePlanner.h
	src/skin.cpp
	src/utils/SVG.h
2015-11-02 16:00:52 +01:00
Ghostkeeper 3a60d0cb01 Merge branch '15.10' of http://github.com/Ultimaker/CuraEngine
Conflicts:
	CMakeLists.txt
	src/FffGcodeWriter.cpp
	src/FffPolygonGenerator.cpp
	src/comb.cpp
	src/gcodeExport.cpp
	src/gcodeExport.h
	src/gcodePlanner.cpp
	src/gcodePlanner.h
	src/skin.cpp
	src/utils/SVG.h
2015-11-02 16:00:06 +01:00
Ghostkeeper 7d70768178 Tell CppUnit to output XML
The standard XmlOutputter seems to output JUnit-style XML.
2015-11-02 15:08:28 +01:00
Ghostkeeper 9c201e3396 Remove old try for return code
Directly returning the boolean didn't work.
2015-11-02 14:50:57 +01:00
Ghostkeeper f00c091ed1 Add actual tests for getDist2FromLineSegmentTest
It tests various types of line segments with points in various places. The tests are currently already failing...
2015-11-02 14:37:03 +01:00
Ghostkeeper 4574e0917e getDist2FromLineSegment pass by const reference
This allows for calling the function with constexpr types.
2015-11-02 14:32:57 +01:00
Ghostkeeper 4ae1f5e6bf Succeeded CppUnit integration
If you add -DBUILD_TESTS=true to CMake, it will add a test target for each test file. These can be executed separately as executable file, or they can be ran all at once with 'make test'.
2015-11-02 13:17:11 +01:00
Ghostkeeper ae02a91a5d Fix license
Yeah I've done that more often... Sorry.
2015-11-02 09:26:13 +01:00
Ghostkeeper 0781a8f4ef Initial setup of CppUnit in CMake
I've setup a basic test but it doesn't actually do anything.
2015-10-30 17:41:48 +01:00
Ghostkeeper f1902d88bf Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-30 17:08:45 +01:00
Ghostkeeper 4cc5c35564 Fix string casting
This script couldn't run because it couldn't convert the ints to strings. It shouldn't convert the ints to strings. It should convert the strings to ints (or floats).
2015-10-30 17:08:18 +01:00
Tim Kuipers f124a7e528 bugfix: settings overrides weren't working 2015-10-30 16:05:24 +01:00
Tim Kuipers 1cd8e1c500 refactor: moved LayerPlanBuffer implementation to cpp file 2015-10-30 15:31:24 +01:00
Tim Kuipers 6dc89bbe7f Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-30 13:37:54 +01:00
Tim Kuipers 16250c54c9 large refactor/bugfix: moved GcodePathConfig initialization to start of FffGcodeWriter.writeGcode(.) and moved setting of layer-dependent config settings inside GcodePlanner.writeGcode(.) 2015-10-30 13:32:49 +01:00
Ghostkeeper c6e201e307 Don't combine infill layers if infill is empty
If the infill density is 0 and it has to combine layers, it would try to access layers below the first layer that don't exist. Instead it will now not combine at all, saving some processing power too.

Contributes to issue Cura-285.
2015-10-30 12:34:31 +01:00
Ghostkeeper c5be07212e Use standard namespace for isinf
This one is in the standard namespace. Otherwise it won't compile on stricter compilers. Contributes to #2.
2015-10-30 11:42:04 +01:00
Tim Kuipers 7035e91ac6 refactor: split raft_config into raft_base_config, raft_interface_config, raft_surface_config 2015-10-29 15:45:14 +01:00
Tim Kuipers 4f972da7ae moved layer_height from GcodePathconfig to GcodePlanner 2015-10-29 15:29:17 +01:00
Ghostkeeper 47718a1ba4 Merge branch 'master' of http://github.com/Ultimaker/CuraEngine 2015-10-29 15:02:31 +01:00
Ghostkeeper 87db7232a1 Added a test case that would generate support
It requires different kinds of support: Support to buildplate, support not to buildplate, supporting edges and supporting faces.
2015-10-29 14:58:33 +01:00
daid b9f9c12d6c Merge branch 'master' of github.com:Ultimaker/CuraEngine 2015-10-29 14:40:16 +01:00
daid 046fa3ba8e Few changes after review by Ruben. Also adds some documentation, but not everything yet. 2015-10-29 14:40:05 +01:00
Tim Kuipers db039033ce lil raft fan speed bug 2015-10-29 11:47:32 +01:00
Tim Kuipers c0fa1b4a1a lil bugfix: no more redundant raft layer comments 2015-10-29 11:41:15 +01:00
Tim Kuipers 1d073600e4 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-10-29 11:36:59 +01:00
Tim Kuipers 8f6cb82421 merge of raft bugfixes 2015-10-29 11:29:43 +01:00
Tim Kuipers a455913165 lil 2015-10-28 17:03:19 +01:00
Tim Kuipers 9b0e4be570 bugfixes print z when raft is present 2015-10-28 17:00:25 +01:00
Ghostkeeper 1023102e3a Log an error when layer height is not positive
In addition to returning false, sliceModel now also logs an error when the layer height is invalid.
2015-10-28 16:38:27 +01:00
Ghostkeeper 0ac334c849 Log an error when slicing failed
In the command socket, when slicing failed, it would just ignore the failure and continue. This is fine, but let's at least log an error when that happens.
2015-10-28 16:34:59 +01:00
Ghostkeeper 696e0132f4 Break when layer height is 0
After discussing this with several colleagues, this seems to be the desired behaviour.
2015-10-28 16:19:06 +01:00
Tim Kuipers f9a0e39a6a Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-28 15:22:08 +01:00
Tim Kuipers ff9e7f11ae optimization:infill layers arent combined when combine count=1 2015-10-28 15:21:44 +01:00
Ghostkeeper fb284397c2 Only assert for insane g-code values in debug mode
A preprocessor definition is added if CMAKE_BUILD_TYPE == DEBUG. So for release builds, set CMAKE_BUILD_TYPE properly to RELEASE and CuraEngine will no longer stop at the assertion.
2015-10-28 15:17:33 +01:00
daid f5e2c5a070 Handle the new string type setting in the test script. (for which I am wondering if this was introduced with agreement in the team...) 2015-10-28 13:47:15 +01:00
Ghostkeeper 85cfd422ba Remove infill_sparse_combine setting
In discussion, we deemed infill_sparse_thickness more clear to the user and infill_sparse_combine superfluous, since it is really an intermediary value used by the engine to round the thickness up. So now it is actually computed in the engine. Accompanying this commit will be a commit to Cura to remove the setting from the .json file.
2015-10-28 13:10:16 +01:00
Ghostkeeper c5334e355e Re-do minimum combine_infill_layers check
This was accidentally removed in the merge...
2015-10-28 12:49:39 +01:00
Tim Kuipers 086c38ac0b Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-28 12:45:37 +01:00
Tim Kuipers 4cb64bab67 Merge conflict resolve of feature_temp_compensation_gcodePlanner 2015-10-28 12:42:57 +01:00
daid 87eaa97f21 Return used commands on failure. 2015-10-28 10:47:22 +01:00
daid 59fc004f09 Did not report failure count from default test, causing the rest of the tests not to run. 2015-10-28 09:44:49 +01:00
daid 5dbfab6ffb Small fix in test script timestamp generator. 2015-10-28 09:36:19 +01:00
daid 5494ce12a5 Update test script to include the base name of the model filename, not the full path. And reduce the amount of random tests when previous tests are failing. 2015-10-28 09:35:13 +01:00
Tim Kuipers 1ed1de147a refactor: renamed heatup time settings: inverted unit: s/*C => *C/s 2015-10-27 16:15:18 +01:00
Tim Kuipers 1c0a616ec7 refactor: flow_temp_graph => material_flow_temp_graph 2015-10-27 15:36:49 +01:00
Tim Kuipers 82a4bff953 use material_flow_dependent_temperature to turn off flow based temp 2015-10-27 15:24:58 +01:00
Tim Kuipers f7bbda7599 new temp settings 2015-10-27 14:10:56 +01:00
Tim Kuipers 273f1f46f3 LayerPLanBuffer documentation 2015-10-27 13:31:45 +01:00
Tim Kuipers e81abe405d lil vector.reserve optimization 2015-10-27 12:29:08 +01:00
daid d0dd97ab04 Handle engine timeouts. 2015-10-27 12:27:39 +01:00
Tim Kuipers d0e6f5c07e Preheat documentation 2015-10-27 12:16:23 +01:00
Tim Kuipers f4eb33e14e made heatup and cool down temps depend on whether we are printing 2015-10-27 12:15:52 +01:00
daid 3d3ddc238f Forgot to actually supply the setting to the engine. 2015-10-27 12:15:44 +01:00
daid 704edd25af Merge branch 'master' of github.com:Ultimaker/CuraEngine 2015-10-27 12:09:53 +01:00
daid e33560beb6 Update test script to use the json file to run tests on settings. 2015-10-27 12:09:40 +01:00
Nicanor Romero Venier 71e486d007 Accepts 'help' as a known command 2015-10-27 10:25:17 +01:00
Tim Kuipers 4ca66b6916 Merge pull request #270 from nicanor-romero/master
Accepts 'help' as a known command
2015-10-27 10:23:28 +01:00
Nicanor Romero Venier 7e6c4661b9 Accepts 'help' as a known command 2015-10-27 09:49:12 +01:00
daid f96eaddf6c Removed code convention file, is moved to Ultimaker/meta as general code convention file for all projects. 2015-10-27 09:27:27 +01:00
Ghostkeeper eb2ae0d699 Correctly handle case of no layers
If there are no layers, the engine used to crash with an assertion error. Instead, it now returns an empty slice, which is correct behaviour. Fixes issue CURA-323.
2015-10-26 12:11:27 +01:00
Ghostkeeper bcee6c7cc2 No negative initial layer thickness
If this setting is zero or negative, make it the minimum layer thickness: 1 micron. Also fixed a mistake in the comment of the similar check for the normal layer thickness setting.
2015-10-26 11:52:32 +01:00
Ghostkeeper 3a28f5d547 No negative layer thickness
If this setting is zero or negative, make it the minimum layer thickness: 1 micron.
2015-10-26 11:47:53 +01:00
Tim Kuipers 5c41425859 lil rename 2015-10-23 17:12:07 +02:00
Tim Kuipers 85959b0aa7 handle tmep inserts during MergeInfillLines 2015-10-23 16:34:02 +02:00
Tim Kuipers 6bd1d9fe8c bugfix: MergeInfillLines copied itself 2015-10-23 15:50:34 +02:00
Tim Kuipers 3e43df9e60 forceMinimalLayerTime: lil optimization 2015-10-23 12:41:21 +02:00
Ghostkeeper 4b11b5946e Remove infill_sparse_combine setting
In discussion, we deemed infill_sparse_thickness more clear to the user and infill_sparse_combine superfluous, since it is really an intermediary value used by the engine to round the thickness up. So now it is actually computed in the engine. Accompanying this commit will be a commit to Cura to remove the setting from the .json file.
2015-10-23 12:14:06 +02:00
Ghostkeeper d558e24b43 Re-do minimum combine_infill_layers check
This was accidentally removed in the merge...
2015-10-23 11:43:51 +02:00
Ghostkeeper 5d6f45a71d Merge branch 'master' of http://github.com/Ultimaker/CuraEngine
Changed files:
 * src/skin.cpp
2015-10-23 11:41:29 +02:00
Ghostkeeper d827cc0f92 Never combine 0 layers
Infill_combine_layers of 0 is an invalid value. So perform this sanity check to make sure that infill_combine_layers is always at least 1.
2015-10-23 11:25:06 +02:00
Tim Kuipers faa580ffe6 refactored TimeMaterialEstimates.travel_time into retracted_travel_time and unretracted 2015-10-22 17:50:10 +02:00
Tim Kuipers 16679d68d0 unindent refactor 2015-10-22 16:44:27 +02:00
Tim Kuipers 9983ebac7b bugfix: layer_plan.processFanSpeedAndMinimalLayerTime called twice on the same layer 2015-10-22 16:42:48 +02:00
Tim Kuipers ee8ef2bfa5 variable buffer size; better idle temp handing; refactored insertPreheatCommand into single and multiExtrusion; more... 2015-10-22 16:30:15 +02:00
Tim Kuipers 7646426f9d LayerPlanBuffer.cpp to move function bodies to 2015-10-22 16:26:45 +02:00
Ghostkeeper 263eb370e9 Handle edge case of parallel line intersection
If a line was parallel with an edge of the polygon, the line should be marked as intersecting that edge to properly register the scanline crossing. This handles that edge case for most cases by adding one intersection. In some cases it is plausible that two intersections should be registered. For those cases this procedure is still wrong, since it is computationally expensive to detect them.
2015-10-22 13:34:13 +02:00
Ghostkeeper c0d22dcf7b Move middle_from and middle_to inside as well
When combing needs additional waypoints (middle_from and middle_to), these waypoints weren't offset away from the edge towards the inside of the mesh. This caused all sorts of mistakes with edge cases. This prevents these edge cases.
2015-10-22 13:29:57 +02:00
Ghostkeeper 01d0a3b8d6 Add variant of writeAreas for point vector
Rather than having to supply a polygon, you can now supply a vector of points too. This helps if you want to visualise a custom polygon. Rather than having to clone a polygon some way, clear it and fill it with your own points, you now don't have to create a polygon container at all.
2015-10-22 11:29:59 +02:00
Ghostkeeper 1dd7313332 Fix moveInside corner cases
When the specified point is closest to a vertex of the polygon rather than an edge, the computed inward direction was wrong. I think this was a typo.
2015-10-22 11:15:59 +02:00
Ghostkeeper 7a7d1ebd77 Edge cases in moveInside
If we're moving points inside, points on the edge also have their direction of motion altered. This effectively means that the corner case happens more often.
2015-10-22 11:11:20 +02:00
Tim Kuipers 2976b1276c refactor: Insert ==> NozzleTempInsert 2015-10-22 11:01:19 +02:00
Tim Kuipers b84f41d9b0 bugfixes: waited for temp on wrong places; inserted preheat commands for each layer instead of jsut the last 2015-10-21 18:05:59 +02:00
Tim Kuipers f142df9176 bugfix: use correct extruder for which to preheat; included cool down inserts 2015-10-21 17:42:23 +02:00
Tim Kuipers 94f90419dd Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-21 17:00:13 +02:00
Tim Kuipers 90547bb69c bugfix: dual prime tower was broken (gave SEGFAULT on acces of config)
Conflicts:
	src/FffGcodeWriter.cpp
2015-10-21 16:59:59 +02:00
Tim Kuipers 3611d53222 bugfix: dual prime tower was broken (gave SEGFAULT on acces of config) 2015-10-21 16:56:00 +02:00
Tim Kuipers ab7ca73596 bugfixes for dual extrusion in LayerPlanBuffer 2015-10-21 16:35:18 +02:00
Tim Kuipers c42568c059 first working preheat inserts in gcode 2015-10-21 15:55:17 +02:00
Tim Kuipers 43eb784b6c cleanup old LayerPLanBuffer code and debug output 2015-10-21 13:35:45 +02:00
Tim Kuipers 68f1eb1fd2 better temp update starting time 2015-10-21 13:31:58 +02:00
Ghostkeeper 3dfb84b9a1 Added include(Point) for 2D bounding boxes too
This functionality already existed for 3D axis aligned bounding boxes. It makes it easier to construct a bounding box when no explicit polygon is available. To facilitate this, the bounding box is properly constructed with high minimum value too.
2015-10-21 12:28:03 +02:00
Ghostkeeper 80896e3f4d Fix order of parameter initialisation
This caused a compiler warning.
2015-10-21 12:16:49 +02:00
Tim Kuipers 6518e14e18 lil code convention braces 2015-10-21 12:05:47 +02:00
Tim Kuipers 4c95e43048 refactor: extrudeSpeedFactor (lil) made more verbose 2015-10-21 12:03:56 +02:00
Tim Kuipers 4c66b7fd01 lil 2015-10-21 11:46:50 +02:00
daid 97ebfe8b12 Remove RPM build. 2015-10-21 10:13:33 +02:00
Tim Kuipers 0cb8b64f70 rewrote LayerPlanBuffer.insertPreheatCommands 2015-10-20 18:12:27 +02:00
Tim Kuipers bfc2d458ae refactor: renamed TempFlowGraph to FlowTempGraph 2015-10-20 13:51:50 +02:00
Tim Kuipers 365de0578c refactor: renamed TempFlowGraph to FlowTempGraph 2015-10-20 13:50:09 +02:00
Tim Kuipers d9c30de796 lil 2015-10-20 13:46:50 +02:00
Ghostkeeper e8cb1028e4 Add check against divide by zero
If two consecutive points are exactly equal, a divide by zero would happen here. This check prevents the divide in that case and skips the second of the points then.
2015-10-20 13:34:32 +02:00
Tim Kuipers bc41b415c2 LayerPlanBuffer: insert temp commands at flush 2015-10-20 13:32:32 +02:00
Ghostkeeper d4adbb97bf Merge master of http://github.com/Ultimaker/CuraEngine
Changed files:
 * src/comb.cpp
 * src/comb.h
2015-10-19 17:06:09 +02:00
Ghostkeeper c8488f6d37 Add writeDashedLine
It draws a dashed line on the canvas. This helps if there are multiple lines on top of each other.
2015-10-19 16:45:16 +02:00
Ghostkeeper 7524df2868 Add yellow colour to SVG
I needed an extra colour for debugging. Why not yellow?
2015-10-19 16:43:55 +02:00
Ghostkeeper 28ac5dc2c9 Switch X to be the horizontal dimension
This was the wrong way around in everything except my writeLines function. X is commonly the horizontal dimension (even in the SVG language itself).
2015-10-19 13:58:25 +02:00
Ghostkeeper f5db2ee8b4 Merge master to 15.10
I need the new writeLines in SVG debugging. I know it's not really a bugfix, but SVG is not used in actual releases, so it would not break anything.
2015-10-19 11:46:09 +02:00
Tim Kuipers 8e736f32aa LayerBuffer bugfixes 2015-10-16 16:11:39 +02:00
Tim Kuipers 9b4d93e236 getSettingAsFlowTempGraph 2015-10-16 15:38:33 +02:00
Ghostkeeper 2f4316e755 Fix infill if too few layers
I just introduced a bug where if there were too few layers such that no infill is generated at all, the infill combining would take a practically infinite amount of time. The cause was an underflow. This fixes it.
2015-10-16 13:55:56 +02:00
Ghostkeeper 52e5b33e7e Remove debug includes
These two includes were used to debug the combing, but they shouldn't have been committed.
2015-10-16 13:45:38 +02:00
Ghostkeeper 01c124774c Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-16 13:35:43 +02:00
Ghostkeeper b4afc8bb4f Narrow range of combining infill layers
Infill layers only need to be combined between <bottom_layers> and <N - 1 - top_layers>. This theoretically makes the code a bit faster. Also, the infill is now combined from 0 upwards rather than in reverse order, and renamed layer_index to layer_idx.
2015-10-16 13:30:46 +02:00
Tim Kuipers d4e2f774db parse array settings 2015-10-16 13:28:33 +02:00
Tim Kuipers eb388c7d80 parse json arrays 2015-10-16 13:28:26 +02:00
Tim Kuipers ab5b7f432e unindeted wrongly indented function 2015-10-16 13:28:17 +02:00
Tim Kuipers e0a09dbe7c parse array settings 2015-10-16 13:20:32 +02:00
Tim Kuipers d83863ce4c parse json arrays 2015-10-16 13:20:17 +02:00
Tim Kuipers fe8def059e unindeted wrongly indented function 2015-10-16 12:09:20 +02:00
Ghostkeeper b91abb4c22 Don't process nonexistent file
If the file you're trying to load doesn't exist, don't add an empty mesh to the mesh group, and don't try to slice that mesh.
2015-10-16 10:40:33 +02:00
Tim Kuipers 4179e46a9d insert preheat command into extruderplan and compute temp from graph 2015-10-16 10:28:24 +02:00
Tim Kuipers 692e875256 more layerPlanBuffer refactor and computation of where to insert the preheat command 2015-10-15 18:58:50 +02:00
Tim Kuipers 9d513291ba used iterator instead of complicated nested for loop bs 2015-10-15 18:41:21 +02:00
Tim Kuipers 5b0370864b syntax mistake fixes 2015-10-15 18:34:21 +02:00
Tim Kuipers 4dcb8563f5 more LayerBuffer crap 2015-10-15 18:32:10 +02:00
Tim Kuipers 21b6be76fe introduced buffer.iterator 2015-10-15 18:28:35 +02:00
Tim Kuipers f06fa4af1a special case for when the first extruder plan of a layer is with the same extruder as the last extruder plan of the previous layer 2015-10-15 18:05:53 +02:00
Tim Kuipers 951eac4c08 config per extruder 2015-10-15 18:05:02 +02:00
Tim Kuipers e4e2dc172e flow-temp graph class 2015-10-15 17:41:22 +02:00
Tim Kuipers 70b0800f2e insertPreheatCommands first approximation 2015-10-15 17:19:32 +02:00
Tim Kuipers aaaf2a6c92 Preheat file first approximation 2015-10-15 17:18:58 +02:00
Ghostkeeper bde136e7ed Fix failed merge
I picked the wrong one to resolve the conflict.
2015-10-15 16:53:42 +02:00
Ghostkeeper 75859623d7 Remove duplicate points from comb path
This sometimes produced zero-length moves at the beginning and end of combing paths.
2015-10-15 16:37:34 +02:00
Ghostkeeper 388a65ef11 Fix combing optimisation intersection bug
The intersections of the line and the polygons was not offset with the extra offset distance to prevent edge cases. This concerns issue #259.
2015-10-15 16:37:34 +02:00
Ghostkeeper 1168c2b120 Document edge cases in line-polygon intersection
It is documented that basically all edge cases trigger a collision.
2015-10-15 16:37:34 +02:00
Ghostkeeper dfd31844ba Handle edge cases for line-polygon collision properly
If a line doesn't intersect but does touch a polygon, a collision is now triggered. This is the desired behaviour in this case, since this method is only used by the combing code.
2015-10-15 16:37:34 +02:00
Ghostkeeper f457f2590c Add writeLines
This draws a polyline on the canvas, which is a sequence of connected line segments. This helps visualising paths.
2015-10-15 16:37:33 +02:00
Ghostkeeper fb9bcccbbe Fix writeLine to transform the points
The transform scales the line to the proper scale, to match the scale of the viewport.
2015-10-15 16:37:33 +02:00
Ghostkeeper cfe216ae44 Merge master onto a41a1e5
Modified files:
    src/gcodeExport.cpp
2015-10-15 16:36:21 +02:00
Tim Kuipers a41a1e556a merge 2015-10-15 11:56:01 +02:00
Tim Kuipers 8081237298 merge 2015-10-15 11:54:33 +02:00
Tim Kuipers 1a7137a6c8 bugfix: make all varargs functions use rvalues - fixes the varargs functions making copies of the objects which are passed by reference 2015-10-15 11:52:22 +02:00
Tim Kuipers 4c967ec6a3 buggy layer buffer used 2015-10-15 09:22:55 +02:00
Tim Kuipers b455661a75 moved processFanSpeedAndMinimalLayerTime outside of gcodePlanner.writeGcode 2015-10-14 14:17:17 +02:00
Tim Kuipers 46b7d1ccab keep time and material info at each GCodePath and each ExtruderPlan 2015-10-14 13:54:29 +02:00
Tim Kuipers fb95022776 assert fix 2015-10-14 11:45:44 +02:00
Tim Kuipers 62ce0b5513 changed starting position to 0,0,20mm 2015-10-14 11:35:15 +02:00
Tim Kuipers 12c25b2214 Revert "added no_point instead of (0,0)"
This reverts commit f49803b8a9.
2015-10-14 11:33:33 +02:00
Tim Kuipers 61915fc322 bugfix: raft is now uses combing, instead of retracting everywhere 2015-10-14 10:57:01 +02:00
Tim Kuipers aa1998dbd8 halfway bugfix: combing during raft 2015-10-14 10:56:36 +02:00
Hajo Nils Krabbenhöft 859a106ec2 rework getLayerSecondWalls() to make sure it doesn't completely remove isles with less than 0.8mm radius (= offset_from_outlines = 2*nozzle_size) 2015-10-14 10:54:34 +02:00
Tim Kuipers ff38534644 bugfix: raftmargin was ignored 2015-10-14 10:45:56 +02:00
Tim Kuipers 2eb81d216f manual merge for raft z 2015-10-14 10:45:31 +02:00
Ghostkeeper 2785659fe6 Move layer loop into combineInfillLayers
The combineInfillLayers function now loops over all layers and combines them. The function therefore no longer needs to be called for every layer.
2015-10-14 10:07:31 +02:00
Ghostkeeper 4bc86ad192 Compute combined infill layers only on modulo-0 layers
For instance, if the sparse_infill_combine setting is 3, it will only be computed for layers 0, 3, 6, etc. This prevents different parts of infill from landing on different layers, causing infill to break into multiple layers.
2015-10-14 10:07:22 +02:00
Tim Kuipers a7b5e05505 bugfix: support roofs sendPolygons now of type SupportType 2015-10-14 10:03:49 +02:00
Tim Kuipers 0fd130cfd3 bugfix: support roof config and support roof sendPolygons(.) 2015-10-14 10:03:33 +02:00
Tim Kuipers ccae7d611e bugfix: timeEstimate recalculate trapezoids failed 2015-10-14 10:02:45 +02:00
Arjen Hiemstra 5dcce74efe Increment layer count for each object that should be printed as a single object
This makes the ordering for one-at-a-time mode more clear

CURA-222 #Start-Review
2015-10-13 17:54:25 +02:00
Tim Kuipers 988af0f6ed (buggy dual extr) refactor: gcodePlanner now consists of ExtruderPlan s which consist of Path s 2015-10-13 15:39:30 +02:00
Tim Kuipers 53714319b4 more work on buffer.insertPreheatCommands 2015-10-13 13:57:28 +02:00
Tim Kuipers fe44537924 Merge branch 'master' into feature_temp_compensation_gcodePlanner 2015-10-13 13:14:31 +02:00
Tim Kuipers a285b22804 reintroduced initial layer height when raft is present 2015-10-13 12:33:03 +02:00
Tim Kuipers e588fe100b Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-10-13 12:01:26 +02:00
Tim Kuipers ad81cc8a12 bugfix: raft is now uses combing, instead of retracting everywhere 2015-10-13 12:01:16 +02:00
Tim Kuipers a3db17442c halfway bugfix: combing during raft 2015-10-13 10:49:19 +02:00
Tim Kuipers e0cf567bf7 bugfix: raftmargin was ignored 2015-10-13 09:55:07 +02:00
Tim Kuipers 904ad24eec bugfix: raft z was computed as if it was a normal layer 2015-10-13 09:47:06 +02:00
Tim Kuipers f49803b8a9 added no_point instead of (0,0) 2015-10-09 17:22:45 +02:00
Tim Kuipers 2b422ce6c0 debug output command socket transmitted data as STL 2015-10-09 15:33:56 +02:00
Ghostkeeper b2cc46a4cb Move layer loop into combineInfillLayers
The combineInfillLayers function now loops over all layers and combines them. The function therefore no longer needs to be called for every layer.
2015-10-09 13:45:39 +02:00
Ghostkeeper 98d105d73c Compute combined infill layers only on modulo-0 layers
For instance, if the sparse_infill_combine setting is 3, it will only be computed for layers 0, 3, 6, etc. This prevents different parts of infill from landing on different layers, causing infill to break into multiple layers.
2015-10-09 10:45:39 +02:00
Tim Kuipers ecd32700f5 lil bugfixes 2015-10-08 16:01:57 +02:00
Ghostkeeper 44c77d2a31 Add Netbeans project file to gitignore
Also categorised the ignore file. Only the "nbproject/*" is new.
2015-10-08 15:35:35 +02:00
Hajo Nils Krabbenhöft f78c3ed4da thicker lines for retraction moves 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 79b16e406a GCodePlanner will initialize command socket on GCodeExport, which will then send combing and retraction paths to GUI 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 8219f32d96 also add new poly types to cpp backend 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 05cfe62927 add new types to protobuf 2015-10-08 15:30:07 +02:00
Ghostkeeper 38e832c172 Move documentation to header file
This documentation should be in the header, as per the code style conventions and for Doxygen.
2015-10-08 15:12:51 +02:00
Tim Kuipers 64ef6e5698 first approximation of the layer plan buffer 2015-10-08 14:49:33 +02:00
Tim Kuipers 769dc252df coding conventions: Pointers vs. references 2015-10-08 11:22:50 +02:00
Tim Kuipers 7093231707 auto-merge fixes 2015-10-07 16:32:25 +02:00
Tim Kuipers ad93a8adde cleanup: no more use of gcode.startPosition; moved gcode.setZ(.) from FffGcodeWriter to gcodePlanner.writeGcode(.) 2015-10-07 16:24:39 +02:00
Tim Kuipers 1bb8bde1de TODO warning; is this code very unsafe maybe? 2015-10-07 15:52:06 +02:00
Tim Kuipers d6822cdaf8 bugfix: processFanSpeedAndMinmalLayerTime was commented 2015-10-07 14:59:25 +02:00
Tim Kuipers 2817528c51 undoing gcodePlanner buffer edits from the commits from the feature_temp_compensation_gcodePlanner branch 2015-10-07 14:58:33 +02:00
Tim Kuipers a52f101c14 fully purged gcodePlanner from references to gcodeExport: moved current position and current extruder to FffGcodeWriter 2015-10-07 14:51:07 +02:00
Tim Kuipers 22b1d427ec forgot to add a header file in last commit 2015-10-07 14:49:33 +02:00
Tim Kuipers b89733320d purged gcodePlanner from references to gcodeExport: moved processFanSpeedAndMinimalLayerTime and factored out lastPosition=gcode.gettPositionXY(); introduced very basic gcodePlanner buffering 2015-10-07 14:09:44 +02:00
Tim Kuipers f662844d78 bugfix: support roofs sendPolygons now of type SupportType 2015-10-07 10:23:37 +02:00
Tim Kuipers 38a4e45771 bugfix: support roof config and support roof sendPolygons(.) 2015-10-06 15:16:43 +02:00
Tim Kuipers dc87dda346 Merge pull request #255 from nicanor-romero/15.10
Explicitly write a \n when finding a new line in a comment
2015-10-06 14:17:52 +02:00
Nicanor Romero Venier f249796f3d Explicitly write a \n when finding a new line in a comment
This way the start_gcode and end_gcode that includes '\n's will not
have their commands executed since they now appear as commented out.
2015-10-06 13:05:00 +02:00
Tim Kuipers 9602616a6b bugfix: timeEstimate recalculate trapezoids failed 2015-10-02 17:01:32 +02:00
Tim Kuipers 0823f8faf5 syntax fixes of last merge 2015-10-01 15:46:17 +02:00
Tim Kuipers 33d63e8359 Merge branch '15.10'
Conflicts:
	src/FffGcodeWriter.cpp
	src/gcodePlanner.cpp
2015-10-01 15:44:20 +02:00
Tim Kuipers 3a8a5844d4 moved file 'new settings master' 2015-10-01 15:08:55 +02:00
Tim Kuipers 7b5bb1e367 write 'end of gcode' comment at end of gcode 2015-10-01 11:18:00 +02:00
Tim Kuipers 017dc5be4c bugfix: profile string caused bugs on UMO and when USB printing 2015-10-01 11:14:18 +02:00
Tim Kuipers 60c9e83da9 bugfix: double skin areas 2015-09-30 16:18:26 +02:00
Tim Kuipers 51927938b0 bugfix: visualization used support areas instead of lines 2015-09-30 12:31:03 +02:00
Tim Kuipers d4b9f7c0bd Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-30 09:43:58 +02:00
Tim Kuipers 0b78099587 bugfix: coasting speeds were 100 times too much (%) 2015-09-30 09:43:46 +02:00
Arjen Hiemstra a61d5385ea Use size_t instead of "long unsigned int" for storing vector sizes
std::vector<>::size() returns a size_t. size_t is a long uint on 64-bit
but a normal uint on 32-bit. So compilation on 32-bit fails because
std::max cannot do template deduction correctly.
2015-09-29 17:52:02 +02:00
Tim Kuipers 24c836f87a Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-29 16:14:50 +02:00
Tim Kuipers 8a2ca62305 bugfix: retractions on any move smaller than the retraction_min_travel when combing was disabled 2015-09-29 16:14:40 +02:00
Arjen Hiemstra b0ae307006 Fix check for "touching buildplate" option of support type
CURA-168 #done
2015-09-29 16:09:06 +02:00
Hajo Nils Krabbenhöft 29dd62345a use int for zhop 2015-09-29 15:49:34 +02:00
Hajo Nils Krabbenhöft b3f555c78f store current z-hop height and keep applying that on multi-step travel 2015-09-29 15:49:25 +02:00
Tim Kuipers 0b2f64c6a7 bugfix: total_layers count 2015-09-29 14:44:50 +02:00
Tim Kuipers d501fe67d5 refactor: totalLayers -> total_layers 2015-09-29 14:41:12 +02:00
Tim Kuipers 7180e77699 refactor: fixed enum FillPerimeterGapMode to match other enums 2015-09-29 11:04:04 +02:00
Olliver Schinagl 9c36fdf513 GCodeExport: (re-)Add layer_count tag
Commit 1070d36 broke the build by enabeling code that was not kept up to
date, this patch fixes it properly by adding the
writeLayerCountComment() method.

Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-29 11:03:35 +02:00
Tim Kuipers bc8a167e7b refactor: fixed enum FillPerimeterGapMode to match other enums 2015-09-29 11:03:32 +02:00
Olliver Schinagl a2881b83f7 GCodeExport: (re-)Add layer_count tag
Commit 1070d36 broke the build by enabeling code that was not kept up to
date, this patch fixes it properly by adding the
writeLayerCountComment() method.

Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-29 10:25:35 +02:00
Olliver Schinagl bd90dd060f Re-nable layer count using a consistent tag name
Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-28 17:37:03 +02:00
Olliver Schinagl 1070d367e1 Re-nable layer count using a consistent tag name
Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-25 12:04:19 +02:00
Arjen Hiemstra 5ac62a96a8 Also fix another call for Fill Perimeter Gaps
Contributes to Ultimaker/Cura#411
2015-09-22 12:38:53 +02:00
Arjen Hiemstra 7010689448 Fix "Fill Perimeter Gaps" to properly check values
Since the enum values were changed to normalised values this check was
wrong. Additonally, now properly use an Enum for the values since that
is a cleaner approach.

Fixes Ultimaker/Cura#411
2015-09-22 12:37:03 +02:00
Jaime van Kessel 869975be6b Merge pull request #247 from fxtentacle/15.10-retract-when-no-combing
always retract when combing is disabled
2015-09-22 09:57:23 +02:00
Hajo Nils Krabbenhöft da3bf8da32 always retract when combing is disabled 2015-09-22 09:53:11 +02:00
Jaime van Kessel e861fba11b Merge pull request #246 from fxtentacle/fix-retraction-for-small-isles
fix retraction for isles < 0.8mm radius
2015-09-21 21:22:58 +08:00
Hajo Nils Krabbenhöft 226c974b5f rework getLayerSecondWalls() to make sure it doesn't completely remove isles with less than 0.8mm radius (= offset_from_outlines = 2*nozzle_size) 2015-09-21 14:14:18 +02:00
Jaime van Kessel b25c72a9e3 Merge pull request #245 from fxtentacle/fix-zhop-2
Keep Z-Hop active for multi-step moves
2015-09-21 18:06:25 +08:00
Hajo Nils Krabbenhöft 6caf2b2db9 instead of the hardcoded 1.5mm, use last_retraction_config->retraction_min_travel_distance as threshold 2015-09-21 12:01:57 +02:00
Hajo Nils Krabbenhöft b6138cb951 replace hardcoded Comb::max_comb_distance_ignored = 1.5mm with function parameter, which defaults to 1.5mm 2015-09-21 12:00:37 +02:00
Jaime van Kessel e9b0cfc939 Merge pull request #244 from fxtentacle/retract-when-no-combing2
always retract when combing is disabled
2015-09-21 17:34:08 +08:00
Hajo Nils Krabbenhöft f6e44af43d use int for zhop 2015-09-21 11:33:09 +02:00
Hajo Nils Krabbenhöft 487f35abd4 store current z-hop height and keep applying that on multi-step travel 2015-09-21 11:33:09 +02:00
Jaime van Kessel 2a163528f9 Merge pull request #242 from fxtentacle/master
send travel moves to GUI from GCodeExport
2015-09-21 17:32:08 +08:00
Hajo Nils Krabbenhöft 02e6c87396 always retract when combing is disabled 2015-09-21 11:31:59 +02:00
Hajo Nils Krabbenhöft b367af6ad1 thicker lines for retraction moves 2015-09-21 00:17:02 +02:00
Hajo Nils Krabbenhöft eef8b77405 GCodePlanner will initialize command socket on GCodeExport, which will then send combing and retraction paths to GUI 2015-09-21 00:11:07 +02:00
Hajo Nils Krabbenhöft 97a4c00022 also add new poly types to cpp backend 2015-09-20 23:23:41 +02:00
Hajo Nils Krabbenhöft 6516ae0da9 add new types to protobuf 2015-09-20 23:16:07 +02:00
Arjen Hiemstra 360e3d7e24 Fix one-at-a-time mode so it does not stop after printing 1 object
CURA-162:review
CURA-190:review
2015-09-18 12:09:08 +02:00
Tim Kuipers c267492a48 bugfix: raft viz 2015-09-10 13:20:44 +02:00
Tim Kuipers 52258e2d3a bugfix: send polygon info after fuzzy skin 2015-09-10 11:44:43 +02:00
Tim Kuipers 92c6edca86 bugfix: send polygon info after fuzzy skin 2015-09-10 11:40:26 +02:00
Tim Kuipers 9c26c820a9 bugfix: skin line distance was infill line width 2015-09-10 11:20:40 +02:00
Tim Kuipers b7993441ff bugfix: support progress was still wrong per object 2015-09-10 10:22:06 +02:00
Tim Kuipers 10ebc96323 bugfix: no bottom/top layers resulted in a lingle layer 2015-09-09 17:06:08 +02:00
Tim Kuipers 13af50f34d Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-09 16:52:25 +02:00
Tim Kuipers 0563c6eacc Merge branch 'bugfix_per_object_support' into 15.10 2015-09-09 15:26:54 +02:00
Arjen Hiemstra 6f95a78f3c Set correct RPATH for CuraEngine 2015-09-08 17:29:25 +02:00
Tim Kuipers ae79d232f3 bugfixes for skin extrusion width; feature: toggle skin_no_small_gaps_heuristic 2015-09-08 16:25:21 +02:00
Tim Kuipers 11786ada66 lil change fuzzy mode 2015-09-08 12:35:24 +02:00
Tim Kuipers 648bc1f3f3 bugfix: fuzzy skin had too many points; redux can lead to not enough points on poly 2015-09-08 12:35:16 +02:00
Tim Kuipers 84be436eb9 feat: fuzzy skin magic mode 2015-09-08 12:35:06 +02:00
Tim Kuipers e04965187a bugfix: visualize skin lines instead of areas 2015-09-08 10:34:53 +02:00
131 arquivos alterados com 12205 adições e 4938 exclusões
+22 -13
Ver Arquivo
@@ -1,25 +1,34 @@
*.tar.bz2
*.tar.gz
*.7z
*.pyc
*.zip
*.exe
.idea
.DS_Store
_bin
_obj
*.depend
*.o
.*.swp
*.gcode
CuraEngine
build/*
*~
NUL
*.gcode
## Building result.
build/*
*.pyc
*.exe
*.o
CuraEngine
_bin
_obj
## IDE project files.
CuraEngine.layout
CuraEngine.cbp
*kdev*
*.kate-swp
nbproject/*
.idea
*.depend
.*.swp
## Documentation.
documentation/html/*
documentation/latex/*
*kdev*
*.kate-swp
## Test results.
tests/output.xml
+101 -13
Ver Arquivo
@@ -2,7 +2,14 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
find_package(Arcus REQUIRED)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -10,6 +17,22 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
option(BUILD_TESTS OFF)
# Add a compiler flag to check the output for insane values if we are in debug mode.
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
endif()
# Add warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -21,7 +44,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
add_library(clipper STATIC libs/clipper/clipper.cpp)
set(engine_SRCS
set(engine_SRCS # Except main.cpp.
src/bridge.cpp
src/comb.cpp
src/commandSocket.cpp
@@ -31,45 +54,110 @@ set(engine_SRCS
src/gcodeExport.cpp
src/gcodePlanner.cpp
src/infill.cpp
src/inset.cpp
src/WallsComputation.cpp
src/layerPart.cpp
src/main.cpp
src/LayerPlanBuffer.cpp
src/MergeInfillLines.cpp
src/mesh.cpp
src/MeshGroup.cpp
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/PrimeTower.cpp
src/Progress.cpp
src/raft.cpp
src/settingRegistry.cpp
src/settings.cpp
src/skin.cpp
src/skirt.cpp
src/sliceDataStorage.cpp
src/slicer.cpp
src/support.cpp
src/timeEstimate.cpp
src/WallsComputation.cpp
src/wallOverlap.cpp
src/Weaver.cpp
src/Wireframe2gcode.cpp
src/infill/NoZigZagConnectorProcessor.cpp
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/progress/Progress.cpp
src/progress/ProgressStageEstimator.cpp
src/settings/SettingConfig.cpp
src/settings/SettingContainer.cpp
src/settings/SettingRegistry.cpp
src/settings/settings.cpp
src/utils/AABB.cpp
src/utils/AABB3D.cpp
src/utils/Date.cpp
src/utils/gettime.cpp
src/utils/LinearAlg2D.cpp
src/utils/logoutput.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
set(engine_TEST
GCodePlannerTest
)
set(engine_TEST_INFILL
)
set(engine_TEST_UTILS
BucketGrid2DTest
LinearAlg2DTest
PolygonUtilsTest
)
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
target_link_libraries(CuraEngine clipper Arcus)
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
# Compiling CuraEngine itself.
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
target_link_libraries(_CuraEngine clipper)
if (ENABLE_ARCUS)
target_link_libraries(_CuraEngine Arcus)
endif ()
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
if (UNIX)
target_link_libraries(CuraEngine pthread)
target_link_libraries(_CuraEngine pthread)
endif()
add_executable(CuraEngine src/main.cpp) #Then compile main.cpp as separate executable, and link the library to it.
target_link_libraries(CuraEngine _CuraEngine)
# Compiling the test environment.
if (BUILD_TESTS)
message(STATUS "Building tests...")
enable_testing()
foreach (test ${engine_TEST})
add_executable(${test} tests/main.cpp tests/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_INFILL})
add_executable(${test} tests/main.cpp tests/infill/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_UTILS})
add_executable(${test} tests/main.cpp tests/utils/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
endif()
add_custom_command(TARGET CuraEngine POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:CuraEngine>)
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
include(CPackConfig.cmake)
+6 -13
Ver Arquivo
@@ -1,19 +1,12 @@
set(CPACK_PACKAGE_VENDOR "Ultimaker")
set(CPACK_PACKAGE_CONTACT "Arjen Hiemstra <a.hiemstra@ultimaker.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cura Engine")
set(CPACK_PACKAGE_VERSION_MAJOR 15)
set(CPACK_PACKAGE_VERSION_MINOR 05)
set(CPACK_PACKAGE_VERSION_PATCH 90)
set(CPACK_GENERATOR "DEB;RPM")
set(RPM_REQUIRES
"arcus >= 15.05.90"
"protobuf >= 3.0.0"
"libstdc++6 >= 4.9.0"
"libgcc1 >= 4.9.0"
)
string(REPLACE ";" "," RPM_REQUIRES "${RPM_REQUIRES}")
set(CPACK_RPM_PACKAGE_REQUIRES ${RPM_REQUIRES})
set(CPACK_PACKAGE_VERSION "15.05.90")
set(CPACK_GENERATOR "DEB")
if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
set(DEB_DEPENDS
"arcus (>= 15.05.90)"
+8 -25
Ver Arquivo
@@ -2,20 +2,18 @@ syntax = "proto3";
package cura.proto;
message ObjectList
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
}
// typeid 1
message Slice
{
repeated ObjectList object_lists = 1;
}
message Object
message Object
{
int64 id = 1;
bytes vertices = 2; //An array of 3 floats.
@@ -24,28 +22,13 @@ message Object
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
}
// typeid 3
message Progress
message Progress
{
float amount = 1;
}
// typeid 2
message SlicedObjectList
{
repeated SlicedObject objects = 1;
}
message SlicedObject
{
int64 id = 1;
repeated Layer layers = 2;
}
message Layer {
int32 id = 1;
float height = 2;
float thickness = 3;
@@ -62,26 +45,24 @@ message Polygon {
SkirtType = 5;
InfillType = 6;
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
}
Type type = 1;
bytes points = 2;
float line_width = 3;
}
// typeid 4
message GCodeLayer {
int64 id = 1;
bytes data = 2;
}
// typeid 5
message ObjectPrintTime {
int64 id = 1;
float time = 2;
float material_amount = 3;
}
// typeid 6
message SettingList {
repeated Setting settings = 1;
}
@@ -92,7 +73,9 @@ message Setting {
bytes value = 2;
}
// typeid 7
message GCodePrefix {
bytes data = 2;
}
message SlicingFinished {
}
+1 -1
Ver Arquivo
@@ -178,7 +178,7 @@ JAVADOC_AUTOBRIEF = NO
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
QT_AUTOBRIEF = YES
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+6 -1
Ver Arquivo
@@ -54,11 +54,16 @@ For that one needs a settings JSON file, which can be found in the Ultimaker/Cur
An example run for an UM2 machine looks as follows:
* Navigate to the CuraEngine directory and execute the following
```
./build/CuraEngine slice -v -j ../Cura/resources/machines/dual_extrusion_printer.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
./build/CuraEngine slice -v -j ../Cura/resources/definitions/dual_extrusion_printer.def.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
```
Run `CuraEngine help` for a general description of how to use the CuraEngine tool.
[Set the environment variable](https://help.ubuntu.com/community/EnvironmentVariables) CURA_ENGINE_SEARCH_PATH to the appropriate paths, delimited by a colon e.g.
```
CURA_ENGINE_SEARCH_PATH=/path/to/Cura/resources/definitions:/user/defined/path
```
Internals
=========
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 20 KiB

-126
Ver Arquivo
@@ -1,126 +0,0 @@
Code Conventions
=======
Note that the code convention described here have not all yet been fully implemented.
Bracketing and indenting
-----
~~~~~~~~~~~~~~~{.cpp}
if (condition) // brackets always on new lines
{ // allways a bracket after an if, for, while, etc.
// indent always with 4 spaces, never with tabs
}
else // else on new line
{
// more code
}
~~~~~~~~~~~~~~~
Naming conventions
------
* variables: lower_case_with_underscores
* functions: loweCamelCase
* classes: UpperCamelCase
* macros: UPPER_CASE_WITH_UNDERSCORES
~~~~~~~~~~~~~~~{.cpp}
#define UPPER_CASE_MACRO 1
class UpperCamelCase
{
private:
MemberVariableObject with_underscores;
public:
MemberVariableObject with_underscores;
public:
UpperCamelCase();
~UpperCamelCase();
// start with input variable(s) and end with output variable(s)
void lowerCamelCaseFunctions(ParamObject& also_with_underscores)
{
LocalObject under_scores;
}
private:
void putFunctionsAndVariablesInSeperatePublicPrivateBlocks();
};
~~~~~~~~~~~~~~~
Ordering
----
~~~~~~~~~~~~~~~{.cpp}
class Example
{
// start with input variable(s) and end with output parameter(s)
void function1(ParamObject& input_variable, int setting_parameter, ParamObject2& return_parameter)
{
function2();
function3();
}
// place functions called solely by one other function below it chronologically
void function2();
void function3();
};
~~~~~~~~~~~~~~~
Documentation
----
We use [Doxygen](www.doxygen.org/) to generate documentation. Try to keep your documentation in doxygen style.
Here's a small example:
~~~~~~~~~~~~~~~{.cpp}
/ *!
* Doxygen style comments!
*
* \param param1 explanation may refer to another \p param2
* /
void function(int param1, int param2)
{
// non-doxygen style comments on implementation details
}
int member; //!< inline doxygen comment on the entry to the left
~~~~~~~~~~~~~~~
Files
--------
For a file Foo.h (UpperCamelCase):
~~~~~~~~~~~~~~~{.cpp}
#ifndef FOO_H
#define FOO_H
// [content]
#endif//FOO_H
~~~~~~~~~~~~~~~
Other
----
~~~~~~~~~~~~~~~{.cpp}
#include <all>
#include <includes>
#include <on>
#include <top>
#include <first_system_includes>
#include <then_library_includes>
#include "finally_local_includes"
enum class EnumExample
{
ELEM0 = 0,
ELEM1 = 1
};
~~~~~~~~~~~~~~~
Illegal syntax
----
~~~~~~~~~~~~~~~{.cpp}
void function()
{
if (condition)
single_line_outside_code_block(); // always use braces!
}; // unneccesary semicolon after function definition is not allowed
~~~~~~~~~~~~~~~
+203
Ver Arquivo
@@ -0,0 +1,203 @@
This file is a conversion from the ENGINE settings of 15.04 to the ENGINE setting of 2.0
This is NOT a conversion on the frontend internal setting names (15.04 has the dictionary of doom)
autoCenter ? ==> center_object OR machine_center_is_zero ??
coolHeadLift ==> cool_lift_head
downSkinCount ==> bottom_layers
enableCombing ==> retraction_combing
enableOozeShield ==> ooze_shield_enabled
endCode ==> machine_end_gcode
extruderOffset[MAX_EXTRUDERS] = (machine_nozzle_offset_x, machine_nozzle_offset_y)
extrusionWidth ==> infill_line_width, skirt_line_width, support_line_width
fanFullOnLayerNr ==> cool_fan_full_layer
fanSpeedMax ==> cool_fan_speed_max
fanSpeedMin ==> cool_fan_speed_min
filamentDiameter ==> material_diameter
filamentFlow ==> material_flow
fixHorrible ==> meshfix_union_all AND/OR meshfix_union_all_remove_holes AND/OR meshfix_extensive_stitching AND/OR magic_mesh_surface_mode
gcodeFlavor ==> machine_gcode_flavor
infillOverlap ==> infill_overlap
infillPattern ==> infill_pattern
infillSpeed ==> speed_infill
initialLayerSpeed ==> speed_layer_0
initialLayerThickness ==> layer_height_0
initialSpeedupLayers ==> speed_slowdown_layers
inset0Speed ==> speed_wall_0
insetCount ==> wall_line_count
insetXSpeed ==> speed_wall_x
layer0extrusionWidth [ Doesn't exist anymore ]
layerThickness ==> layer_height
matrix [ Doesn't exist anymore ]
minimalExtrusionBeforeRetraction
minimalFeedrate ==> cool_min_speed
minimalLayerTime ==> cool_min_layer_time
moveSpeed ==> speed_travel
multiVolumeOverlap ==> multiple_mesh_overlap
nozzleSize ==> machine_nozzle_size
objectPosition ==> mesh_position_x, mesh_position_y, mesh_position_z
objectSink [ Doesn't exist in CuraEngine anymore ]
perimeterBeforeInfill = not(infill_before_walls)
postSwitchExtruderCode ==> machine_extruder_start_code
preSwitchExtruderCode ==> machine_extruder_end_code
printSpeed ==> speed_prime_tower, speed_support_lines, speed_support_roof, skirt_speed
raftAirGap ==> raft_airgap
raftAirGapLayer0 ?!?!?
raftBaseLinewidth ==> raft_base_line_width
raftBaseSpeed ==> raft_interface_speed, raft_base_speed
raftBaseThickness ==> raft_base_thickness
raftFanSpeed ==> raft_base_fan_speed, raft_interface_fan_speed, raft_surface_fan_speed
raftInterfaceLineSpacing==> raft_interface_line_spacing
raftInterfaceLinewidth ==> raft_interface_line_width
raftInterfaceThickness ==> raft_interface_thickness
raftLineSpacing ==> raft_base_line_spacing
raftMargin ==> raft_margin
raftSurfaceLayers ==> raft_surface_layers
raftSurfaceLineSpacing ==> raft_surface_line_spacing
raftSurfaceLinewidth ==> raft_surface_line_width
raftSurfaceSpeed ==> raft_surface_speed
raftSurfaceThickness ==> raft_surface_thickness
retractionAmount ==> retraction_amount (set retraction_enable = true)
retractionAmountExtruderSwitch ==> switch_extruder_retraction_amount
retractionAmountPrime ==> retraction_extra_prime_amount
retractionMinimalDistance ==> retraction_extrusion_window ( set retraction_count_max = 1 )
retractionSpeed ==> retraction_retract_speed (, retraction_prime_speed ?), switch_extruder_retraction_speed
retractionZHop ==> retraction_hop
simpleMode ??!
skinSpeed ==> speed_topbottom
skirtDistance ==> skirt_gap
skirtLineCount ==> brim_line_count, skirt_line_count
skirtMinLength ==> skirt_minimal_length
sparseInfillLineDistance ==> infill_line_distance
spiralizeMode ==> magic_spiralize
startCode ==> machine_start_gcode
supportAngle ==> support_angle, support_enable=true if support_angle>0
supportEverywhere ==> support_type
supportExtruder ==> support_extruder_nr, support_extruder_nr_layer_0
supportLineDistance ==> support_line_distance
supportType ==> support_pattern
supportXYDistance ==> support_xy_distance
supportZDistance ==> support_z_distance
upSkinCount ==> top_layers
wipeTowerSize ==> prime_tower_size
NEW:
adhesion_extruder_nr
adhesion_type
alternate_extra_perimeter
coasting_enable
coasting_min_volume_move
coasting_min_volume_retract
coasting_speed_move
coasting_speed_retract
coasting_volume_move
coasting_volume_retract
cool_min_layer_time_fan_speed_max
draft_shield_dist
draft_shield_height
extruder_nr
fill_perimeter_gaps
infill_sparse_thickness
infill_wipe_dist
machine_depth
machine_extruder_count
machine_extruder_end_pos_abs
machine_extruder_end_pos_x
machine_extruder_end_pos_y
machine_extruder_start_pos_abs
machine_extruder_start_pos_x
machine_extruder_start_pos_y
machine_heated_bed
machine_nozzle_cool_down_speed
machine_nozzle_expansion_angle
machine_nozzle_head_distance
machine_nozzle_heat_up_speed
machine_nozzle_tip_outer_diameter
machine_print_temp_wait
machine_use_extruder_offset_to_offset_coords
machine_width
magic_fuzzy_skin_enabled
magic_fuzzy_skin_point_dist
magic_fuzzy_skin_thickness
material_bed_temperature
material_bed_temp_prepend
material_bed_temp_wait
material_extrusion_cool_down_speed
material_flow_dependent_temperature
material_flow_temp_graph
material_print_temperature
material_print_temp_prepend
material_print_temp_wait
material_standby_temperature
meshfix_keep_open_polygons
ooze_shield_angle
ooze_shield_dist
prime_tower_dir_outward
prime_tower_distance
prime_tower_flow
prime_tower_line_width
prime_tower_position_x
prime_tower_position_y
prime_tower_wipe_enabled
remove_overlapping_walls_0_enabled
remove_overlapping_walls_x_enabled
retraction_min_travel
skin_alternate_rotation
skin_line_width
skin_no_small_gaps_heuristic
skin_outline_count
support_area_smoothing
support_bottom_distance
support_bottom_stair_step_height
support_conical_angle
support_conical_enabled
support_conical_min_width
support_connect_zigzags
support_join_distance
support_minimal_diameter
support_offset
support_roof_enable
support_roof_extruder_nr
support_roof_height
support_roof_line_distance
support_roof_line_width
support_roof_pattern
support_top_distance
support_tower_diameter
support_tower_roof_angle
switch_extruder_prime_speed
top_bottom_pattern
travel_avoid_distance
travel_avoid_other_parts
travel_compensate_overlapping_walls_enabled
wall_line_width_0
wall_line_width_x
wireframe_bottom_delay
wireframe_drag_along
wireframe_enabled
wireframe_fall_down
wireframe_flat_delay
wireframe_flow_connection
wireframe_flow_flat
wireframe_height
wireframe_nozzle_clearance
wireframe_printspeed_bottom
wireframe_printspeed_down
wireframe_printspeed_flat
wireframe_printspeed_up
wireframe_roof_drag_along
wireframe_roof_fall_down
wireframe_roof_inset
wireframe_roof_outer_delay
wireframe_straight_before_down
wireframe_strategy
wireframe_top_delay
wireframe_top_jump
wireframe_up_half_speed
xy_offset
z_seam_type
+19
Ver Arquivo
@@ -0,0 +1,19 @@
find engine setting literals
cd ~/Development/CuraEngine/output/reflection/
~/bin/substitute.pl y 'while(/getSetting\w+\("(\w+)"\)/gsm) { print "$1\n"; }' ../../src/ | sort | uniq > engineSettingLiterals.txt
run setting inheritance reflection
cd ~/Development/CuraEngine
./build/CuraEngine analyse ../Cura/resources/machines/fdmprinter.json meta/refl_ff.gv output/reflection/engineSettingLiterals.txt -piew
dot meta/refl_ff.gv -Tpng > meta/rafl_ff_dotted.png
green block = used in engine
red edge = inherit function only
black edge = parent-child relation
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 284 KiB

+42
Ver Arquivo
@@ -0,0 +1,42 @@
{
"version": 1,
"name": "Command line setting defaults CuraEngine",
"author": "Ultimaker B.V.",
"categories": {
"command_line_settings": {
"label": "Command Line Settings",
"settings": {
"center_object": {
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
"type": "boolean",
"default": false
},
"machine_print_temp_wait": {
"description": "Whether to wait for the nozzle temperature to be reached when preheating the nozzles at the start of the gcode.",
"type": "boolean",
"default": true
},
"mesh_position_x": {
"description": "Offset applied to the object in the x direction.",
"type": "float",
"default": 0
},
"mesh_position_y": {
"description": "Offset applied to the object in the y direction.",
"type": "float",
"default": 0
},
"mesh_position_z": {
"description": "Offset applied to the object in the z direction. With this you can perform what was used to call 'Object Sink'.",
"type": "float",
"default": 0
},
"prime_tower_dir_outward": {
"description": "Whether to start printing in the middle of the prime tower and end up at the perimeter, or the other way around. This is only used for certain types of prime tower.",
"type": "boolean",
"default": false
}
}
}
}
}
+2 -2
Ver Arquivo
@@ -1,7 +1,7 @@
#ifndef EXTRUDER_TRAIN_H
#define EXTRUDER_TRAIN_H
#include "settings.h"
#include "settings/settings.h"
namespace cura
{
@@ -13,7 +13,7 @@ public:
int getExtruderNr() { return extruder_nr; }
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
: SettingsBase(settings)
: SettingsBase(settings, std::string("extruder"))
, extruder_nr(extruder_nr)
{ }
+22
Ver Arquivo
@@ -0,0 +1,22 @@
#ifndef FAN_SPEED_LAYER_TIME_H
#define FAN_SPEED_LAYER_TIME_H
#include "settings/settings.h"
namespace cura
{
struct FanSpeedLayerTimeSettings
{
public:
double cool_min_layer_time;
double cool_min_layer_time_fan_speed_max;
double cool_fan_speed_min;
double cool_fan_speed_max;
double cool_min_speed;
int cool_fan_full_layer;
};
} // namespace cura
#endif // FAN_SPEED_LAYER_TIME_H
+463 -365
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+169 -101
Ver Arquivo
@@ -5,6 +5,8 @@
#include <fstream>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/NoCopy.h"
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "raft.h"
#include "infill.h"
@@ -13,8 +15,13 @@
#include "gcodePlanner.h"
#include "gcodeExport.h"
#include "commandSocket.h"
#include "utils/polygonUtils.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "PrintFeature.h"
#include "LayerPlanBuffer.h"
namespace cura
{
@@ -26,46 +33,68 @@ namespace cura
*
* The main function of this class is FffGcodeWriter::writeGCode().
*/
class FffGcodeWriter : public SettingsMessenger
class FffGcodeWriter : public SettingsMessenger, NoCopy
{
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
private:
int max_object_height;
int meshgroup_number; //!< used for sequential printing of objects
int max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
/*
* Buffer for all layer plans (of type GCodePlanner)
*
* The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used.
* Another reason is to perform Auto Temperature.
*/
LayerPlanBuffer layer_plan_buffer;
/*!
* The class holding the current state of the gcode being written.
*
* It holds information such as the last written position etc.
*/
GCodeExport gcode;
CommandSocket* command_socket;
/*!
* The gcode file to write to when using CuraEngine as command line tool.
*/
std::ofstream output_file;
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
bool skirt_is_processed[MAX_EXTRUDERS]; //!< Whether the skirt polygons have been processed into planned paths for each extruder train
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //!< The settings used relating to minimal layer time and fan speeds.
Point last_position_planned; //!< The position of the head before planning the next layer
int current_extruder_planned; //!< The extruder train in use before planning the next layer
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, layer_plan_buffer(this, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // TODO: make configurable
, is_inside_mesh_layer_part(false)
{
meshgroup_number = 1;
max_object_height = 0;
command_socket = NULL;
}
void resetFileNumber()
{
meshgroup_number = 1;
for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
{
skirt_is_processed[extruder_nr] = false;
}
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
}
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (command_socket)
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
output_file.open(filename);
@@ -76,94 +105,136 @@ public:
}
return false;
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
gcode.setOutputStream(stream);
}
double getTotalFilamentUsed(int e)
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode.getTotalFilamentUsed(e);
return gcode.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode.getTotalPrintTime();
}
/*!
* Write all the gcode for the current meshgroup.
* This is the primary function of this class.
*
* \param[in] storage The data storage from which to get the polygons to print and the areas to fill.
* \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process.
*/
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
private:
/*!
* Set the FffGcodeWriter::fan_speed_layer_time_settings by retrieving all settings from the global/per-meshgroup settings.
*/
void setConfigFanSpeedLayerTime();
/*!
* Create and set the SliceDataStorage::coasting_config for each extruder.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigCoasting(SliceDataStorage& storage);
//Setup the retraction parameters.
/*!
* Set the retraction config globally, per extruder and per mesh.
*
* \param[out] storage The data storage to which to save the configurations
*/
void setConfigRetraction(SliceDataStorage& storage);
void setConfigSkirt(SliceDataStorage& storage, int layer_thickness);
void setConfigSupport(SliceDataStorage& storage, int layer_thickness);
void setConfigInsets(SliceMeshStorage& mesh, int layer_thickness);
void setConfigSkin(SliceMeshStorage& mesh, int layer_thickness);
void setConfigInfill(SliceMeshStorage& mesh, int layer_thickness);
/*!
* Initialize the GcodePathConfig config parameters which don't change over all layers, for each feature.
*
* The features are: skirt, support and for each mesh: outer wall, inner walls, skin, infill (and combined infill)
*
* \param[out] storage The data storage to which to save the configurations
*/
void initConfigs(SliceDataStorage& storage);
/*!
* Set temperatures and perform initial priming.
* \param storage Input: where the slice data is stored.
*
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
*
* \param[in] storage where the slice data is stored.
*/
void processStartingCode(SliceDataStorage& storage);
/*!
* Move up and over the just printed model to print the next model.
* \param storage Input: where the slice data is stored.
* Move up and over the already printed meshgroups to print the next meshgroup.
*
* \param[in] storage where the slice data is stored.
*/
void processNextMeshGroupCode(SliceDataStorage& storage);
/*!
* Add raft gcode.
* \param storage Input: where the slice data is stored.
* \param totalLayers The total number of layers.
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
*
* \param[in] storage where the slice data is stored.
* \param total_layers The total number of layers.
*/
void processRaft(SliceDataStorage& storage, unsigned int totalLayers);
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Add a layer to the gcode.
* \param storage Input: where the slice data is stored.
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
*
* \param[in] storage where the slice data is stored.
* \param layer_nr The index of the layer to write the gcode of.
* \param totalLayers The total number of layers.
* \param total_layers The total number of layers.
* \param has_raft Whether a raft is used for this print.
*/
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int totalLayers, bool has_raft);
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
/*!
* Interpolate between the initial layer speeds and the eventual speeds.
* \param storage Input: where the slice data is stored.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processInitialLayersSpeedup(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Add the skirt to the gcode.
* \param storage Input: where the slice data is stored.
* Add the skirt to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param extruder_nr The extrudewr train for which to process the skirt
*/
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
/*!
* Adds the ooze shield to the print.
* \param storage Input: where the slice data is stored.
* Adds the ooze shield to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Adds the draft protection screen to the print.
* \param storage Input: where the slice data is stored.
* Adds the draft protection screen to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
@@ -171,16 +242,18 @@ private:
/*!
* Calculate in which order to print the meshes.
* \param storage Input: where the slice data is stored.
*
* \param[in] storage where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of mesh indices ordered on print order.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
/*!
* Add a single layer from a single mesh-volume to the GCode in mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -188,9 +261,10 @@ private:
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the open polylines from a single layer from a single mesh-volume to the GCode for mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh for which to add to the gcode.
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -198,9 +272,10 @@ private:
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add a single layer from a single mesh-volume to the GCode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -208,35 +283,36 @@ private:
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer.
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Add normal sparse infill for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
@@ -247,34 +323,34 @@ private:
/*!
* Add the gcode of the top/bottom skin of the given part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param skin_overlap The distance by which the skinfill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width);
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle, int extrusion_width);
/*!
* Add the support to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
* \param before_rest Whether the function has been called before adding the rest to the gcode, or after.
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, or after.
*/
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
/*!
* Add the support lines/walls to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support roofs to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support roofs to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
@@ -285,7 +361,7 @@ private:
*
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
*
* \param storage Input: where the slice data is stored.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr The extruder to which to switch
@@ -294,21 +370,13 @@ private:
/*!
* Add the prime tower gcode for the current layer.
* \param storage Input: where the slice data is stored.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param prev_extruder The current extruder with which we last printed.
*/
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
/*!
* Finish the layer by applying speed corrections for minimal layer times and determine the fanSpeed.
* \param storage Input: where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processFanSpeedAndMinimalLayerTime(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Add the end gcode and set all temperatures to zero.
*/
+293 -282
Ver Arquivo
@@ -1,9 +1,7 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include <random> // for bulging effect?
#include <functional> // for bugling?
#include <cmath> // for bulging?
#include <map> // multimap (ordered map allowing duplicate keys)
#include "slicer.h"
#include "utils/gettime.h"
@@ -12,13 +10,18 @@
#include "support.h"
#include "multiVolumes.h"
#include "layerPart.h"
#include "inset.h"
#include "WallsComputation.h"
#include "skirt.h"
#include "skin.h"
#include "infill.h"
#include "raft.h"
#include "debug.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
#include "progress/ProgressEstimatorLinear.h"
namespace cura
{
@@ -26,9 +29,6 @@ namespace cura
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (commandSocket)
commandSocket->beginSendSlicedObject();
if (!sliceModel(meshgroup, timeKeeper, storage))
{
return false;
@@ -41,21 +41,31 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
storage.model_size = storage.model_max - storage.model_min;
log("Slicing model...\n");
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{
initial_layer_thickness = layer_thickness;
int initial_layer_thickness = getSettingInMicrons("layer_height_0");
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Initial layer height %i is disallowed.\n", initial_layer_thickness);
return false;
}
int layer_thickness = getSettingInMicrons("layer_height");
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Layer height %i is disallowed.\n", layer_thickness);
return false;
}
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
return true; //This is NOT an error state!
}
std::vector<Slicer*> slicerList;
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
@@ -67,23 +77,21 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
bulgeWalls(slicerList, meshgroup);
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
generateMultipleVolumesOverlap(slicerList);
// TODO!!! dont generate multi volume overlap with infill meshes!
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
@@ -91,28 +99,32 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
bool has_raft = meshStorage.getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
//Add the raft offset to each layer.
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
getSettingInMicrons("layer_height_0")
- initial_slice_z;
if (has_raft)
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("adhesion_extruder_nr"));
layer.printZ +=
meshStorage.getSettingInMicrons("raft_base_thickness")
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("layer_height") //raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap")
- initial_slice_z;
}
else
{
meshStorage.layers[layer_nr].printZ +=
meshStorage.getSettingInMicrons("layer_height_0")
- initial_slice_z;
train->getSettingInMicrons("raft_base_thickness")
+ train->getSettingInMicrons("raft_interface_thickness")
+ train->getSettingAsCount("raft_surface_layers") * train->getSettingInMicrons("raft_surface_thickness")
+ train->getSettingInMicrons("raft_airgap")
- train->getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
{
layer.printZ += train->getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
}
}
@@ -121,149 +133,263 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (commandSocket)
if (CommandSocket::isInstantiated())
{
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0 && !has_raft? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
return true;
}
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
// const
unsigned int total_layers = storage.meshes.at(0).layers.size();
//layerparts2HTML(storage, "output/output.html");
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
// compute layer count and remove first empty layers
// there is no separate progress stage for removeEmptyFisrtLayer (TODO)
unsigned int total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
processInsets(storage, layer_number);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
if (!mesh.getSettingBoolean("infill_mesh"))
{
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
}
}
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
if (total_layers < 1)
// handle meshes
std::vector<double> mesh_timings;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
log("Stopping process because there are no layers.\n");
mesh_timings.push_back(1.0); // TODO: have a more accurate estimate of the relative time it takes per mesh, based on the height and number of polygons
}
ProgressStageEstimator inset_skin_progress_estimate(mesh_timings);
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, &time_keeper);
std::vector<unsigned int> mesh_order;
{ // compute mesh order
std::multimap<int, unsigned int> order_to_mesh_indices;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
order_to_mesh_indices.emplace(storage.meshes[mesh_idx].getSettingAsIndex("infill_mesh_order"), mesh_idx);
}
for (std::pair<const int, unsigned int>& order_and_mesh_idx : order_to_mesh_indices)
{
mesh_order.push_back(order_and_mesh_idx.second);
}
}
for (unsigned int mesh_idx : mesh_order)
{
processBasicWallsSkinInfill(storage, mesh_idx, mesh_order, total_layers, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_idx + 1, storage.meshes.size());
}
//layerparts2HTML(storage, "output/output.html");
// we need to remove empty layers after we have procesed the insets
// processInsets might throw away parts if they have no wall at all (cause it doesn't fit)
// brim depends on the first layer not being empty
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers); // changes total_layers!
if (total_layers == 0)
{
log("Stopping process because there are no non-empty layers.\n");
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, total_layers);
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
}
}
}
*/
// handle helpers
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, total_layers);
processOozeShield(storage, total_layers);
processDraftShield(storage, total_layers);
processPlatformAdhesion(storage);
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
int mesh_max_bottom_layer_count = 0;
if (getSettingBoolean("magic_spiralize"))
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
for(SliceMeshStorage& mesh : storage.meshes)
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
processDerivedWallsSkinInfill(mesh, total_layers);
}
}
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh"))
{
processInfillMesh(storage, mesh_idx, mesh_order, total_layers);
}
// TODO: make progress more accurate!!
// note: estimated time for insets : skins = 22.953 : 48.858
std::vector<double> walls_vs_skin_timing({22.953, 48.858});
ProgressStageEstimator* mesh_inset_skin_progress_estimator = new ProgressStageEstimator(walls_vs_skin_timing);
inset_skin_progress_estimate.nextStage(mesh_inset_skin_progress_estimator); // the stage of this function call
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(total_layers);
mesh_inset_skin_progress_estimator->nextStage(inset_estimator);
// walls
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
processInsets(mesh, layer_number);
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(total_layers);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
// skin & infill
// Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
if (mesh.getSettingBoolean("magic_spiralize"))
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkins(storage, layer_number);
}
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
}
for(unsigned int layer_number = total_layers-1; layer_number > 0; layer_number--)
{
for(SliceMeshStorage& mesh : storage.meshes)
combineInfillLayers(layer_number, mesh, mesh.getSettingAsCount("infill_sparse_combine"));
}
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, total_layers);
processOozeShield(storage, total_layers);
processDraftShield(storage, total_layers);
processPlatformAdhesion(storage);
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzySkin(mesh);
processSkinsAndInfill(mesh, layer_number);
}
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
}
void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int layer_nr)
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
{
for(SliceMeshStorage& mesh : storage.meshes)
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
for (unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
SliceLayer& layer = mesh.layers[layer_idx];
std::vector<PolygonsPart> new_parts;
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() > 0)
{
sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0);
for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++)
sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x);
}
}
}
else
{ // only send polygon data
SliceLayer* layer = &mesh.layers[layer_nr];
for(SliceLayerPart& part : layer->parts)
{
sendPolygons(Inset0Type, layer_nr, part.outline, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
for (unsigned int other_mesh_idx : mesh_order)
{
for (PolygonRef polyline : layer->openPolyLines)
if (other_mesh_idx == mesh_idx)
{
Polygons segments;
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
break; // all previous meshes have been processed
}
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (layer_idx >= other_mesh.layers.size())
{
continue;
}
SliceLayer& other_layer = other_mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
for (SliceLayerPart& other_part : other_layer.parts)
{
PolygonRef segment = segments.newPoly();
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
if (!part.boundaryBox.hit(other_part.boundaryBox))
{
continue;
}
Polygons& infill = other_part.infill_area;
Polygons new_outline = part.outline.intersection(infill);
if (new_outline.size() == 1)
{
PolygonsPart outline_part_here;
outline_part_here.add(new_outline[0]);
new_parts.push_back(outline_part_here);
}
else if (new_outline.size() > 1)
{
std::vector<PolygonsPart> new_parts_here = new_outline.splitIntoParts();
for (PolygonsPart& new_part_here : new_parts_here)
{
new_parts.push_back(new_part_here);
}
}
infill = infill.difference(part.outline);
other_part.infill_area_per_combine.back() = infill;
}
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
layer.parts.clear();
for (PolygonsPart& part : new_parts)
{
layer.parts.emplace_back();
layer.parts.back().outline = part;
layer.parts.back().boundaryBox.calculate(part);
}
}
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers)
{
// combine infill
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
// fuzzy skin
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzyWalls(mesh);
}
}
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable");
WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall);
walls_computation.generateInsets(layer);
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (PolygonRef polyline : layer->openPolyLines)
{
Polygons segments;
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
{
PolygonRef segment = segments.newPoly();
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
}
}
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers)
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers)
{
int n_empty_first_layers = 0;
for (unsigned int layer_idx = 0; layer_idx < totalLayers; layer_idx++)
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
bool layer_is_empty = true;
for (SliceMeshStorage& mesh : storage.meshes)
@@ -297,50 +423,34 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
layer.printZ -= n_empty_first_layers * layer_height;
}
}
totalLayers -= n_empty_first_layers;
total_layers -= n_empty_first_layers;
}
}
void FffPolygonGenerator::processSkins(SliceDataStorage& storage, unsigned int layer_nr)
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
return;
}
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
int extrusionWidth = mesh.getSettingInMicrons("wall_line_width_x");
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
generateSkins(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
{
int infill_skin_overlap = 0;
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
{
infill_skin_overlap = extrusionWidth / 2;
}
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
if (mesh.getSettingString("fill_perimeter_gaps") == "Skin")
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
}
else if (mesh.getSettingString("fill_perimeter_gaps") == "Everywhere")
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, 0, 0);
}
}
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, extrusionWidth);
}
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int totalLayers)
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
@@ -349,28 +459,28 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
}
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
}
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
for(unsigned int layer_nr=1; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
}
for(unsigned int layer_nr=totalLayers-1; layer_nr>0; layer_nr--)
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
{
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int totalLayers)
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
{
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
@@ -387,7 +497,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
int layer_skip = 500 / layer_height + 1;
Polygons& draft_shield = storage.draft_protection_shield;
for (unsigned int layer_nr = 0; layer_nr < totalLayers && layer_nr < max_screen_layer; layer_nr += layer_skip)
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
@@ -397,37 +507,42 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
{
SettingsBaseVirtual* train = storage.meshgroup->getExtruderTrain(getSettingBoolean("adhesion_extruder_nr"));
switch(getSettingAsPlatformAdhesion("adhesion_type"))
{
case EPlatformAdhesion::SKIRT:
if (getSettingInMicrons("draft_shield_height") == 0)
if (train->getSettingInMicrons("draft_shield_height") == 0)
{ // draft screen replaces skirt
generateSkirt(storage, getSettingInMicrons("skirt_gap"), getSettingAsCount("skirt_line_count"), getSettingInMicrons("skirt_minimal_length"));
generateSkirt(storage, train->getSettingInMicrons("skirt_gap"), train->getSettingAsCount("skirt_line_count"), train->getSettingInMicrons("skirt_minimal_length"));
}
break;
case EPlatformAdhesion::BRIM:
generateSkirt(storage, 0, getSettingAsCount("brim_line_count"), getSettingInMicrons("skirt_minimal_length"));
generateSkirt(storage, 0, train->getSettingAsCount("brim_line_count"), train->getSettingInMicrons("skirt_minimal_length"));
break;
case EPlatformAdhesion::RAFT:
generateRaft(storage, getSettingInMicrons("raft_margin"));
generateRaft(storage, train->getSettingInMicrons("raft_margin"));
break;
}
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
}
void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
{
if (mesh.getSettingAsCount("wall_line_count") == 0)
{
return;
}
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
int64_t range_random_point_dist = avg_dist_between_points / 2;
for (SliceLayer& layer : mesh.layers)
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
{
SliceLayer& layer = mesh.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{
Polygons results;
@@ -447,7 +562,7 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
{
int r = rand() % (fuzziness * 2) - fuzziness;
Point perp_to_p0p1 = crossZ(p0p1);
Point perp_to_p0p1 = turn90CCW(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
result.add(pa);
@@ -476,109 +591,5 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
}
}
void FffPolygonGenerator::bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup)
{
assert(slicerList.size() == meshgroup->meshes.size());
for (unsigned int mesh_idx = 0; mesh_idx < slicerList.size(); mesh_idx++)
{
Slicer* slicer = slicerList[mesh_idx];
Mesh& mesh = meshgroup->meshes[mesh_idx];
if (!mesh.getSettingBoolean("magic_bulge_walls"))
{
// continue; // TODO
}
auto getBulging = [](Point xy, int z)
{
std::hash<int> hash_fn;
int cell_size = MM2INT(0.2);
int cell_dim = 5; // surrounding taken into account
double result = 0.0;
int bulging = MM2INT(10.0);
Point3 middle(xy.X / cell_size, xy.Y / cell_size, z / cell_size);
double total_weight = 0.0;
for (int x = middle.x - cell_dim; x < middle.x + cell_dim; x++)
{
for (int y = middle.y - cell_dim; y < middle.y + cell_dim; y++)
{
for (int z = middle.z - cell_dim; z < middle.z + cell_dim; z++)
{
srand(x ^ (y << 8) ^ (z << 16)); // set seed
int h = rand();
// int h = hash_fn(x ^ (y << 8) ^ (z << 16));
double r = (double(h % 200000 - 100000))/100000.0; // between -1 and 1
double weight = sqrt(1.0 / (1.0 + static_cast<double>(((Point3(xy.X, xy.Y, z) - Point3(x,y,z)* cell_size)).vSize()) * 4));
total_weight += weight;
result += r * weight ;
}
}
}
return static_cast<int>(result / total_weight * bulging);
// return rand() % (bulging*2) - bulging;
};
int64_t avg_dist_between_points = MM2INT(0.5); // mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
int64_t range_random_point_dist = avg_dist_between_points / 2;
int layer_height = mesh.getSettingInMicrons("layer_height");
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SlicerLayer& layer = slicer->layers[layer_nr];
Polygons& outlines = layer.polygonList;
Polygons results;
int z_approx = layer_nr * layer_height;
for (PolygonRef poly : outlines)
{
// generate points in between p0 and p1
PolygonRef result = results.newPoly();
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
Point* p0 = &poly.back();
for (Point& p1 : poly)
{ // 'a' is the (next) new point between p0 and p1
Point p0p1 = p1 - *p0;
int64_t p0p1_size = vSize(p0p1);
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
{
Point in_between = *p0 + normal(p0p1, p0pa_dist);
int r = getBulging(in_between, z_approx);
Point perp_to_p0p1 = crossZ(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = in_between + fuzz;
result.add(pa);
dist_last_point = p0pa_dist;
}
dist_left_over = p0p1_size - dist_last_point;
p0 = &p1;
}
while (result.size() < 3 )
{
unsigned int point_idx = poly.size() - 2;
result.add(poly[point_idx]);
if (point_idx == 0) { break; }
point_idx--;
}
if (result.size() < 3)
{
result.clear();
for (Point& p : poly)
result.add(p);
}
}
outlines = results;
}
}
}
}//namespace cura
+53 -44
Ver Arquivo
@@ -4,16 +4,18 @@
#include "MeshGroup.h"
#include "utils/polygonUtils.h"
#include "utils/NoCopy.h"
#include "utils/gettime.h"
#include "settings.h"
#include "settings/settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
#include "PrintFeature.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
namespace cura
{
class Slicer; // forward declaration
/*!
* Primary stage in Fused Filament Fabrication processing: Polygons are generated.
* The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object.
@@ -23,28 +25,16 @@ class Slicer; // forward declaration
*
* The main function of this class is FffPolygonGenerator::generateAreas().
*/
class FffPolygonGenerator : public SettingsMessenger
class FffPolygonGenerator : public SettingsMessenger, NoCopy
{
private:
CommandSocket* commandSocket;
public:
/*!
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
* Basic constructor
*/
FffPolygonGenerator(SettingsBase* settings_)
: SettingsMessenger(settings_)
, commandSocket(nullptr)
{
}
/*!
* Set the FffAreaGenerator::commandSocket
*/
void setCommandSocket(CommandSocket* socket)
{
commandSocket = socket;
}
/*!
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
@@ -56,17 +46,6 @@ public:
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* Send polygons over the command socket, if there is one.
* \param type The type of polygon to send
* \param layer_nr The layer number at which the polygons occur
* \param polygons The polygons to be sent
*/
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (commandSocket)
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
}
/*!
* Slice the \p object and store the outlines in the \p storage.
@@ -87,42 +66,75 @@ private:
*/
void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);
/*!
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, skin and infill
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_idx The index of the mesh to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed (used for infill meshes)
* \param total_layers The total number of layers over all objects
* \param inset_skin_progress_estimate The progress stage estimate calculator
*/
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_idx The index of the mesh to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed
* \param total_layers The total number of layers over all objects
*/
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_idx, std::vector<unsigned int>& mesh_order, size_t total_layers);
/*!
* Process features which are derived from the basic walls, skin, and infill:
* fuzzy skin, infill combine
*
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers over all objects
*/
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers);
/*!
* Remove all bottom layers which are empty.
*
* \warning Changes \p total_layers
*
* \param storage Input and Ouput parameter: stores all layers
* \param layer_height The height of each layer
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers);
void removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers);
/*!
* Generate the inset polygons which form the walls.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
void processInsets(SliceMeshStorage& mesh, unsigned int layer_nr);
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void processOozeShield(SliceDataStorage& storage, unsigned int totalLayers);
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Generate the skin areas.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the skin areas.
*/
void processSkins(SliceDataStorage& storage, unsigned int layer_nr);
void processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr);
/*!
* Generate the polygons where the draft screen should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void processDraftShield(SliceDataStorage& storage, unsigned int totalLayers);
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Generate the skirt/brim/raft areas/insets.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
@@ -132,15 +144,12 @@ private:
/*!
* Special mode: Make the outer wall 'fuzzy'
* Make the outer wall 'fuzzy'
*/
void processFuzzySkin(SliceMeshStorage& mesh);
void processFuzzyWalls(SliceMeshStorage& mesh);
/*!
* Special mode: bulge the outer walls
*/
void bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup);
};
}//namespace cura
+51 -9
Ver Arquivo
@@ -5,12 +5,26 @@ namespace cura
FffProcessor FffProcessor::instance; // definition must be in cpp
FffProcessor::FffProcessor()
: SettingsBase("global")
, polygon_generator(this)
, gcode_writer(this)
, meshgroup_number(0)
{
}
int FffProcessor::getMeshgroupNr()
{
return meshgroup_number;
}
std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup)
{
std::stringstream sstream;
if (first_meshgroup)
{
sstream << getAllLocalSettingsString(); // global settings
sstream << " -g";
}
else
@@ -26,7 +40,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup.meshes[mesh_idx];
sstream << " -e" << mesh.getSettingAsCount("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
sstream << " -e" << mesh.getSettingAsIndex("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
}
sstream << "\n";
return sstream.str();
@@ -57,23 +71,43 @@ bool FffProcessor::processFiles(const std::vector< std::string >& files)
bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, first_meshgroup).c_str()); }
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, meshgroup_number == 0).c_str()); }
time_keeper.restart();
if (!meshgroup)
return false;
TimeKeeper time_keeper_total;
polygon_generator.setParent(meshgroup);
gcode_writer.setParent(meshgroup);
bool empty = true;
for (Mesh& mesh : meshgroup->meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
{
empty = false;
}
}
if (empty)
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
return true;
}
if (meshgroup->getSettingBoolean("wireframe_enabled"))
{
log("starting Neith Weaver...\n");
Weaver w(this);
w.weave(meshgroup, command_socket);
w.weave(meshgroup);
log("starting Neith Gcode generation...\n");
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
gcoder.writeGCode(command_socket);
gcoder.writeGCode();
log("finished Neith Gcode generation...\n");
} else
@@ -84,17 +118,25 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
return false;
}
gcode_writer.setCommandSocket(command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
gcode_writer.writeGCode(storage, time_keeper);
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->sendLayerData();
}
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
first_meshgroup = false;
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
meshgroup_number++;
polygon_generator.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
gcode_writer.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
return true;
}
+108 -32
Ver Arquivo
@@ -1,13 +1,13 @@
#ifndef FFF_PROCESSOR_H
#define FFF_PROCESSOR_H
#include "settings.h"
#include "settings/settings.h"
#include "FffGcodeWriter.h"
#include "FffPolygonGenerator.h"
#include "commandSocket.h"
#include "Weaver.h"
#include "Wireframe2gcode.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "utils/gettime.h"
#include "utils/NoCopy.h"
@@ -19,76 +19,152 @@ namespace cura {
class FffProcessor : public SettingsBase , NoCopy
{
private:
/*!
* The FffProcessor used for the (current) slicing (The instance of this singleton)
*/
static FffProcessor instance;
FffProcessor()
: polygon_generator(this)
, gcode_writer(this)
, first_meshgroup(true)
{
command_socket = NULL;
}
FffProcessor();
public:
/*!
* Get the instance
* \return The instance
*/
static FffProcessor* getInstance()
{
return &instance;
}
/*!
* Get the index of the meshgroup currently being processed, starting at zero.
*/
int getMeshgroupNr();
private:
/*!
* The polygon generator, which slices the models and generates all polygons to be printed and areas to be filled.
*/
FffPolygonGenerator polygon_generator;
/*!
* The gcode writer, which generates paths in layer plans in a buffer, which converts these paths into gcode commands.
*/
FffGcodeWriter gcode_writer;
CommandSocket* command_socket;
bool first_meshgroup;
/*!
* The index of the meshgroup currently being processed, starting at zero.
*/
int meshgroup_number;
/*!
* A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* Used in debugging.
*/
std::string profile_string = "";
/*!
* Get all settings for the current meshgroup in the format by which CuraEngine is called via the command line.
*
* Also includes all global settings if this is the first meshgroup.
*
* Used in debugging.
*
* \param meshgroup The meshgroup for which to stringify all settings
* \param first_meshgroup Whether this is the first meshgroup and all global settigns should be included as well
*/
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
public:
/*!
* Get a string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* \return A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*/
std::string getProfileString() { return profile_string; }
/*!
* The stop watch used to time how long the different stages take to compute.
*/
TimeKeeper time_keeper; // TODO: use singleton time keeper
void resetFileNumber()
/*!
* Reset the meshgroup number to the first meshgroup to start a new slicing.
*/
void resetMeshGroupNumber()
{
gcode_writer.resetFileNumber();
meshgroup_number = 0;
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
gcode_writer.setCommandSocket(socket);
polygon_generator.setCommandSocket(socket);
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
return gcode_writer.setTargetStream(stream);
}
double getTotalFilamentUsed(int e)
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode_writer.getTotalFilamentUsed(e);
return gcode_writer.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode_writer.getTotalPrintTime();
}
/*!
* Add the end gcode and set all temperatures to zero.
*/
void finalize()
{
gcode_writer.finalize();
}
/*!
* Process all files into one meshgroup
*
* \warning Unused!
*/
bool processFiles(const std::vector<std::string> &files);
/*!
* Generate gcode for a given \p meshgroup
* The primary function of this class.
*
* \param meshgroup The meshgroup for which to generate gcode
* \return Whether this function succeeded
*/
bool processMeshGroup(MeshGroup* meshgroup);
};
+77
Ver Arquivo
@@ -0,0 +1,77 @@
#ifndef FLOW_TEMP_GRAPH
#define FLOW_TEMP_GRAPH
#include <cassert>
#include "utils/logoutput.h"
namespace cura
{
/*!
* Class representing a graph matching a flow to a temperature.
* The graph generally consists of several linear line segments between points at which the temperature and flow are matched.
*/
class FlowTempGraph
{
public:
struct Datum
{
double flow; //!< The flow in mm^3/s
double temp; //!< The temperature in *C
Datum(double flow, double temp)
: flow(flow)
, temp(temp)
{}
};
std::vector<Datum> data; //!< The points of the graph between which the graph is linearly interpolated
FlowTempGraph()
{}
/*!
* Get the temperature corresponding to a specific flow.
*
* For flows outside of the chart, the temperature at the minimal or maximal flow is returned.
* When the graph is empty, the @p material_print_temperature is returned.
*
* \param flow the flow in mm^3/s
* \param material_print_temperature The default printing temp (backward compatibility for when the graph fails)
* \return the corresponding temp
*/
double getTemp(double flow, double material_print_temperature, bool flow_dependent_temperature)
{
if (!flow_dependent_temperature || data.size() == 0)
{
return material_print_temperature;
}
if (data.size() == 1)
{
return data.front().temp;
}
if (flow < data.front().flow)
{
logWarning("Warning! Flow too low!\n"); // TODO
return data.front().temp;
}
Datum* last_datum = &data.front();
for (unsigned int datum_idx = 1; datum_idx < data.size(); datum_idx++)
{
Datum& datum = data[datum_idx];
if (datum.flow >= flow)
{
return last_datum->temp + (datum.temp - last_datum->temp) * (flow - last_datum->flow) / (datum.flow - last_datum->flow);
}
last_datum = &datum;
}
logWarning("Warning! Flow too high!\n"); // TODO
return data.back().temp;
};
};
} // namespace cura
#endif // FLOW_TEMP_GRAPH
+242
Ver Arquivo
@@ -0,0 +1,242 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "FffProcessor.h"
namespace cura {
void LayerPlanBuffer::flush()
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the very last layer
}
while (!buffer.empty())
{
buffer.front().writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
}
}
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
{
double acc_time = 0.0;
for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
{
GCodePath& path = extruder_plan_before.paths[path_idx];
acc_time += path.estimates.getTotalTime();
if (acc_time > time_after_extruder_plan_start)
{
// logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
return;
}
}
extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
// = special insert after all extruder plans
}
double LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
bool first_it = true;
double in_between_time = 0.0;
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
{
GCodePlanner& layer = *layers[layer_idx];
if (!first_it)
{
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
}
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
if (extruder_plan.extruder == extruder)
{
return preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
}
in_between_time += extruder_plan.estimates.getTotalTime();
}
first_it = false;
}
// The last extruder plan with the same extruder falls outside of the buffer
// assume the nozzle has cooled down to strandby temperature already.
return preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(preheat_config.getStandbyTemp(extruder), extruder, required_temp, false);
}
void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
{
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
if (time_after_extruder_plan_start < 0)
{
time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
}
insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
}
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
extruder_plan.insertCommand(0, extruder, required_temp, true); // just after the extruder switch, wait for the destination temperature to be reached
double time_before_extruder_plan_to_insert = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
bool first_it = true; // Whether it's the first iteration of the for loop below
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
{
GCodePlanner& layer = *layers[layer_idx];
if (!first_it)
{
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
}
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
assert (extruder_plan_before.extruder != extruder);
double time_here = extruder_plan_before.estimates.getTotalTime();
if (time_here >= time_before_extruder_plan_to_insert)
{
insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
return;
}
time_before_extruder_plan_to_insert -= time_here;
}
first_it = false;
}
// time_before_extruder_plan_to_insert falls before all plans in the buffer
ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
}
void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
ExtruderPlan* prev_extruder_plan = nullptr;
if (extruder_plan_idx == 0)
{
if (layer_plan_idx == 0)
{ // the very first extruder plan of the current meshgroup
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
{ // set temperature of the first nozzle, turn other nozzles down
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{
// override values from GCodeExport::setInitialTemps
// the first used extruder should be set to the required temp in the start gcode
// see FffGcodeWriter::processStartingCode
if (extruder_idx == extruder)
{
gcode.setInitialTemp(extruder_idx, required_temp);
}
else
{
gcode.setInitialTemp(extruder_idx, preheat_config.getStandbyTemp(extruder_idx));
}
}
else
{
if (extruder_idx == extruder)
{
extruder_plan.insertCommand(0, extruder, required_temp, true);
}
else
{
extruder_plan.insertCommand(0, extruder_idx, preheat_config.getStandbyTemp(extruder_idx), false);
}
}
}
return;
}
prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
}
else
{
prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
}
assert(prev_extruder_plan != nullptr);
int prev_extruder = prev_extruder_plan->extruder;
if (prev_extruder != extruder)
{ // set previous extruder to standby temperature
prev_extruder_plan->insertCommand(prev_extruder_plan->paths.size(), prev_extruder, preheat_config.getStandbyTemp(prev_extruder), false);
}
if (prev_extruder == extruder)
{
if (preheat_config.usesFlowDependentTemp(extruder))
{
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
}
}
else
{
insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
}
}
void LayerPlanBuffer::insertPreheatCommands()
{
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
{ // disregard empty layer
buffer.pop_back();
return;
}
std::vector<GCodePlanner*> layers;
layers.reserve(buffer.size());
for (GCodePlanner& layer_plan : buffer)
{
layers.push_back(&layer_plan);
}
unsigned int layer_idx = layers.size() - 1;
// insert commands for all extruder plans on this layer
GCodePlanner& layer_plan = *layers[layer_idx];
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
{
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
double time = extruder_plan.estimates.getTotalUnretractedTime();
if (time <= 0.0
|| extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
)
{
continue;
}
double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);
insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
}
}
} // namespace cura
+133
Ver Arquivo
@@ -0,0 +1,133 @@
#ifndef LAYER_PLAN_BUFFER_H
#define LAYER_PLAN_BUFFER_H
#include <list>
#include "settings/settings.h"
#include "commandSocket.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "Preheat.h"
namespace cura
{
class LayerPlanBuffer : SettingsMessenger
{
GCodeExport& gcode;
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
static constexpr unsigned int buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value
// this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted.
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
: SettingsMessenger(settings)
, gcode(gcode)
{ }
void setPreheatConfig(MeshGroup& settings)
{
preheat_config.setConfig(settings);
}
/*!
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
* Pop back the oldest layer plan is it exceeds the buffer size and write it to gcode.
*/
template<typename... Args>
GCodePlanner& emplace_back(Args&&... constructor_args)
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
}
buffer.emplace_back(constructor_args...);
if (buffer.size() > buffer_size)
{
buffer.front().writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
}
return buffer.back();
}
/*!
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
*/
void flush();
/*!
* Insert the preheat command for @p extruder into @p extruder_plan_before
*
* \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command
* \param time_after_extruder_plan_start The time after the start of the extruder plan, before which to insert the preheat command
* \param extruder The extruder for which to set the temperature
* \param temp The temperature of the preheat command
*/
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp);
/*!
* Compute the time needed to preheat, based either on the time the extruder has been on standby
* or based on the temp of the previous extruder plan which has the same extruder nr.
*
* \param layers The layers in the buffer, moved to a vector
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
* \return the time needed to preheat
*/
double timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
/*!
* For two consecutive extruder plans of the same extruder (so on different layers),
* preheat the extruder to the temperature corresponding to the average flow of the second extruder plan.
*
* The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer.
*
* \param prev_extruder_plan The former extruder plan (of the former layer)
* \param extruder The extruder for which too set the temperature
* \param required_temp The required temperature for the second extruder plan
*/
void insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp);
/*!
* Insert the preheat command for an extruder plan which is preceded by an extruder plan with a different extruder.
* Find the time window in which this extruder hasn't been used
* and compute at what time the preheat command needs to be inserted.
* Then insert the preheat command in the right extruder plan.
*
* \param layers The layers in the buffer, moved to a vector
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
*/
void insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
/*!
* Insert the preheat command for the extruder plan corersponding to @p extruder_plan_idx of the layer corresponding to @p layer_plan_idx.
*
* \param layers The layers of the buffer, moved to a temporary vector (from lower to upper layers)
* \param layer_plan_idx The index of the layer plan for which to generate a preheat command
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to generate the preheat command
*/
void insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
/*!
* Insert the preheat commands for the last added layer (unless that layer was empty)
*/
void insertPreheatCommands();
};
} // namespace cura
#endif // LAYER_PLAN_BUFFER_H
+119 -43
Ver Arquivo
@@ -2,6 +2,8 @@
#include <algorithm> // min
#include "utils/linearAlg2D.h"
namespace cura
{
@@ -12,6 +14,7 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
double speed_mod = old_line_width / new_line_width_mm;
double extrusion_mod = new_line_width_mm / old_line_width;
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
@@ -20,10 +23,8 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
Point prev_middle;
Point last_middle;
int64_t line_width;
MergeInfillLines merger(gcode, paths, travelConfig, nozzle_size);
if (merger.isConvertible(path_idx, prev_middle, last_middle, line_width, false))
if (isConvertible(path_idx, prev_middle, last_middle, line_width, false))
{
// path_idx + 3 is the index of the second extrusion move to be converted in combination with the first
{
@@ -39,75 +40,150 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
}
path_idx += 2;
for (; merger.isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
extruder_plan.handleInserts(path_idx, gcode);
for (; isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, speed, last_path, line_width);
}
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
extruder_plan.handleInserts(path_idx, gcode);
return true;
}
return false;
};
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
int64_t max_line_width = nozzle_size * 3 / 2;
unsigned int idx = path_idx_first_move;
if (idx + 3 > paths.size()-1) return false;
if (paths[idx+0].config != &travelConfig) return false;
if (paths[idx+1].points.size() > 1) return false;
if (paths[idx+1].config == &travelConfig) return false;
// if (paths[idx+2].points.size() > 1) return false;
if (paths[idx+2].config != &travelConfig) return false;
if (paths[idx+3].points.size() > 1) return false;
if (paths[idx+3].config == &travelConfig) return false;
if (idx + 3 > paths.size()-1)
{
return false;
}
if ( paths[idx+0].config != &travelConfig // must be travel
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|| paths[idx+1].config == &travelConfig // must be extrusion
// || paths[idx+2].points.size() > 1 // travel must be direct
|| paths[idx+2].config != &travelConfig // must be travel
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|| paths[idx+3].config == &travelConfig // must be extrusion
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
)
{
return false;
}
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
return false;
}
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
{ // both extrusion moves must be of lines space filling type!
return false;
}
int64_t line_width = paths[idx+1].config->getLineWidth();
Point& a = paths[idx+0].points.back(); // first extruded line from
Point& b = paths[idx+1].points.back(); // first extruded line to
Point& c = paths[idx+2].points.back(); // second extruded line from
Point& d = paths[idx+3].points.back(); // second extruded line to
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
}
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
use_second_middle_as_first = false;
int64_t max_line_width = nozzle_size * 3 / 2;
Point ab = b - a;
Point cd = d - c;
if (b == c)
{
return false; // the line segments are connected!
}
int64_t ab_size = vSize(ab);
int64_t cd_size = vSize(cd);
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
{
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
}
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
{
return false; // extrusion moves not in the same or opposite diraction
if (prod < 0) { ab = ab * -1; }
}
Point infill_vector = (cd + ab) / 2;
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
// make lines in the same direction by flipping one
if (prod < 0)
{
ab = ab * -1;
}
else if (prod == 0)
{
return false; // lines are orthogonal!
}
else if (b == d || a == c)
{
return false; // the line segments are connected!
}
first_middle = (use_second_middle_as_first)?
second_middle :
(a + b) / 2;
second_middle = (c + d) / 2;
Point dir_vector_perp = crossZ(second_middle - first_middle);
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0) return false;
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (line_width > max_line_width) return false; // combined lines would be too wide
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
{ // check whether the two lines are adjacent
Point ca = first_middle - c;
double ca_size = vSizeMM(ca);
double cd_size = vSizeMM(cd);
double prod = INT2MM(dot(ca, cd));
double fraction = prod / ( ca_size * cd_size );
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
if (dir_vector_perp_length == 0)
{
return false;
}
if (dir_vector_perp_length > 5 * nozzle_size)
{
return false; // infill lines too far apart
}
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
// compute the resulting line width
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (resulting_line_width > max_line_width)
{
return false; // combined lines would be too wide
}
if (resulting_line_width == 0)
{
return false; // dot is zero, so lines are in each others extension, not next to eachother
}
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
Point ac = c - first_middle;
Point infill_vector_perp = turn90CCW(infill_vector);
int64_t perp_proj = dot(ac, infill_vector_perp);
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
{
return false; // lines are too far apart or too close together
}
// check whether the two line segments are adjacent.
// full infill in a narrow area might result in line segments with arbitrary distance between them
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
{
return false;
}
return true;
};
+42 -7
Ver Arquivo
@@ -12,22 +12,42 @@ class MergeInfillLines
{
// void merge(Point& from, Point& p0, Point& p1);
GCodeExport& gcode; //!< Where to write the combined line to
int layer_nr; //!< The current layer number
std::vector<GCodePath>& paths; //!< The paths currently under consideration
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
*
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
*
* \param a first from
* \param b first to
* \param c second from
* \param d second to
* \param line_width The line width of the moves
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
*
@@ -41,8 +61,8 @@ public:
/*!
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
*/
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), paths(paths), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
/*!
* Check for lots of small moves and combine them into one large line.
@@ -58,6 +78,21 @@ public:
*/
bool mergeInfillLines(double speed, unsigned int& path_idx);
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
}
};
}//namespace cura
+162 -19
Ver Arquivo
@@ -28,7 +28,120 @@ void* fgets_(char* ptr, size_t len, FILE* f)
return nullptr;
}
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
MeshGroup::MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base, std::string("meshgroup"))
, extruder_count(-1)
{}
MeshGroup::~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
int MeshGroup::getExtruderCount()
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
}
return extruders[extruder_nr];
}
ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr)
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
const ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr) const
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
Point3 MeshGroup::min() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 MeshGroup::max() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void MeshGroup::clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void MeshGroup::finalize()
{
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rt");
char buffer[1024];
@@ -61,29 +174,41 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return true;
}
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
fseek(f, 0L, SEEK_END);
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
rewind(f); //Seek back to start.
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return false;
}
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
uint32_t reported_face_count;
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return false;
}
if (reported_face_count != face_count)
{
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
mesh->faces.reserve(faceCount);
mesh->vertices.reserve(faceCount);
for(unsigned int i=0;i<faceCount;i++)
mesh->faces.reserve(face_count);
mesh->vertices.reserve(face_count);
for (unsigned int i = 0; i < face_count; i++)
{
if (fread(buffer, 50, 1, f) != 1)
{
@@ -102,13 +227,34 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return true;
}
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
if (f == nullptr)
{
return false;
}
//Skip any whitespace at the beginning of the file.
unsigned long long num_whitespace = 0; //Number of whitespace characters.
unsigned char whitespace;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
while(isspace(whitespace))
{
num_whitespace++;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
}
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
char buffer[6];
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
@@ -135,22 +281,19 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return loadMeshSTL_binary(mesh, filename, matrix);
}
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
{
const char* ext = strrchr(filename, '.');
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
{
if (object_parent_settings)
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
{
meshgroup->meshes.emplace_back(object_parent_settings); // make new mesh with [object_parent_settings] as parent settings object
meshgroup->meshes.push_back(mesh);
return true;
}
else
{
meshgroup->meshes.emplace_back(meshgroup); // make new mesh with [meshgroup] as parent settings object
}
return loadMeshSTL(&meshgroup->meshes[meshgroup->meshes.size()-1], filename, transformation);
}
return false;
}
}//namespace cura
}//namespace cura
+19 -96
Ver Arquivo
@@ -2,6 +2,7 @@
#ifndef MESH_GROUP_H
#define MESH_GROUP_H
#include "utils/NoCopy.h"
#include "mesh.h"
#include "ExtruderTrain.h"
@@ -14,112 +15,34 @@ namespace cura
* One MeshGroup is a whole which is printed at once.
* Generally there is one single MeshGroup, though when using one-at-a-time printing, multiple MeshGroups are processed consecutively.
*/
class MeshGroup : public SettingsBase
class MeshGroup : public SettingsBase, NoCopy
{
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
int extruder_count;
public:
int getExtruderCount()
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
int getExtruderCount();
MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
{}
MeshGroup(SettingsBaseVirtual* settings_base);
~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
}
return extruders[extruder_nr];
}
~MeshGroup();
/*!
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
*/
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr);
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr);
const ExtruderTrain* getExtruderTrain(unsigned int extruder_nr) const;
std::vector<Mesh> meshes;
Point3 min() //! minimal corner of bounding box
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 max() //! maximal corner of bounding box
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
Point3 min() const; //! minimal corner of bounding box
Point3 max() const; //! maximal corner of bounding box
void clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void clear();
void finalize()
{
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
void finalize();
};
/*!
@@ -131,7 +54,7 @@ public:
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
* \return whether the file could be loaded
*/
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
}//namespace cura
#endif//MESH_GROUP_H
+178
Ver Arquivo
@@ -0,0 +1,178 @@
#ifndef PREHEAT_H
#define PREHEAT_H
#include <cassert>
#include <algorithm> // max
#include "utils/logoutput.h"
#include "MeshGroup.h"
#include "FlowTempGraph.h"
namespace cura
{
/*!
* Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature.
*/
class Preheat
{
/*!
* The nozzle and material temperature settings for an extruder train.
*/
class Config
{
public:
double time_to_heatup_1_degree; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature)
double time_to_cooldown_1_degree; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature)
double heatup_cooldown_time_mod_while_printing; //!< The time to be added to Preheat::time_to_heatup_1_degree and subtracted from Preheat::time_to_cooldown_1_degree to get the timings while printing
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
double material_print_temperature; //!< default print temp (backward compatilibily)
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
};
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
public:
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
* \return the standby temp
*/
double getStandbyTemp(int extruder)
{
return config_per_extruder[extruder].standby_temp;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
*/
void setConfig(MeshGroup& settings)
{
for (int extruder_nr = 0; extruder_nr < settings.getExtruderCount(); extruder_nr++)
{
assert(settings.getExtruderTrain(extruder_nr) != nullptr);
ExtruderTrain& extruder_train = *settings.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
config.time_to_cooldown_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed"); // 0.5
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature"); // 220
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
}
}
bool usesFlowDependentTemp(int extruder_nr)
{
return config_per_extruder[extruder_nr].flow_dependent_temperature;
}
private:
/*!
* Calculate time to heat up from standby temperature to a given temperature.
* Assumes @p temp is higher than the standby temperature.
*
* \param extruder The extruder for which to get the time
* \param temp The temperature to be reached
*/
double timeToHeatFromStandbyToPrintTemp(unsigned int extruder, double temp)
{
return (temp - config_per_extruder[extruder].standby_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
public:
/*!
* Get the optimal temperature corresponding to a given average flow.
* \param extruder The extruder train
* \param flow The flow for which to get the optimal temperature
* \return The corresponding optimal temperature
*/
double getTemp(unsigned int extruder, double flow)
{
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
/*!
* Decide when to start warming up again after starting to cool down towards the standby temperature.
* Two cases are considered:
* the case where the standby temperature is reached \__/ .
* and the case where it isn't \/ .
*
* IT is assumed that the printer is not printing during this cool down and warm up time.
*
* Assumes from_temp is approximately the same as @p temp
*
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \return The time before the end of the @p time_window to insert the preheat command
*/
double timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
{
double time_ratio_cooldown_heatup = config_per_extruder[extruder].time_to_cooldown_1_degree / config_per_extruder[extruder].time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = timeToHeatFromStandbyToPrintTemp(extruder, temp);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_standby_temp < time_window)
{
return time_to_heat_from_standby_to_print_temp;
}
else
{
return time_window * config_per_extruder[extruder].time_to_heatup_1_degree / (config_per_extruder[extruder].time_to_cooldown_1_degree + config_per_extruder[extruder].time_to_heatup_1_degree);
}
}
/*!
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
* If the printer is printing in the mean time the warming up will take longer.
*
*
* \param from_temp The temperature at which the nozzle was before
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \param printing Whether the printer is printing in the time to heat up the nozzle
* \return The time needed to reach the desired temperature (@p temp)
*/
double timeBeforeEndToInsertPreheatCommand_warmUp(double from_temp, unsigned int extruder, double temp, bool printing)
{
if (temp > from_temp)
{
if (printing)
{
return (temp - from_temp) * (config_per_extruder[extruder].time_to_heatup_1_degree + config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
else
{
return (temp - from_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
}
else
{
if (printing)
{
return (from_temp - temp) * config_per_extruder[extruder].time_to_cooldown_1_degree;
}
else
{
return (from_temp - temp) * std::max(0.0, config_per_extruder[extruder].time_to_cooldown_1_degree - config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
}
}
};
} // namespace cura
#endif // PREHEAT_H
+49 -31
Ver Arquivo
@@ -5,6 +5,7 @@
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "infill.h"
#include "PrintFeature.h"
namespace cura
{
@@ -16,20 +17,29 @@ PrimeTower::PrimeTower()
void PrimeTower::setConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder, int layer_thickness)
void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder)
{
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
for (int extr = 0; extr < extruder_count; extr++)
{
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
}
}
void PrimeTower::setConfigs(MeshGroup* meshgroup, int layer_thickness)
{
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
for (int extr = 0; extr < extruder_count; extr++)
{
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "WALL-INNER");// so that visualization in the old Cura still works (TODO)
GCodePathConfig& conf = config_per_extruder.back();
conf.setSpeed(train->getSettingInMillimetersPerSecond("speed_prime_tower"));
conf.setLineWidth(train->getSettingInMicrons("prime_tower_line_width"));
conf.setFlow(train->getSettingInPercentage("prime_tower_flow"));
GCodePathConfig& conf = config_per_extruder[extr];
conf.setLayerHeight(layer_thickness);
}
}
@@ -41,18 +51,19 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
extruder_count = storage.getSettingAsCount("machine_extruder_count");
int max_object_height_per_extruder[extruder_count];
int max_object_height_per_extruder[extruder_count];
std::fill_n(max_object_height_per_extruder, extruder_count, -1); // unitialize all as -1
{ // compute max_object_height_per_extruder
memset(max_object_height_per_extruder, -1, sizeof(max_object_height_per_extruder));
for (SliceMeshStorage& mesh : storage.meshes)
{
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_object_height_per_extruder[extr_nr] =
std::max( max_object_height_per_extruder[extr_nr]
, mesh.layer_nr_max_filled_layer );
}
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_extruder_nr] =
std::max( max_object_height_per_extruder[support_extruder_nr]
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_infill_extruder_nr] =
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
max_object_height_per_extruder[support_roof_extruder_nr] =
@@ -72,7 +83,7 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
if (extruder_nr == extruder_max_object_height) { continue; }
if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
if (extruder_second_max_object_height == -1 || max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
{
extruder_second_max_object_height = extruder_nr;
}
@@ -93,7 +104,7 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
@@ -104,23 +115,21 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLayers)
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
&& storage.getSettingInMicrons("prime_tower_size") > 0)
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
{
generatePaths3(storage);
}
}
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers)
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
@@ -159,7 +168,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
double infill_overlap = 15; // so that it can't be zero
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
generateGroundpoly(storage);
@@ -170,7 +179,13 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
std::vector<Polygons>& patterns = patterns_per_extruder.back();
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
Polygons result_polygons; // should remain empty, since we generate lines pattern!
int outline_offset = -line_width/2;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle);
infill_comp.generate(result_polygons, result_lines);
}
}
}
@@ -179,9 +194,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
{
return;
}
@@ -218,10 +231,15 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
}
if (wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
+5 -4
Ver Arquivo
@@ -26,7 +26,8 @@ private:
};
public:
void setConfigs(MeshGroup* configs, std::vector<RetractionConfig>& retraction_config_per_extruder, int layer_thickness);
void initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder);
void setConfigs(MeshGroup* configs, int layer_thickness);
Polygons ground_poly;
@@ -44,10 +45,10 @@ public:
* Generate the area where the prime tower should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void generatePaths(SliceDataStorage& storage, unsigned int totalLayers);
void generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers);
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
void computePrimeTowerMax(SliceDataStorage& storage);
+11 -12
Ver Arquivo
@@ -4,24 +4,23 @@
namespace cura
{
enum class EPrintFeature : unsigned int
enum class PrintFeatureType
{
OUTER_WALL,
INNER_WALLS,
INFILL,
SKIN,
HELPERS,
UNCLASSIFIED,
ENUM_COUNT
NoneType, // unused, but libArcus depends on it
OuterWall,
InnerWall,
Skin,
Support,
Skirt,
Infill,
SupportInfill,
MoveCombing,
MoveRetraction
};
} // namespace cura
#endif // PRINT_FEATURE
+25
Ver Arquivo
@@ -0,0 +1,25 @@
#ifndef SPACE_FILL_TYPE
#define SPACE_FILL_TYPE
namespace cura
{
/*!
* Enum class enumerating the strategies with which an area can be occupied with filament
*
* The walls/perimeters are Polygons
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
* Grid, Triangles and lines infill is Lines
*/
enum class SpaceFillType
{
None,
Polygons,
PolyLines,
Lines
};
} // namespace cura
#endif // SPACE_FILL_TYPE
+79
Ver Arquivo
@@ -0,0 +1,79 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "WallsComputation.h"
#include "utils/polygonUtils.h"
namespace cura {
WallsComputation::WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall)
: wall_0_inset(wall_0_inset)
, line_width_0(line_width_0)
, line_width_x(line_width_x)
, insetCount(insetCount)
, recompute_outline_based_on_outer_wall(recompute_outline_based_on_outer_wall)
{
}
void WallsComputation::generateInsets(SliceLayerPart* part)
{
if (insetCount == 0)
{
part->insets.push_back(part->outline);
part->print_outline = part->outline;
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
part->insets[0] = part->outline.offset(-line_width_0 / 2 - wall_0_inset);
} else if (i == 1)
{
part->insets[1] = part->insets[0].offset(-line_width_0 / 2 + wall_0_inset - line_width_x / 2);
} else
{
part->insets[i] = part->insets[i-1].offset(-line_width_x);
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
if (i == 0)
{
if (recompute_outline_based_on_outer_wall)
{
part->print_outline = part->insets[0].offset(line_width_0 / 2);
}
else
{
part->print_outline = part->outline;
}
}
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void WallsComputation::generateInsets(SliceLayer* layer)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr]);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
+69
Ver Arquivo
@@ -0,0 +1,69 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Function container for computing the outer walls / insets / perimeters polygons of a layer
*/
class WallsComputation
{
public:
/*!
* The offset applied to the outer wall
*/
int wall_0_inset;
/*!
* line width of the outer wall
*/
int line_width_0;
/*!
* line width of other walls
*/
int line_width_x;
/*!
* The number of insets to to generate
*/
int insetCount;
/*!
* Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
bool recompute_outline_based_on_outer_wall;
/*!
* Basic constructor initializing the parameters with which to perform the walls computation
*
* \param wall_0_inset The offset applied to the outer wall
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param recompute_outline_based_on_outer_wall Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by WallsComputation::line_width_0 instead of the first,
* which leads to better results for a smaller WallsComputation::line_width_0 than WallsComputation::line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
*/
void generateInsets(SliceLayer* layer);
private:
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
*/
void generateInsets(SliceLayerPart* part);
};
}//namespace cura
#endif//INSET_H
+35 -22
Ver Arquivo
@@ -4,13 +4,14 @@
#include <fstream> // debug IO
#include <unistd.h>
#include "Progress.h"
#include "progress/Progress.h"
#include "weaveDataStorage.h"
#include "PrintFeature.h"
namespace cura
{
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
void Weaver::weave(MeshGroup* meshgroup)
{
wireFrame.meshgroup = meshgroup;
@@ -28,14 +29,13 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
slicerList.push_back(slicer);
}
int starting_layer_idx;
{ // find first non-empty layer
for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
{
Polygons parts;
for (cura::Slicer* slicer : slicerList)
parts.add(slicer->layers[starting_layer_idx].polygonList);
parts.add(slicer->layers[starting_layer_idx].polygons);
if (parts.size() > 0)
break;
@@ -51,12 +51,19 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{
int starting_z = -1;
for (cura::Slicer* slicer : slicerList)
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
if (slicerList.empty()) //Wait, there is nothing to slice.
{
wireFrame.z_bottom = 0;
}
else
{
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
}
Point starting_point_in_layer;
if (wireFrame.bottom_outline.size() > 0)
@@ -64,23 +71,24 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
else
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
parts1.add(slicer->layers[layer_idx].polygonList);
parts1.add(slicer->layers[layer_idx].polygons);
Polygons chainified;
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
}
if (chainified.size() > 0)
{
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
@@ -101,10 +109,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
@@ -136,16 +144,21 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{ // roofs:
WeaveLayer& top_layer = wireFrame.layers.back();
Polygons to_be_supported; // empty for the top layer
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
{
WeaveLayer& top_layer = wireFrame.layers.back();
Polygons to_be_supported; // empty for the top layer
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
}
}
{ // bottom:
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
{
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
}
}
}
+4 -4
Ver Arquivo
@@ -3,11 +3,12 @@
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings.h"
#include "settings/settings.h"
#include "MeshGroup.h"
#include "slicer.h"
#include "utils/NoCopy.h"
#include "utils/polygon.h"
#include "utils/polygonUtils.h"
@@ -19,7 +20,7 @@ namespace cura
/*!
* The main weaver / WirePrint / wireframe printing class, which computes the basic paths to be followed.
*/
class Weaver : public SettingsMessenger
class Weaver : public SettingsMessenger, NoCopy
{
friend class Wireframe2gcode;
private:
@@ -60,9 +61,8 @@ public:
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
*
* \param objects The objects for which to create a wireframe print
* \param commandSocket the commandSocket
*/
void weave(MeshGroup* objects, CommandSocket* commandSocket);
void weave(MeshGroup* objects);
private:
+65 -45
Ver Arquivo
@@ -4,7 +4,7 @@
#include <fstream> // debug IO
#include "weaveDataStorage.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "pathOrderOptimizer.h" // for skirt
@@ -12,22 +12,32 @@ namespace cura
{
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
void Wireframe2gcode::writeGCode()
{
gcode.preSetup(wireFrame.meshgroup);
if (commandSocket)
commandSocket->beginGCode();
gcode.setInitialTemps(wireFrame.meshgroup);
processStartingCode(commandSocket);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
int maxObjectHeight = wireFrame.layers.back().z1;
processStartingCode();
processSkirt(commandSocket);
int maxObjectHeight;
if (wireFrame.layers.empty())
{
maxObjectHeight = 0;
}
else
{
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt();
unsigned int totalLayers = wireFrame.layers.size();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment("SKIRT");
@@ -68,10 +78,10 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, totalLayers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_nr];
@@ -157,13 +167,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
gcode.writeFanCommand(0);
finalize(maxObjectHeight);
if (commandSocket)
{
commandSocket->sendGCodeLayer();
commandSocket->endSendSlicedObject();
}
finalize();
}
@@ -231,11 +235,14 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
RetractionConfig retraction_config;
// TODO: get these from the settings!
retraction_config.amount = 500; //INT2MM(getSettingInt("retraction_amount"))
retraction_config.primeAmount = 0;//INT2MM(getSettingInt("retractionPrime
retraction_config.distance = 500; //INT2MM(getSettingInt("retraction_amount"))
retraction_config.prime_volume = 0;//INT2MM(getSettingInt("retractionPrime
retraction_config.speed = 20; // 40;
retraction_config.primeSpeed = 15; // 30;
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
double top_retract_pause = 2.0;
int retract_hop_dist = 1000;
@@ -473,8 +480,8 @@ void Wireframe2gcode::writeMoveWithRetract(Point to)
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
: SettingsMessenger(settings_base)
, gcode(gcode)
, wireFrame(weaver.wireFrame)
{
wireFrame = weaver.wireFrame;
initial_layer_thickness = getSettingInMicrons("layer_height_0");
connectionHeight = getSettingInMicrons("wireframe_height");
roof_inset = getSettingInMicrons("wireframe_roof_inset");
@@ -529,33 +536,42 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
standard_retraction_config.amount = INT2MM(getSettingInMicrons("retraction_amount"));
standard_retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
standard_retraction_config.distance = getSettingInMillimeters("retraction_amount");
standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
standard_retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
void Wireframe2gcode::processStartingCode()
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
if (!CommandSocket::isInstantiated())
{
if (!command_socket)
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
}
gcode.writeCode(gcode.getFileHeader().c_str());
}
else
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
if (getSettingBoolean("material_bed_temp_prepend"))
{
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
{
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
}
}
if (getSettingBoolean("material_print_temp_prepend"))
{
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
{
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
if (getSettingBoolean("machine_print_temp_wait"))
{
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
}
}
}
}
@@ -572,30 +588,34 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
}
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
void Wireframe2gcode::processSkirt()
{
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
{
return;
}
Polygons skirt = wireFrame.bottom_outline.offset(100000+5000).offset(-100000);
PathOrderOptimizer order(gcode.getStartPositionXY());
PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN));
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
{
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
PolygonRef poly = skirt[actual_poly_idx];
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
unsigned int poly_idx = order.polyOrder[poly_order_idx];
PolygonRef poly = skirt[poly_idx];
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
}
}
}
void Wireframe2gcode::finalize(int maxObjectHeight)
void Wireframe2gcode::finalize()
{
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
gcode.finalize(getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
}
+9 -7
Ver Arquivo
@@ -4,9 +4,11 @@
#include <functional> // passing function pointer or lambda as argument to a function
#include "utils/NoCopy.h"
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings.h"
#include "settings/settings.h"
#include "MeshGroup.h"
#include "slicer.h"
@@ -22,7 +24,7 @@ namespace cura
/*!
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
*/
class Wireframe2gcode : public SettingsMessenger
class Wireframe2gcode : public SettingsMessenger, NoCopy
{
private:
static const int STRATEGY_COMPENSATE = 0;
@@ -69,26 +71,26 @@ public:
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
void writeGCode(CommandSocket* commandSocket);
void writeGCode();
private:
WireFrame wireFrame;
WireFrame& wireFrame;
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
*/
void processStartingCode(CommandSocket* command_socket);
void processStartingCode();
/*!
* Lay down a skirt
*/
void processSkirt(CommandSocket* commandSocket);
void processSkirt();
/*!
* End gcode: nozzle temp down
*/
void finalize(int maxObjectHeight);
void finalize();
void writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& outlines
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
+193 -95
Ver Arquivo
@@ -5,55 +5,45 @@
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "utils/SVG.h"
namespace cura {
bool Comb::moveInsideBoundary(Point* p, int distance)
{
return PolygonUtils::moveInside(boundary_inside, *p, distance) != NO_INDEX;
}
Polygons Comb::getLayerSecondWalls()
{
Polygons layer_walls;
for (SliceMeshStorage& mesh : storage.meshes)
{
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
{
if (part.insets.size() >= 2)
{
layer_walls.add(part.insets[1]);
}
else
{
layer_walls.add(part.outline.offset(-offset_from_outlines));
}
}
}
return layer_walls;
}
// boundary_outside is only computed when it's needed!
Polygons* Comb::getBoundaryOutside()
Polygons& Comb::getBoundaryOutside()
{
if (!boundary_outside)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
}
return boundary_outside;
return *boundary_outside;
}
BucketGrid2D<PolygonsPointIndex>& Comb::getOutsideLocToLine()
{
Polygons& outside = getBoundaryOutside();
if (!outside_loc_to_line)
{
outside_loc_to_line = PolygonUtils::createLocToLineGrid(outside, offset_from_outlines_outside * 3 / 2);
}
return *outside_loc_to_line;
}
Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, max_crossing_dist2(offset_from_outlines_outside * offset_from_outlines_outside * 3) // so max_crossing_dist = offset_from_outlines_outside * sqrt(3), which is a bit more than sqrt(2) which is necesary for 90* corners
, avoid_other_parts(travel_avoid_other_parts)
// , boundary_inside( boundary.offset(-offset_from_outlines) ) // TODO: make inside boundary configurable?
, boundary_inside( getLayerSecondWalls() )
, boundary_inside( comb_boundary_inside )
, boundary_outside(nullptr)
, outside_loc_to_line(nullptr)
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
{
}
@@ -61,24 +51,28 @@ Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_bounda
Comb::~Comb()
{
if (boundary_outside)
{
delete boundary_outside;
}
if (outside_loc_to_line)
{
delete outside_loc_to_line;
}
}
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside)
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
{
return true;
}
//Move start and end point inside the comb boundary
unsigned int start_inside_poly = NO_INDEX;
if (startInside)
{
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
if (!inside(start_inside_poly) || start_inside_poly == NO_INDEX)
if (!boundary_inside.inside(start_inside_poly) || start_inside_poly == NO_INDEX)
{
if (start_inside_poly != NO_INDEX)
{ // if not yet inside because of overshoot, try again
@@ -94,7 +88,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
if (endInside)
{
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
if (!inside(endPoint) || end_inside_poly == NO_INDEX)
if (!boundary_inside.inside(endPoint) || end_inside_poly == NO_INDEX)
{
if (end_inside_poly != NO_INDEX)
{ // if not yet inside because of overshoot, try again
@@ -117,84 +111,136 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
return true;
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
Point middle_from;
Point middle_to;
// INSIDE | in_between | OUTSIDE | in_between | INSIDE
// ^crossing_1_in ^crossing_1_mid ^crossing_1_out ^crossing_2_out ^crossing_2_mid ^crossing_2_in
//
// when startPoint is inside crossing_1_in is of interest
// when it is in between inside and outside it is equal to crossing_1_mid
Point crossing_1_in_or_mid; // the point inside the starting polygon if startPoint is inside or the startPoint itself if it is not inside
Point crossing_1_out;
Point crossing_2_in_or_mid; // the point inside the ending polygon if endPoint is inside or the endPoint itself if it is not inside
Point crossing_2_out;
if (startInside && endInside)
{
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]);
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
middle_from = middle_from_cp.location;
middle_to = middle_to_cp.location;
}
else
{
if (!startInside && !endInside)
{
middle_from = startPoint;
middle_to = endPoint;
}
else if (!startInside && endInside)
{ // find crossing over the in-between area between inside and outside
if (startInside)
{
middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]);
crossing_1_in_or_mid = PolygonUtils::moveInside(crossing_1_in_cp, offset_dist_to_get_from_on_the_polygon_to_outside); // in-case
}
else if (startInside && !endInside)
else
{
middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
crossing_1_in_or_mid = startPoint; // mid-case
}
if (endInside)
{
ClosestPolygonPoint crossing_2_in_cp = PolygonUtils::findClosest(crossing_1_in_or_mid, boundary_inside[end_part_boundary_poly_idx]);
crossing_2_in_or_mid = PolygonUtils::moveInside(crossing_2_in_cp, offset_dist_to_get_from_on_the_polygon_to_outside); // in-case
}
else
{
crossing_2_in_or_mid = endPoint; // mid-case
}
}
bool avoid_other_parts_now = avoid_other_parts;
if (avoid_other_parts_now && vSize2(crossing_1_in_or_mid - crossing_2_in_or_mid) < offset_from_outlines_outside * offset_from_outlines_outside * 4)
{ // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside
avoid_other_parts_now = false;
}
if (avoid_other_parts_now)
{ // compute the crossing points when moving through air
Polygons& outside = getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
crossing_1_out = crossing_1_in_or_mid;
if (startInside || outside.inside(crossing_1_in_or_mid, true)) // start in_between
{ // move outside
ClosestPolygonPoint* crossing_1_out_cpp = PolygonUtils::findClose(crossing_1_in_or_mid, outside, getOutsideLocToLine());
if (crossing_1_out_cpp)
{
crossing_1_out = PolygonUtils::moveOutside(*crossing_1_out_cpp, offset_dist_to_get_from_on_the_polygon_to_outside);
}
else
{
PolygonUtils::moveOutside(outside, crossing_1_out, offset_dist_to_get_from_on_the_polygon_to_outside);
}
}
int64_t in_out_dist2_1 = vSize2(crossing_1_out - crossing_1_in_or_mid);
if (startInside && in_out_dist2_1 > max_crossing_dist2) // moveInside moved too far
{ // if move is to far over in_between
// find crossing closer by
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(boundary_inside[start_part_boundary_poly_idx], startPoint, endPoint);
if (best)
{
crossing_1_in_or_mid = PolygonUtils::moveInside(best->first, offset_dist_to_get_from_on_the_polygon_to_outside);
crossing_1_out = PolygonUtils::moveOutside(best->second, offset_dist_to_get_from_on_the_polygon_to_outside);
}
}
crossing_2_out = crossing_2_in_or_mid;
if (endInside || outside.inside(crossing_2_in_or_mid, true))
{ // move outside
ClosestPolygonPoint* crossing_2_out_cpp = PolygonUtils::findClose(crossing_2_in_or_mid, outside, getOutsideLocToLine());
if (crossing_2_out_cpp)
{
crossing_2_out = PolygonUtils::moveOutside(*crossing_2_out_cpp, offset_dist_to_get_from_on_the_polygon_to_outside);
}
else
{
PolygonUtils::moveOutside(outside, crossing_2_out, offset_dist_to_get_from_on_the_polygon_to_outside);
}
}
int64_t in_out_dist2_2 = vSize2(crossing_2_out - crossing_2_in_or_mid);
if (endInside && in_out_dist2_2 > max_crossing_dist2) // moveInside moved too far
{ // if move is to far over in_between
// find crossing closer by
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(boundary_inside[end_part_boundary_poly_idx], endPoint, crossing_1_out);
if (best)
{
crossing_2_in_or_mid = PolygonUtils::moveInside(best->first, offset_dist_to_get_from_on_the_polygon_to_outside);
crossing_2_out = PolygonUtils::moveOutside(best->second, offset_dist_to_get_from_on_the_polygon_to_outside);
}
}
}
if (startInside)
{
// start to boundary
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_begin, startPoint, middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part_begin, startPoint, crossing_1_in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
// throught air from boundary to boundary
if (avoid_other_parts)
if (avoid_other_parts_now)
{
Polygons& middle = *getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
Point from_outside = middle_from;
if (startInside || middle.inside(from_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, from_outside, -offset_extra_start_end, max_moveInside_distance2);
}
Point to_outside = middle_to;
if (endInside || middle.inside(to_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, to_outside, -offset_extra_start_end, max_moveInside_distance2);
}
combPaths.emplace_back();
combPaths.back().throughAir = true;
if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) )
{ // via outside is a detour
combPaths.back().push_back(middle_from);
combPaths.back().push_back(middle_to);
combPaths.throughAir = true;
if ( vSize(crossing_1_in_or_mid - crossing_2_in_or_mid) < vSize(crossing_1_in_or_mid - crossing_1_out) + vSize(crossing_2_in_or_mid - crossing_2_out) )
{ // via outside is moving more over the in-between zone
combPaths.back().push_back(crossing_1_in_or_mid);
combPaths.back().push_back(crossing_2_in_or_mid);
}
else
{
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(getBoundaryOutside(), crossing_1_out, crossing_2_out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
}
else
{ // directly through air (not avoiding other parts)
combPaths.emplace_back();
combPaths.back().throughAir = true;
combPaths.throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().push_back(middle_from);
combPaths.back().push_back(middle_to);
combPaths.back().push_back(crossing_1_in_or_mid);
combPaths.back().push_back(crossing_2_in_or_mid);
}
if (endInside)
@@ -202,13 +248,45 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
// boundary to end
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_end, middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part_end, crossing_2_in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
return true;
}
}
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::findBestCrossing(PolygonRef from, Point estimated_start, Point estimated_end)
{
ClosestPolygonPoint* best_in = nullptr;
ClosestPolygonPoint* best_out = nullptr;
int64_t best_detour_dist = std::numeric_limits<int64_t>::max();
std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> crossing_out_candidates = PolygonUtils::findClose(from, getBoundaryOutside(), getOutsideLocToLine());
for (std::pair<ClosestPolygonPoint, ClosestPolygonPoint>& crossing_candidate : crossing_out_candidates)
{
int64_t crossing_dist2 = vSize2(crossing_candidate.first.location - crossing_candidate.second.location);
if (crossing_dist2 > max_crossing_dist2)
{
continue;
}
int64_t dist_to_start = vSize(crossing_candidate.second.location - estimated_start); // use outside location, so that the crossing direction is taken into account
int64_t dist_to_end = vSize(crossing_candidate.second.location - estimated_end);
int64_t detour_dist = dist_to_start + dist_to_end;
if (detour_dist < best_detour_dist)
{
best_in = &crossing_candidate.first;
best_out = &crossing_candidate.second;
best_detour_dist = detour_dist;
}
}
if (best_detour_dist == std::numeric_limits<int64_t>::max())
{
return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>();
}
return std::make_shared<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(*best_in, *best_out);
}
void LinePolygonsCrossings::calcScanlineCrossings()
{
@@ -219,18 +297,32 @@ void LinePolygonsCrossings::calcScanlineCrossings()
{
PolyCrossings minMax(poly_idx);
PolygonRef poly = boundary[poly_idx];
Point p0 = transformation_matrix.apply(poly.back());
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = transformation_matrix.apply(poly[point_idx]);
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
{
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
{
p0 = p1;
continue;
}
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
{
if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; }
if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; }
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
{
minMax.min.x = x;
minMax.min.point_idx = point_idx;
}
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
{
minMax.max.x = x;
minMax.max.point_idx = point_idx;
}
}
}
p0 = p1;
@@ -276,14 +368,14 @@ bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
}
void LinePolygonsCrossings::getCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, Comb::max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
{
//We're not crossing any boundaries. So skip the comb generation.
combPath.push_back(startPoint);
combPath.push_back(endPoint);
return;
combPath.push_back(endPoint);
return;
}
calcScanlineCrossings();
@@ -291,6 +383,7 @@ void LinePolygonsCrossings::getCombingPath(CombPath& combPath)
CombPath basicPath;
getBasicCombingPath(basicPath);
optimizePath(basicPath, combPath);
// combPath = basicPath; // uncomment to disable comb path optimization
}
@@ -308,7 +401,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
< poly.size() / 2 )
{ // follow the path in the same direction as the winding order of the boundary polygon
@@ -323,13 +416,13 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
{ // follow the path in the opposite direction of the winding order of the boundary polygon
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
}
@@ -348,10 +441,14 @@ LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlong
}
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
{
optimized_comb_path.push_back(startPoint);
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
{
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
{
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
{
@@ -379,6 +476,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
}
}
}
optimized_comb_path.push_back(comb_path.back());
return true;
}
+38 -33
Ver Arquivo
@@ -2,18 +2,22 @@
#ifndef COMB_H
#define COMB_H
#include <memory> // shared_ptr
#include "utils/polygon.h"
#include "utils/BucketGrid2D.h"
#include "utils/polygonUtils.h"
namespace cura
{
struct CombPath : public std::vector<Point> //!< A single path either inside or outise the parts
{
bool throughAir = false; //!< Whether the path is one which moves through air.
bool cross_boundary = false; //!< Whether the path crosses a boundary.
};
struct CombPaths : public std::vector<CombPath> //!< A list of paths alternating between inside a part and outside a part
{
bool throughAir = false; //!< Whether the path is one which moves through air.
};
/*!
@@ -113,7 +117,7 @@ private:
*
* \param combPath Output parameter: the points along the combing path.
*/
void getCombingPath(CombPath& combPath);
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
/*!
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
@@ -178,10 +182,10 @@ public:
* \param endPoint Where to end the combing move.
* \param combPath Output parameter: the combing path generated.
*/
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside)
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
linePolygonsCrossings.getCombingPath(combPath);
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
};
};
@@ -205,47 +209,58 @@ class Comb
friend class LinePolygonsCrossings;
private:
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
unsigned int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
const int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
const int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
const int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
const int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
const int64_t max_crossing_dist2; //!< The maximal distance by which to cross the in_between area between inside and outside
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
static const int64_t max_comb_distance_ignored = MM2INT(1.5); //!< If the direct path from start point to end point is shorter than this, go directly without any combing.
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
const bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
Polygons boundary_inside; //!< The boundary within which to comb.
Polygons& boundary_inside; //!< The boundary within which to comb.
Polygons* boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
BucketGrid2D<PolygonsPointIndex>* outside_loc_to_line; //!< The BucketGrid mapping locations to line segments of the outside boundary.
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
/*!
* Collects the inner most walls for every mesh in the layer (not support) or computes them from the outlines using Comb::offset_from_outlines.
*/
Polygons getLayerSecondWalls();
/*!
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
*/
Polygons* getBoundaryOutside();
Polygons& getBoundaryOutside();
/*!
* Get the BucketGrid mapping locations to line segments of the outside boundary. Calculate it when it hasn't been calculated yet.
*/
BucketGrid2D<PolygonsPointIndex>& getOutsideLocToLine();
/*!
* Find the best crossing from some inside polygon to the outside boundary.
*
* The detour from \p estimated_start to \p estimated_end is minimized.
*
* \param from From which inside boundary the crossing to the outside starts or ends
* \param estimated_start The one point to which to stay close when evaluating crossings which cross about the same distance
* \param estimated_end The other point to which to stay close when evaluating crossings which cross about the same distance
* \return A pair of which the first is the crossing point on the inside boundary and the second the crossing point on the outside boundary
*/
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findBestCrossing(PolygonRef from, Point estimated_start, Point estimated_end);
public:
/*!
* Initializes the combing areas for every mesh in the layer (not support)
* \param storage Where the layer polygon data is stored
* \param layer_nr The number of the layer for which to generate the combing areas.
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
* \param offset_from_outlines The offset from the outline polygon, to create the combing boundary in case there is no second wall.
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
*/
Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~Comb();
//! Utility function for `boundary_inside.inside(p)`.
bool inside(const Point p) { return boundary_inside.inside(p); }
/*!
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
@@ -257,17 +272,7 @@ public:
* \param endInside Whether we want to end up inside the comb boundary
* \return Whether combing has succeeded; otherwise a retraction is needed.
*/
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false);
/*!
* Move \p p to inside the inner comb boundary with a \p distance from the boundary.
*
* \param p the point to change/move
* \param distance the distance from the resulting point to the boundary on the inside
* \return whether the point has been moved inside
*/
bool moveInsideBoundary(Point* p, int distance);
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
};
}//namespace cura
+259 -123
Ver Arquivo
@@ -1,150 +1,263 @@
#include "utils/logoutput.h"
#include "commandSocket.h"
#include "FffProcessor.h"
#include "Progress.h"
#include "progress/Progress.h"
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#include <Arcus/SocketListener.h>
#include <Arcus/Error.h>
#endif
#include <string> // stoi
#ifdef _WIN32
#include <windows.h>
#endif
#include "settings/SettingRegistry.h" // loadExtruderJSONsettings
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
// std::cerr << x;
namespace cura {
#define BYTES_PER_FLOAT 4
#define FLOATS_PER_VECTOR 3
#define VECTORS_PER_FACE 3
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
#ifdef ARCUS
class Listener : public Arcus::SocketListener
{
public:
void stateChanged(Arcus::SocketState::SocketState newState) override
{
}
void messageReceived() override
{
}
void error(const Arcus::Error & error) override
{
if (error.getErrorCode() == Arcus::ErrorCode::Debug)
{
log("%s\n", error.toString().c_str());
}
else
{
logError("%s\n", error.toString().c_str());
}
}
};
class CommandSocket::Private
{
public:
Private()
: socket(nullptr)
, object_count(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
cura::proto::Layer* getLayerById(int id);
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
// Message that holds a list of sliced objects
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
// Message that holds the currently sliced object (to be added to sliced_object_list)
cura::proto::SlicedObject* current_sliced_object;
// Number of sliced objects for this sliced object list
int sliced_objects;
// Ids of the sliced objects
std::vector<int64_t> object_ids;
// Number of layers sent to the front end so far
// Used for incrementing the current layer in one at a time mode
int current_layer_count;
int current_layer_offset;
std::string temp_gcode_file;
std::ostringstream gcode_output_stream;
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
};
#endif
CommandSocket::CommandSocket()
: d(new Private)
#ifdef ARCUS
: private_data(new Private)
#endif
{
FffProcessor::getInstance()->setCommandSocket(this);
#ifdef ARCUS
#endif
}
CommandSocket* CommandSocket::getInstance()
{
return instance;
}
void CommandSocket::instantiate()
{
instance = new CommandSocket();
}
bool CommandSocket::isInstantiated()
{
return instance != nullptr;
}
void CommandSocket::connect(const std::string& ip, int port)
{
d->socket = new Arcus::Socket();
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
#ifdef ARCUS
private_data->socket = new Arcus::Socket();
private_data->socket->addListener(new Listener());
d->socket->connect(ip, port);
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
private_data->socket->connect(ip, port);
log("Connecting to %s:%i\n", ip.c_str(), port);
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
log("Connected to %s:%i\n", ip.c_str(), port);
bool slice_another_time = true;
// Start & continue listening as long as socket is not closed and there is no error.
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error)
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
{
//If there is an object to slice, do so.
if(d->objects_to_slice.size())
{
for(auto object : d->objects_to_slice)
{
FffProcessor::getInstance()->processMeshGroup(object.get());
}
d->objects_to_slice.clear();
sendPrintTime();
//TODO: Support all-at-once/one-at-a-time printing
//d->processor->processModel(d->object_to_slice.get());
//d->object_to_slice.reset();
//d->processor->resetFileNumber();
//sendPrintTime();
}
// Actually start handling messages.
Arcus::MessagePtr message = d->socket->takeNextMessage();
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if(setting_list)
if (setting_list)
{
handleSettingList(setting_list);
}
/*cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
if(object_list)
if (object_list)
{
handleObjectList(object_list);
}*/
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get());
if(slice)
if (slice)
{
// Reset object counts
d->object_count = 0;
d->object_ids.clear();
for(auto object : slice->object_lists())
private_data->object_count = 0;
for (auto object : slice->object_lists())
{
handleObjectList(&object);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if(!d->socket->errorString().empty())
//If there is an object to slice, do so.
if (private_data->objects_to_slice.size())
{
logError("%s\n", d->socket->errorString().data());
d->socket->clearError();
FffProcessor::getInstance()->resetMeshGroupNumber();
for (auto object : private_data->objects_to_slice)
{
if (!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
private_data->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
flushGcode();
sendPrintTime();
sendFinishedSlicing();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//private_data->processor->processModel(private_data->object_to_slice.get());
//private_data->object_to_slice.reset();
//private_data->processor->resetFileNumber();
//sendPrintTime();
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
log("Closing connection\n");
private_data->socket->close();
#endif
}
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
{
FMatrix3x3 matrix;
//d->object_count = 0;
//d->object_ids.clear();
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* object_to_slice = d->objects_to_slice.back().get();
for(auto object : list->objects())
if (list->objects_size() <= 0)
{
object_to_slice->meshes.push_back(object_to_slice); //Construct a new mesh (with object_to_slice as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = object_to_slice->meshes.back();
return;
}
FMatrix3x3 matrix;
//private_data->object_count = 0;
//private_data->object_ids.clear();
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
for (auto setting : list->settings())
{
meshgroup->setSetting(setting.name(), setting.value());
}
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, train);
}
for (auto object : list->objects())
{
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
for(int i = 0; i < face_count; ++i)
if (face_count <= 0)
{
logWarning("Got an empty mesh, ignoring it!");
continue;
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
for (auto setting : object.settings())
{
if (setting.name() == "extruder_nr")
{
extruder_train_nr = std::stoi(setting.value());
break;
}
}
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = meshgroup->meshes.back();
for (int i = 0; i < face_count; ++i)
{
//TODO: Apply matrix
std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face);
@@ -155,74 +268,75 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
verts[1] = matrix.apply(float_vertices[1]);
verts[2] = matrix.apply(float_vertices[2]);
mesh.addFace(verts[0], verts[1], verts[2]);
}
for(auto setting : object.settings())
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n");
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n");
for (auto setting : object.settings())
{
mesh.setSetting(setting.name(), setting.value());
}
d->object_ids.push_back(object.id());
mesh.finish();
}
for(auto setting : list->settings())
{
object_to_slice->setSetting(setting.name(), setting.value());
}
d->object_count++;
object_to_slice->finalize();
private_data->object_count++;
meshgroup->finalize();
}
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
{
for(auto setting : list->settings())
for (auto setting : list->settings())
{
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
if(!d->current_sliced_object)
{
return;
}
cura::proto::Layer* layer = d->getLayerById(layer_nr);
#ifdef ARCUS
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
#endif
}
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
{
if(!d->current_sliced_object)
return;
#ifdef ARCUS
if (polygons.size() == 0)
return;
cura::proto::Layer* layer = d->getLayerById(layer_nr);
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
for (unsigned int i = 0; i < polygons.size(); ++i)
{
cura::proto::Polygon* p = layer->add_polygons();
cura::proto::Polygon* p = proto_layer->add_polygons();
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
std::string polydata;
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
p->set_points(polydata);
p->set_line_width(line_width);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::Progress>();
amount /= d->object_count;
amount += d->sliced_objects * (1. / d->object_count);
amount /= private_data->object_count;
amount += private_data->sliced_objects * (1. / private_data->object_count);
message->set_amount(amount);
d->socket->sendMessage(message);
private_data->socket->sendMessage(message);
#endif
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
@@ -232,10 +346,12 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
void CommandSocket::sendPrintTime()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
d->socket->sendMessage(message);
private_data->socket->sendMessage(message);
#endif
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -247,68 +363,88 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
// socket.sendFloat32(print_time);
}
void CommandSocket::beginSendSlicedObject()
void CommandSocket::sendLayerData()
{
if(!d->sliced_object_list)
{
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
}
#ifdef ARCUS
#endif
#ifdef ARCUS
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
d->current_sliced_object = d->sliced_object_list->add_objects();
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
if (private_data->sliced_objects >= private_data->object_count)
{
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
private_data->sliced_objects = 0;
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_layers.clear();
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
}
#endif
}
void CommandSocket::endSendSlicedObject()
void CommandSocket::sendFinishedSlicing()
{
d->sliced_objects++;
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
if(d->sliced_objects >= d->object_count)
{
d->socket->sendMessage(d->sliced_object_list);
d->sliced_objects = 0;
d->sliced_object_list.reset();
d->current_sliced_object = nullptr;
}
#ifdef ARCUS
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
#endif
}
void CommandSocket::beginGCode()
{
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
#endif
}
void CommandSocket::sendGCodeLayer()
void CommandSocket::flushGcode()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodeLayer>();
message->set_id(d->object_ids[0]);
message->set_data(d->gcode_output_stream.str());
d->socket->sendMessage(message);
message->set_data(private_data->gcode_output_stream.str());
private_data->socket->sendMessage(message);
d->gcode_output_stream.str("");
private_data->gcode_output_stream.str("");
#endif
}
void CommandSocket::sendGCodePrefix(std::string prefix)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
message->set_data(prefix);
d->socket->sendMessage(message);
private_data->socket->sendMessage(message);
#endif
}
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
#ifdef ARCUS
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
{
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
id += current_layer_offset;
cura::proto::Layer* layer = nullptr;
if(itr != current_sliced_object->mutable_layers()->end())
auto itr = sliced_layers.find(id);
std::shared_ptr<cura::proto::Layer> layer;
if (itr != sliced_layers.end())
{
layer = &(*itr);
layer = itr->second;
}
else
{
layer = current_sliced_object->add_layers();
layer = std::make_shared<cura::proto::Layer>();
layer->set_id(id);
current_layer_count++;
sliced_layers[id] = layer;
}
return layer;
}
#endif
}//namespace cura
+52 -13
Ver Arquivo
@@ -3,20 +3,33 @@
#include "utils/socket.h"
#include "utils/polygon.h"
#include "settings.h"
#include "Progress.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura {
namespace cura
{
class CommandSocket
{
private:
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
public:
CommandSocket();
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
static void instantiate(); //!< Instantiate the CommandSocket.
static bool isInstantiated(); //!< Check whether the singleton is instantiated
/*!
* Connect with the GUI
* This creates and initialises the arcus socket and then continues listening for messages.
@@ -24,7 +37,8 @@ public:
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
@@ -36,17 +50,23 @@ public:
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
#endif
/*!
* Does nothing at the moment
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
*/
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the engine. This is used for the layerview in the GUI
*/
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
*/
@@ -66,17 +86,36 @@ public:
* Does nothing at the moment
*/
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
/*!
* Send the sliced layer data to the GUI.
*
* The GUI may use this to visualise the g-code, so that the user can
* inspect the result of slicing.
*/
void sendLayerData();
void beginSendSlicedObject();
void endSendSlicedObject();
/*!
* \brief Sends a message to indicate that all the slicing is done.
*
* This should indicate that no more data (g-code, prefix/postfix, metadata
* or otherwise) should be sent any more regarding the latest slice job.
*/
void sendFinishedSlicing();
void beginGCode();
void sendGCodeLayer();
/*!
* Flush the gcode in gcode_output_stream into a message queued in the socket.
*/
void flushGcode();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> d;
const std::unique_ptr<Private> private_data;
#endif
};
}//namespace cura
+520 -240
Ver Arquivo
@@ -1,39 +1,179 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdarg.h>
#include <iomanip>
#include <cmath>
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "PrintFeature.h"
#include "utils/Date.h"
namespace cura {
GCodeExport::GCodeExport()
: output_stream(&std::cout), currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
: output_stream(&std::cout)
, currentPosition(0,0,MM2INT(20))
, layer_nr(0)
{
extrusion_amount = 0;
current_e_value = 0;
current_extruder = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
currentSpeed = 1;
retractionPrimeSpeed = 1;
isRetracted = false;
isZHopped = false;
last_coasted_amount_mm3 = 0;
isZHopped = 0;
setFlavor(EGCodeFlavor::REPRAP);
initial_bed_temp = 0;
extruder_count = 0;
}
GCodeExport::~GCodeExport()
{
}
void GCodeExport::preSetup(MeshGroup* settings)
{
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
extruder_count = settings->getSettingAsCount("machine_extruder_count");
for (unsigned int n = 0; n < extruder_count; n++)
{
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[n].end_code = train->getSettingString("machine_extruder_end_code");
extruder_attr[n].extruder_switch_retraction_config.distance = train->getSettingInMillimeters("switch_extruder_retraction_amount");
extruder_attr[n].extruder_switch_retraction_config.prime_volume = 0.0;
extruder_attr[n].extruder_switch_retraction_config.speed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
extruder_attr[n].extruder_switch_retraction_config.primeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
extruder_attr[n].extruder_switch_retraction_config.zHop = train->getSettingInMicrons("switch_extruder_retraction_hop");
extruder_attr[n].extruder_switch_retraction_config.retraction_count_max = 9999999; // extruder switch retraction is never limited
extruder_attr[n].extruder_switch_retraction_config.retraction_extrusion_window = 99999.9; // so that extruder switch retractions won't affect the retraction buffer (extruded_volume_at_previous_n_retractions)
extruder_attr[n].extruder_switch_retraction_config.retraction_min_travel_distance = 0; // no limitation on travel distance for an extruder switch retract
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
}
machine_dimensions.x = settings->getSettingInMicrons("machine_width");
machine_dimensions.y = settings->getSettingInMicrons("machine_depth");
machine_dimensions.z = settings->getSettingInMicrons("machine_height");
machine_name = settings->getSettingString("machine_name");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
}
else
{
new_line = "\n";
}
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
{
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
const ExtruderTrain* extr_train = settings.getExtruderTrain(extr_nr);
assert(extr_train);
double temp = extr_train->getSettingInDegreeCelsius((extr_nr == 0)? "material_print_temperature" : "material_standby_temperature");
setInitialTemp(extr_nr, temp);
}
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature");
}
void GCodeExport::setInitialTemp(int extruder_nr, double temp)
{
extruder_attr[extruder_nr].initial_temp = temp;
if (flavor == EGCodeFlavor::GRIFFIN || flavor == EGCodeFlavor::ULTIGCODE)
{
extruder_attr[extruder_nr].currentTemperature = temp;
}
}
std::string GCodeExport::getFileHeader(const double* print_time, const std::vector<double>& filament_used, const std::vector<int16_t>& mat_ids)
{
std::ostringstream prefix;
switch (flavor)
{
case EGCodeFlavor::GRIFFIN:
prefix << ";START_OF_HEADER" << new_line;
prefix << ";HEADER_VERSION:0.1" << new_line;
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";GENERATOR.NAME:Cura_SteamEngine" << new_line;
prefix << ";GENERATOR.VERSION:" << VERSION << new_line;
prefix << ";GENERATOR.BUILD_DATE:" << Date::getDate().toStringDashed() << new_line;
prefix << ";TARGET_MACHINE.NAME:" << machine_name << new_line;
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".INITIAL_TEMPERATURE:" << extruder_attr[extr_nr].initial_temp << new_line;
if (filament_used.size() == extruder_count)
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.VOLUME_USED:" << static_cast<int>(filament_used[extr_nr]) << new_line;
}
if (mat_ids.size() == extruder_count)
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line; // TODO: convert to hexadecimal format
}
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.DIAMETER:" << float(INT2MM(getNozzleSize(extr_nr))) << new_line;
}
prefix << ";BUILD_PLATE.INITIAL_TEMPERATURE:" << initial_bed_temp << new_line;
if (print_time)
{
prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line;
}
prefix << ";PRINT.SIZE.MIN.X:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Y:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Z:0" << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(machine_dimensions.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(machine_dimensions.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(machine_dimensions.z) << new_line;
prefix << ";END_OF_HEADER" << new_line;
return prefix.str();
default:
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";TIME:" << ((print_time)? static_cast<int>(*print_time) : 6666) << new_line;
if (flavor == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << ((filament_used.size() >= 1)? static_cast<int>(filament_used[0]) : 6666) << new_line;
prefix << ";MATERIAL2:" << ((filament_used.size() >= 2)? static_cast<int>(filament_used[1]) : 0) << new_line;
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// TODO: the second nozzle size isn't always initiated! ";NOZZLE_DIAMETER2:"
}
return prefix.str();
}
}
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
layer_nr = layer_nr_;
}
void GCodeExport::setOutputStream(std::ostream* stream)
{
output_stream = stream;
*output_stream << std::fixed;
}
int GCodeExport::getNozzleSize(int extruder_idx)
{
return extruder_attr[extruder_idx].nozzle_size;
}
Point GCodeExport::getExtruderOffset(int id)
{
return extruder_attr[id].nozzle_offset;
@@ -63,6 +203,15 @@ void GCodeExport::setFlavor(EGCodeFlavor flavor)
{
is_volumatric = false;
}
if (flavor == EGCodeFlavor::BFB || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC || flavor == EGCodeFlavor::ULTIGCODE)
{
firmware_retract = true;
}
else
{
firmware_retract = false;
}
}
EGCodeFlavor GCodeExport::getFlavor()
@@ -89,17 +238,6 @@ int GCodeExport::getPositionZ()
return currentPosition.z;
}
void GCodeExport::resetStartPosition()
{
startPosition.x = INT32_MIN;
startPosition.y = INT32_MIN;
}
Point GCodeExport::getStartPositionXY()
{
return Point(startPosition.x, startPosition.y);
}
int GCodeExport::getExtruderNr()
{
return current_extruder;
@@ -112,35 +250,68 @@ void GCodeExport::setFilamentDiameter(unsigned int extruder, int diameter)
extruder_attr[extruder].filament_area = area;
}
double GCodeExport::getFilamentArea(unsigned int extruder)
{
return extruder_attr[extruder].filament_area;
}
double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
double GCodeExport::getCurrentExtrudedVolume()
{
double extrusion_amount = current_e_value;
if (!firmware_retract)
{ // no E values are changed to perform a retraction
extrusion_amount -= extruder_attr[current_extruder].retraction_e_amount_at_e_start; // subtract the increment in E which was used for the first unretraction instead of extrusion
extrusion_amount += extruder_attr[current_extruder].retraction_e_amount_current; // add the decrement in E which the filament is behind on extrusion due to the last retraction
}
if (is_volumatric)
{
return extrusion_amount;
}
else
{
return extrusion_amount * getFilamentArea(extruder);
return extrusion_amount * extruder_attr[current_extruder].filament_area;
}
}
double GCodeExport::eToMm(double e)
{
if (is_volumatric)
{
return e / extruder_attr[current_extruder].filament_area;
}
else
{
return e;
}
}
double GCodeExport::mm3ToE(double mm3)
{
if (is_volumatric)
{
return mm3;
}
else
{
return mm3 / extruder_attr[current_extruder].filament_area;
}
}
double GCodeExport::mmToE(double mm)
{
if (is_volumatric)
{
return mm * extruder_attr[current_extruder].filament_area;
}
else
{
return mm;
}
}
double GCodeExport::getTotalFilamentUsed(int e)
double GCodeExport::getTotalFilamentUsed(int extruder_nr)
{
if (e == current_extruder)
return extruder_attr[e].totalFilament + getExtrusionAmountMM3(e);
return extruder_attr[e].totalFilament;
if (extruder_nr == current_extruder)
return extruder_attr[extruder_nr].totalFilament + getCurrentExtrudedVolume();
return extruder_attr[extruder_nr].totalFilament;
}
double GCodeExport::getTotalPrintTime(EPrintFeature print_feature)
{
return total_print_time_per_feature[(unsigned int)print_feature];
}
double GCodeExport::getTotalPrintTime()
{
return totalPrintTime;
@@ -149,62 +320,110 @@ double GCodeExport::getTotalPrintTime()
void GCodeExport::resetTotalPrintTimeAndFilament()
{
totalPrintTime = 0;
for (unsigned int feat_idx = 0; feat_idx < (unsigned int)EPrintFeature::ENUM_COUNT; feat_idx++)
{
total_print_time_per_feature[feat_idx] = 0.0;
}
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
{
extruder_attr[e].totalFilament = 0.0;
extruder_attr[e].currentTemperature = 0;
}
extrusion_amount = 0.0;
current_e_value = 0.0;
estimateCalculator.reset();
}
void GCodeExport::updateTotalPrintTime(EPrintFeature print_feature)
void GCodeExport::updateTotalPrintTime()
{
double time = estimateCalculator.calculate();
totalPrintTime += time;
total_print_time_per_feature[(unsigned int)print_feature] += time;
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
}
void GCodeExport::writeComment(std::string comment)
{
*output_stream << ";" << comment << "\n";
*output_stream << ";";
for (unsigned int i = 0; i < comment.length(); i++)
{
if (comment[i] == '\n')
{
*output_stream << "\\n";
}else{
*output_stream << comment[i];
}
}
*output_stream << new_line;
}
void GCodeExport::writeTypeComment(const char* type)
{
*output_stream << ";TYPE:" << type << "\n";
*output_stream << ";TYPE:" << type << new_line;
}
void GCodeExport::writeTypeComment(PrintFeatureType type)
{
switch (type)
{
case PrintFeatureType::OuterWall:
*output_stream << ";TYPE:WALL-OUTER" << new_line;
break;
case PrintFeatureType::InnerWall:
*output_stream << ";TYPE:WALL-INNER" << new_line;
break;
case PrintFeatureType::Skin:
*output_stream << ";TYPE:SKIN" << new_line;
break;
case PrintFeatureType::Support:
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::Skirt:
*output_stream << ";TYPE:SKIRT" << new_line;
break;
case PrintFeatureType::Infill:
*output_stream << ";TYPE:FILL" << new_line;
break;
case PrintFeatureType::SupportInfill:
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::MoveCombing:
case PrintFeatureType::MoveRetraction:
default:
// do nothing
break;
}
}
void GCodeExport::writeLayerComment(int layer_nr)
{
*output_stream << ";LAYER:" << layer_nr << "\n";
*output_stream << ";LAYER:" << layer_nr << new_line;
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << new_line;
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << "\n";
*output_stream << line << new_line;
}
void GCodeExport::resetExtrusionValue()
{
if (extrusion_amount != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
if (flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
extruder_attr[current_extruder].totalFilament += getExtrusionAmountMM3(current_extruder);
for (unsigned int i = 0; i < extrusion_amount_at_previous_n_retractions.size(); i++)
extrusion_amount_at_previous_n_retractions[i] -= extrusion_amount;
extrusion_amount = 0.0;
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
double current_extruded_volume = getCurrentExtrudedVolume();
extruder_attr[current_extruder].totalFilament += current_extruded_volume;
for (double& extruded_volume_at_retraction : extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions)
{ // update the extruded_volume_at_previous_n_retractions only of the current extruder, since other extruders don't extrude the current volume
extruded_volume_at_retraction -= current_extruded_volume;
}
current_e_value = 0.0;
extruder_attr[current_extruder].retraction_e_amount_at_e_start = extruder_attr[current_extruder].retraction_e_amount_current;
}
}
void GCodeExport::writeDelay(double timeAmount)
{
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
totalPrintTime += timeAmount;
*output_stream << "G4 P" << int(timeAmount * 1000) << new_line;
estimateCalculator.addTime(timeAmount);
}
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
@@ -217,238 +436,297 @@ void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
writeMove(p.x, p.y, p.z, speed, extrusion_mm3_per_mm);
}
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
Point gcode_pos = getGcodePos(x,y, current_extruder);
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
float fspeed = speed * 60;
float rpm = extrusion_per_mm * speed * 60;
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
rpm /= mm_per_rpm;
if (rpm > 0)
{
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
if (currentSpeed != double(rpm))
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
// (Trick copied from KISSlicer, thanks Jonathan)
fspeed *= (rpm / (roundf(rpm * 100) / 100));
//Increase the extrusion amount to calculate the amount of filament used.
Point3 diff = Point3(x,y,z) - getPosition();
current_e_value += extrusion_per_mm * diff.vSizeMM();
}
else
{
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!extruder_attr[current_extruder].retraction_e_amount_current)
{
*output_stream << "M103" << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
}
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
return;
assert(speed*60 < 10000 && speed*60 > 100); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
#ifdef ASSERT_INSANE_OUTPUT
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
assert(currentPosition != no_point3);
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
#endif //ASSERT_INSANE_OUTPUT
if (extrusion_mm3_per_mm < 0)
logWarning("Warning! Negative extrusion move!");
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder);
}
Point gcode_pos = getGcodePos(x,y, current_extruder);
if (flavor == EGCodeFlavor::BFB)
{
//For Bits From Bytes machines, we need to handle this completely differently. As they do not use E values but RPM values.
float fspeed = speed * 60;
float rpm = extrusion_per_mm * speed * 60;
const float mm_per_rpm = 4.0; //All BFB machines have 4mm per RPM extrusion.
rpm /= mm_per_rpm;
if (rpm > 0)
{
if (isRetracted)
{
if (currentSpeed != double(rpm))
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << "\r\n";
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
*output_stream << "M" << int((current_extruder + 1) * 100 + 1) << "\r\n";
isRetracted = false;
}
//Fix the speed by the actual RPM we are asking, because of rounding errors we cannot get all RPM values, but we have a lot more resolution in the feedrate value.
// (Trick copied from KISSlicer, thanks Jonathan)
fspeed *= (rpm / (roundf(rpm * 100) / 100));
writeMoveBFB(x, y, z, speed, extrusion_mm3_per_mm);
return;
}
//Increase the extrusion amount to calculate the amount of filament used.
Point3 diff = Point3(x,y,z) - getPosition();
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
}else{
//If we are not extruding, check if we still need to disable the extruder. This causes a retraction due to auto-retraction.
if (!isRetracted)
{
*output_stream << "M103\r\n";
isRetracted = true;
}
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
Point gcode_pos = getGcodePos(x,y, current_extruder);
if (extrusion_mm3_per_mm > 0.000001)
{
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
isZHopped = 0;
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
double prime_volume = extruder_attr[current_extruder].prime_volume;
current_e_value += mm3ToE(prime_volume);
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
if (firmware_retract)
{ // note that BFB is handled differently
*output_stream << "G11" << new_line;
//Assume default UM2 retraction settings.
if (prime_volume > 0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0);
}
else
{
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
if (getCurrentExtrudedVolume() > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
{
resetExtrusionValue();
}
extruder_attr[current_extruder].retraction_e_amount_current = 0.0;
}
else if (prime_volume > 0.0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
extruder_attr[current_extruder].prime_volume = 0.0;
current_e_value += extrusion_per_mm * diff.vSizeMM();
*output_stream << "G1";
}
else
{
//Normal E handling.
if (extrusion_mm3_per_mm > 0.000001)
{
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
isZHopped = false;
}
extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder);
if (isRetracted)
{
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
*output_stream << "G11\n";
//Assume default UM2 retraction settings.
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
}else{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
}
if (getExtrusionAmountMM3(current_extruder) > 10000.0) //According to https://github.com/Ultimaker/CuraEngine/issues/14 having more then 21m of extrusion causes inaccuracies. So reset it every 10m, just to be sure.
resetExtrusionValue();
isRetracted = false;
}
else
{
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
}
}
last_coasted_amount_mm3 = 0;
extrusion_amount += extrusion_per_mm * diff.vSizeMM();
*output_stream << "G1";
}else{
*output_stream << "G0";
}
*output_stream << "G0";
if (currentSpeed != speed)
if (CommandSocket::isInstantiated())
{
*output_stream << " F" << (speed * 60);
currentSpeed = speed;
// we should send this travel as a non-retraction move
cura::Polygons travelPoly;
PolygonRef travel = travelPoly.newPoly();
travel.add(Point(currentPosition.x, currentPosition.y));
travel.add(Point(x, y));
CommandSocket::getInstance()->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
*output_stream << std::setprecision(3) <<
" X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y);
if (z != currentPosition.z)
*output_stream << " Z" << INT2MM(z);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
*output_stream << "\n";
}
if (currentSpeed != speed)
{
*output_stream << " F" << (speed * 60);
currentSpeed = speed;
}
*output_stream << std::setprecision(3) <<
" X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y);
if (z != currentPosition.z + isZHopped)
*output_stream << " Z" << INT2MM(z + isZHopped);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
*output_stream << new_line;
currentPosition = Point3(x, y, z);
startPosition = currentPosition;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), speed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool extruder_switch)
{
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
return;
if (isRetracted)
return;
if (config->amount <= 0)
return;
if (!force && config->retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max - 1
&& extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + config->retraction_extrusion_window)
return;
ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
if (config->primeAmount > 0)
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
{
extrusion_amount += config->primeAmount;
if (extruder_switch)
{
if (!extr_attr.retraction_e_amount_current)
*output_stream << "M103" << new_line;
extr_attr.retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
}
return;
}
retractionPrimeSpeed = config->primeSpeed;
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
double old_retraction_e_amount = extr_attr.retraction_e_amount_current;
double new_retraction_e_amount = mmToE(config->distance);
double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount;
if (std::abs(retraction_diff_e_amount) < 0.000001)
{
*output_stream << "G10\n";
return;
}
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
{
// extruder switch could have introduced data which falls outside the retraction window
// also the retraction_count_max could have changed between the last retraction and this
extruded_volume_at_previous_n_retractions.pop_back();
}
if (!force && config->retraction_count_max <= 0)
{
return;
}
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extr_attr.filament_area)
{
return;
}
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
}
if (firmware_retract)
{
if (extruder_switch && extr_attr.retraction_e_amount_current)
{
return;
}
*output_stream << "G10";
if (extruder_switch)
{
*output_stream << " S1";
}
*output_stream << new_line;
//Assume default UM2 retraction settings.
double retraction_distance = 4.5;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - retraction_distance), 25); // TODO: hardcoded values!
}else{
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount - config->amount << "\n";
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount - config->amount), currentSpeed);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), 25); // TODO: hardcoded values!
}
else
{
double speed = ((retraction_diff_e_amount < 0.0)? config->speed : extr_attr.last_retraction_prime_speed) * 60;
current_e_value += retraction_diff_e_amount;
*output_stream << "G1 F" << speed << " "
<< extr_attr.extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
extr_attr.last_retraction_prime_speed = config->primeSpeed;
}
extr_attr.retraction_e_amount_current = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
extr_attr.prime_volume += config->prime_volume;
if (config->zHop > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n";
isZHopped = true;
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
}
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max)
{
extrusion_amount_at_previous_n_retractions.pop_back();
}
isRetracted = true;
}
void GCodeExport::writeRetraction_extruderSwitch()
{
if (isRetracted) { return; }
if (flavor == EGCodeFlavor::BFB)
{
if (!isRetracted)
*output_stream << "M103\r\n";
ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
RetractionConfig* config = &extr_attr.extruder_switch_retraction_config;
isRetracted = true;
return;
}
resetExtrusionValue();
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
*output_stream << "G10 S1\n";
}else{
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << (extrusion_amount - extruder_attr[current_extruder].extruderSwitchRetraction) << "\n";
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
}
isRetracted = true;
writeRetraction(config, true, true);
}
void GCodeExport::switchExtruder(int new_extruder)
{
if (current_extruder == new_extruder)
return;
if (!isRetracted) // assumes the last retraction already was an extruder switch retraction
{
writeRetraction_extruderSwitch();
}
writeRetraction_extruderSwitch();
resetExtrusionValue(); // zero the E value on the old extruder, so that the current_e_value is registered on the old extruder
int old_extruder = current_extruder;
current_extruder = new_extruder;
if (flavor == EGCodeFlavor::MACH3)
resetExtrusionValue();
isRetracted = true;
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M135 T" << current_extruder << "\n";
{
*output_stream << "M135 T" << current_extruder << new_line;
}
else
*output_stream << "T" << current_extruder << "\n";
{
*output_stream << "T" << current_extruder << new_line;
}
resetExtrusionValue(); // zero the E value on the new extruder, because a firmware bug in Griffin adjusted the E-value when performing a toolswitch (should be fixed as of 9 may 2016)
writeCode(extruder_attr[new_extruder].start_code.c_str());
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
currentPosition.z += 1;
}
void GCodeExport::writeCode(const char* str)
{
*output_stream << str;
if (flavor == EGCodeFlavor::BFB)
*output_stream << "\r\n";
else
*output_stream << "\n";
*output_stream << str << new_line;
}
void GCodeExport::writePrimeTrain()
{
*output_stream << "G280" << new_line;
}
void GCodeExport::writeFanCommand(double speed)
{
if (currentFanSpeed == speed)
@@ -456,16 +734,16 @@ void GCodeExport::writeFanCommand(double speed)
if (speed > 0)
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
}
else
{
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M127 T0\n";
*output_stream << "M127 T0" << new_line;
else
*output_stream << "M107\n";
*output_stream << "M107" << new_line;
}
currentFanSpeed = speed;
}
@@ -481,7 +759,7 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
*output_stream << "M104";
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << "\n";
*output_stream << " S" << temperature << new_line;
extruder_attr[extruder].currentTemperature = temperature;
}
@@ -491,17 +769,18 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << "\n";
*output_stream << temperature << new_line;
}
void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* endCode)
void GCodeExport::finalize(const char* endCode)
{
writeFanCommand(0);
setZ(maxObjectHeight + 5000);
writeMove(Point3(0,0,maxObjectHeight + 5000) + getPositionXY(), moveSpeed, 0);
writeCode(endCode);
log("Print time: %d\n", int(getTotalPrintTime()));
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
long print_time = getTotalPrintTime();
int mat_0 = getTotalFilamentUsed(0);
log("Print time: %d\n", print_time);
log("Print time (readable): %dh %dm %ds\n", print_time / 60 / 60, (print_time / 60) % 60, print_time % 60);
log("Filament: %d\n", mat_0);
for(int n=1; n<MAX_EXTRUDERS; n++)
if (getTotalFilamentUsed(n) > 0)
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
@@ -509,3 +788,4 @@ void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* en
}
}//namespace cura
+255 -104
Ver Arquivo
@@ -6,89 +6,130 @@
#include <deque> // for extrusionAmountAtPreviousRetractions
#include <sstream> // for stream.str()
#include "settings.h"
#include "settings/settings.h"
#include "utils/intpoint.h"
#include "utils/NoCopy.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "PrintFeature.h"
#include "commandSocket.h"
namespace cura {
/*!
* Coasting configuration used during printing.
* Can differ per extruder.
*
* Might be used in the future to have different coasting per feature, e.g. outer wall only.
*/
struct CoastingConfig
{
bool coasting_enable;
double coasting_volume_move;
double coasting_speed_move;
double coasting_min_volume_move;
double coasting_volume_retract;
double coasting_speed_retract;
double coasting_min_volume_retract;
bool coasting_enable; //!< Whether coasting is enabled on the extruder to which this config is attached
double coasting_volume; //!< The volume leeked when printing without feeding
double coasting_speed; //!< A modifier (0-1) on the last used travel speed to move slower during coasting
double coasting_min_volume; //!< The minimal volume printed to build up enough pressure to leek the coasting_volume
};
/*!
* The retraction configuration used in the GCodePathConfig of each feature (and the travel config)
*/
class RetractionConfig
{
public:
double amount; //!< The amount retracted
double speed; //!< The speed with which to retract
double primeSpeed; //!< the speed with which to unretract
double primeAmount; //!< the amount of material primed after unretracting
double distance; //!< The distance retracted (in mm)
double speed; //!< The speed with which to retract (in mm/s)
double primeSpeed; //!< the speed with which to unretract (in mm/s)
double prime_volume; //!< the amount of material primed after unretracting (in mm^3)
int zHop; //!< the amount with which to lift the head during a retraction-travel
int retraction_min_travel_distance; //!<
double retraction_extrusion_window;
int retraction_count_max;
int retraction_min_travel_distance; //!< Minimal distance traversed to even consider retracting (in micron)
double retraction_extrusion_window; //!< Window of mm extruded filament in which to limit the amount of retractions
int retraction_count_max; //!< The maximum amount of retractions allowed to occur in the RetractionConfig::retraction_extrusion_window
};
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
/*!
* The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
*/
class GCodePathConfig
{
private:
double speed; //!< movement speed
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
double speed; //!< current movement speed (mm/s) (modified by layer_nr etc.)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow in %
int layer_thickness; //!< layer height
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
double flow; //!< extrusion flow modifier in %
int layer_thickness; //!< layer height in micron
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line traversed
public:
const char* name;
bool spiralize;
RetractionConfig *const retraction_config;
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
void setSpeed(double speed)
PrintFeatureType type; //!< name of the feature type
RetractionConfig *const retraction_config; //!< The retraction configuration to use when retracting after a part of this feature has been printed.
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type)
: speed_iconic(0)
, speed(0)
, line_width(0)
, extrusion_mm3_per_mm(0.0)
, type(type)
, retraction_config(retraction_config)
{
}
/*!
* Initialize some of the member variables.
*
* \warning GCodePathConfig::setLayerHeight still has to be called before this object can be used.
*
* \param speed The regular speed with which to print this feature
* \param line_width The line width for this feature
* \param flow The flow modifier to apply to the extruded filament when printing this feature
*/
void init(double speed, int line_width, double flow)
{
speed_iconic = speed;
this->speed = speed;
}
void setLineWidth(int line_width)
{
this->line_width = line_width;
calculateExtrusion();
this->flow = flow;
}
/*!
* Set the layer height and (re)compute the extrusion_per_mm
*/
void setLayerHeight(int layer_height)
{
this->layer_thickness = layer_height;
calculateExtrusion();
}
void setFlow(double flow)
{
this->flow = flow;
calculateExtrusion();
}
/*!
* Set the speed to somewhere between the @p min_speed and the speed_iconic.
*
* This functions should not be called with @p layer_nr > @p max_speed_layer !
*
* \param min_speed The speed at layer zero
* \param layer_nr The layer number
* \param max_speed_layer The layer number for which the speed_iconic should be used.
*/
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
{
speed = (speed*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
speed = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
* Set the speed to the iconic speed, i.e. the normal speed of the feature type for which this is a config.
*/
void setSpeedIconic()
{
speed = speed_iconic;
}
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM()
{
return extrusion_mm3_per_mm;
}
/*!
* Get the movement speed in mm/s
*/
double getSpeed()
{
return speed;
@@ -98,7 +139,17 @@ public:
{
return line_width;
}
bool isTravelPath()
{
return line_width == 0;
}
double getFlowPercentage()
{
return flow;
}
private:
void calculateExtrusion()
{
@@ -108,68 +159,130 @@ private:
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
// Any customizations on GCodes flavors are done in this class.
class GCodeExport
class GCodeExport : public NoCopy
{
private:
struct ExtruderTrainAttributes
{
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string start_code;
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
double extruderSwitchRetraction;
int extruderSwitchRetractionSpeed;
int extruderSwitchPrimeSpeed;
RetractionConfig extruder_switch_retraction_config; //!< Retraction configuration used when performing extruder switches
double totalFilament; //!< total filament used per extruder in mm^3
int currentTemperature;
int initial_temp; //!< Temperature this nozzle needs to be at the start of the print.
double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values)
double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle.
double prime_volume; //!< Amount of material (in mm^3) to be primed after an unretration (due to oozing and/or coasting)
double last_retraction_prime_speed; //!< The last prime speed (in mm/s) of the to-be-primed amount
std::deque<double> extruded_volume_at_previous_n_retractions; // in mm^3
ExtruderTrainAttributes()
: nozzle_offset(0,0)
, extruderCharacter(0)
, start_code("")
, end_code("")
, filament_area(0)
, extruderSwitchRetraction(0.0)
, extruderSwitchRetractionSpeed(0)
, extruderSwitchPrimeSpeed(0)
, totalFilament(0)
, currentTemperature(0)
, initial_temp(0)
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
, last_retraction_prime_speed(0.0)
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
unsigned int extruder_count;
bool use_extruder_offset_to_offset_coords;
std::ostream* output_stream;
double extrusion_amount; // in mm or mm^3
std::deque<double> extrusion_amount_at_previous_n_retractions; // in mm or mm^3
Point3 currentPosition;
Point3 startPosition;
double currentSpeed;
int zPos;
bool isRetracted;
bool isZHopped;
Point3 machine_dimensions;
std::string machine_name;
std::ostream* output_stream;
std::string new_line;
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
Point3 currentPosition;
double currentSpeed; //!< The current speed (F values / 60) in mm/s
int zPos; // TODO: why is this different from currentPosition.z ? zPos is set every layer, while currentPosition.z is set every move. However, the z position is generally not changed within a layer!
int isZHopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts)
double last_coasted_amount_mm3; //!< The coasted amount of filament to be primed on the first next extrusion. (same type as GCodeExport::extrusion_amount)
double retractionPrimeSpeed;
int current_extruder;
int currentFanSpeed;
EGCodeFlavor flavor;
double totalPrintTime;
double total_print_time_per_feature[(unsigned int)EPrintFeature::ENUM_COUNT];
double totalPrintTime; //!< The total estimated print time in seconds
TimeEstimateCalculator estimateCalculator;
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
unsigned int layer_nr; //!< for sending travel data
int initial_bed_temp; //!< bed temperature at the beginning of the print.
protected:
/*!
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param e the value to convert
* \return the value converted to mm
*/
double eToMm(double e);
/*!
* Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm3 the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mm3ToE(double mm3);
/*!
* Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mmToE(double mm);
public:
GCodeExport();
~GCodeExport();
/*!
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
*
* \param print_time The total print time in seconds of the whole gcode (if known)
* \param filament_used The total mm^3 filament used for each extruder or a vector of the wrong size of unknown
* \param mat_ids The material ids for each material.
* \return The string representing the file header
*/
std::string getFileHeader(const double* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<int16_t>& mat_ids = std::vector<int16_t>());
void setLayerNr(unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
int getNozzleSize(int extruder_idx);
Point getExtruderOffset(int id);
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
@@ -179,38 +292,55 @@ public:
void setZ(int z);
void setLastCoastedAmountMM3(double last_coasted_amount) { this->last_coasted_amount_mm3 = last_coasted_amount; }
void addLastCoastedVolume(double last_coasted_volume)
{
extruder_attr[current_extruder].prime_volume += last_coasted_volume;
}
Point3 getPosition();
Point getPositionXY();
void resetStartPosition();
Point getStartPositionXY();
int getPositionZ();
int getExtruderNr();
void setFilamentDiameter(unsigned int n, int diameter);
double getFilamentArea(unsigned int extruder);
double getExtrusionAmountMM3(unsigned int extruder);
double getTotalFilamentUsed(int e);
double getCurrentExtrudedVolume();
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr);
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime();
double getTotalPrintTime(EPrintFeature print_feature);
void updateTotalPrintTime(EPrintFeature print_feature = EPrintFeature::UNCLASSIFIED);
void updateTotalPrintTime();
void resetTotalPrintTimeAndFilament();
void writeComment(std::string comment);
void writeTypeComment(const char* type);
void writeTypeComment(PrintFeatureType type);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
void writeLine(const char* line);
/*!
* Reset the current_e_value to prevent too high E values.
*
* The current extruded volume is added to the current extruder_attr.
*/
void resetExtrusionValue();
void writeDelay(double timeAmount);
@@ -220,8 +350,12 @@ public:
void writeMove(Point3 p, double speed, double extrusion_per_mm);
private:
void writeMove(int x, int y, int z, double speed, double extrusion_per_mm);
/*!
* The writeMove when flavor == BFB
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_per_mm);
public:
void writeRetraction(RetractionConfig* config, bool force=false);
void writeRetraction(RetractionConfig* config, bool force = false, bool extruder_switch = false);
void writeRetraction_extruderSwitch();
@@ -229,35 +363,52 @@ public:
void writeCode(const char* str);
/*!
* Write the gcode for priming the current extruder train so that it can be used.
*/
void writePrimeTrain();
void writeFanCommand(double speed);
void writeTemperatureCommand(int extruder, double temperature, bool wait = false);
void writeBedTemperatureCommand(double temperature, bool wait = false);
void preSetup(MeshGroup* settings)
{
for(int n=0; n<settings->getSettingAsCount("machine_extruder_count"); n++)
{
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[n].end_code = train->getSettingString("machine_extruder_end_code");
extruder_attr[n].extruderSwitchRetraction = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
}
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
}
void finalize(int maxObjectHeight, double moveSpeed, const char* endCode);
/*!
* Set member variables using the settings in \p settings
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
*/
void preSetup(MeshGroup* settings);
/*!
* Handle the initial (bed/nozzle) temperatures before any gcode is processed.
* These temperatures are set in the pre-print setup in the firmware.
*
* See FffGcodeWriter::processStartingCode
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
*/
void setInitialTemps(const MeshGroup& settings);
/*!
* Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
* This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
*
* \param extruder_nr The extruder number for which to better specify the temp
* \param temp The temp at which the nozzle should be at startup
*/
void setInitialTemp(int extruder_nr, double temp);
/*!
* Finish the gcode: turn fans off, write end gcode and flush all gcode left in the buffer.
*
* \param endCode The end gcode to be appended at the very end.
*/
void finalize(const char* endCode);
};
}
#endif//GCODEEXPORT_H
+613 -287
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+507 -70
Ver Arquivo
@@ -8,6 +8,9 @@
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
namespace cura
@@ -15,46 +18,367 @@ namespace cura
class SliceDataStorage;
/*!
* A gcode command to insert before a specific path.
*
* Currently only used for preheat commands
*/
struct NozzleTempInsert
{
const unsigned int path_idx; //!< The path before which to insert this command
double time_after_path_start; //!< The time after the start of the path, before which to insert the command // TODO: use this to insert command in between moves in a path!
int extruder; //!< The extruder for which to set the temp
double temperature; //!< The temperature of the temperature command to insert
bool wait; //!< Whether to wait for the temperature to be reached
NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start = 0.0)
: path_idx(path_idx)
, time_after_path_start(time_after_path_start)
, extruder(extruder)
, temperature(temperature)
, wait(wait)
{}
/*!
* Write the temperature command at the current position in the gcode.
* \param gcode The actual gcode writer
*/
void write(GCodeExport& gcode)
{
gcode.writeTemperatureCommand(extruder, temperature, wait);
}
};
class GCodePlanner; // forward declaration so that TimeMaterialEstimates can be a friend
/*!
* Time and material estimates for a portion of paths, e.g. layer, extruder plan, path.
*/
class TimeMaterialEstimates
{
friend class GCodePlanner;
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion)
double material; //!< Material used (in mm^3)
public:
/*!
* Basic contructor
*
* \param extrude_time Time in seconds occupied by extrusion
* \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion)
* \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion)
* \param material Material used (in mm^3)
*/
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material)
: extrude_time(extrude_time)
, unretracted_travel_time(unretracted_travel_time)
, retracted_travel_time(retracted_travel_time)
, material(material)
{
}
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
, retracted_travel_time(0.0)
, material(0.0)
{
}
/*!
* Set all estimates to zero.
*/
void reset()
{
extrude_time = 0.0;
unretracted_travel_time = 0.0;
retracted_travel_time = 0.0;
material = 0.0;
}
/*!
* Pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return The resulting estimates
*/
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
}
/*!
* In place pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return These estimates
*/
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other)
{
extrude_time += other.extrude_time;
unretracted_travel_time += other.unretracted_travel_time;
retracted_travel_time += other.retracted_travel_time;
material += other.material;
return *this;
}
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
*
* Each of the estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
* This causes the estimates in this instance to change. Each of the
* estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return A reference to this instance.
*/
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
/*!
* Get total time estimate. The different time estimate member values added together.
*
* \return the total of all different time estimate values
*/
double getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
/*!
* Get the total time during which the head is not retracted.
*
* This includes extrusion time and non-retracted travel time
*
* \return the total time during which the head is not retracted.
*/
double getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
/*!
* Get the total travel time.
*
* This includes the retracted travel time as well as the unretracted travel time.
*
* \return the total travel time.
*/
double getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
/*!
* Get the extrusion time.
*
* \return extrusion time.
*/
double getExtrudeTime() const
{
return extrude_time;
}
/*!
* Get the amount of material used in mm^3.
*
* \return amount of material
*/
double getMaterial() const
{
return material;
}
};
/*!
* A class for representing a planned path.
*
* A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc.
*
* This is a compact premature representation in which are line segments have the same config, i.e. the config of this path.
*
* In the final representation (gcode) each line segment may have different properties,
* which are added when the generated GCodePaths are processed.
*/
class GCodePath
{
public:
GCodePathConfig* config; //!< The configuration settings of the path.
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
int extruder; //!< The extruder used for this path.
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
/*!
* Whether this config is the config of a travel path.
*
* \return Whether this config is the config of a travel path.
*/
bool isTravelPath()
{
return config->isTravelPath();
}
/*!
* Get the material flow in mm^3 per mm traversed.
*
* \warning Can only be called after the layer height has been set (which is done while writing the gcode!)
*
* \return The flow
*/
double getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
};
/*!
* An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train.
*
* It allows for temperature command inserts which can be inserted in between paths.
*/
class ExtruderPlan
{
public:
std::vector<GCodePath> paths; //!< The paths planned for this extruder
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
int extruder; //!< The extruder used for this paths in the current plan.
double required_temp; //!< The required temperature at the start of this extruder plan.
TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan.
/*!
* Simple contructor.
*
* \warning Doesn't set the required temperature yet.
*
* \param extruder The extruder number for which this object is a plan.
*/
ExtruderPlan(int extruder)
: extruder(extruder)
, required_temp(-1)
{
}
/*!
* Add a new Insert, constructed with the given arguments
*
* \see NozzleTempInsert
*
* \param contructor_args The arguments for the constructor of an insert
*/
template<typename... Args>
void insertCommand(Args&&... contructor_args)
{
inserts.emplace_back(contructor_args...);
}
/*!
* Insert the inserts into gcode which should be inserted before \p path_idx
*
* \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleInserts(unsigned int& path_idx, GCodeExport& gcode)
{
while ( ! inserts.empty() && path_idx >= inserts.front().path_idx)
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
inserts.front().write(gcode);
inserts.pop_front();
}
}
/*!
* Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan
*
* Inserts temperature commands which should be inserted _after_ the last path.
* Also inserts all temperatures which should have been inserted earlier,
* but for which ExtruderPlan::handleInserts hasn't been called correctly.
*
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleAllRemainingInserts(GCodeExport& gcode)
{
while ( ! inserts.empty() )
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
NozzleTempInsert& insert = inserts.front();
assert(insert.path_idx == paths.size());
insert.write(gcode);
inserts.pop_front();
}
}
};
class LayerPlanBuffer; // forward declaration to prevent circular dependency
/*!
* The GCodePlanner class stores multiple moves that are planned.
*
*
* It facilitates the combing to keep the head inside the print.
* It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
*
* A GCodePlanner is also knows as a 'layer plan'.
*
*/
class GCodePlanner
class GCodePlanner : public NoCopy
{
friend class LayerPlanBuffer;
private:
GCodeExport& gcode;
SliceDataStorage& storage;
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
Point lastPosition;
std::vector<GCodePath> paths;
int layer_nr; //!< The layer number of this layer plan
bool was_combing;
bool is_going_to_comb;
int z;
int layer_thickness;
Point start_position;
Point lastPosition;
std::vector<ExtruderPlan> extruder_plans; //!< should always contain at least one ExtruderPlan
bool was_inside; //!< Whether the last planned (extrusion) move was inside a layer part
bool is_inside; //!< Whether the destination of the next planned travel move is inside a layer part
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
Comb* comb;
RetractionConfig* last_retraction_config;
GCodePathConfig travelConfig; //!< The config used for travel moves (only the speed and retraction config are set!)
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings;
double extrudeSpeedFactor;
double travelSpeedFactor; // TODO: remove this unused var?
int currentExtruder;
double travelSpeedFactor;
double fan_speed;
double extraTime;
double totalPrintTime;
@@ -65,10 +389,12 @@ private:
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
*
* \param config The config used for the path returned
* \param space_fill_type The type of space filling which this path employs
* \param flow (optional) A ratio for the extrusion speed
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
@@ -82,26 +408,78 @@ private:
*/
void forceNewPathStart();
public:
/*
/*!
*
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
void setCombing(bool going_to_comb);
private:
/*!
* Compute the boundary within which to comb, or to move into when performing a retraction.
* \param combing_mode Whether combing is enabled and full or within infill only.
* \return the comb_boundary_inside
*/
Polygons computeCombBoundaryInside(CombingMode combing_mode);
public:
int getLayerNr()
{
return layer_nr;
}
Point getLastPosition()
{
return lastPosition;
}
/*!
* return whether the last position planned was inside the mesh (used in combing)
*/
bool getIsInsideMesh()
{
return was_inside;
}
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
}
/*!
* Set whether the next destination is inside a layer part or not.
*
* Features like infill, walls, skin etc. are considered inside.
* Features like prime tower and support are considered outside.
*/
void setIsInside(bool going_to_comb);
bool setExtruder(int extruder);
/*!
* Get the last planned extruder.
*/
int getExtruder()
{
return currentExtruder;
return extruder_plans.back().extruder;
}
void setExtrudeSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1.0;
this->extrudeSpeedFactor = speedFactor;
}
double getExtrudeSpeedFactor()
@@ -117,16 +495,12 @@ public:
{
return this->travelSpeedFactor;
}
/*!
* Whether the current retracted path is to be an extruder switch retraction.
* This function is used to avoid a G10 S1 after a G10.
*
* \param path_idx The index of the current retracted path
* \return Whether the path should be an extgruder switch retracted path
*/
bool makeRetractSwitchRetract(unsigned int path_idx);
void setFanSpeed(double _fan_speed)
{
fan_speed = _fan_speed;
}
/*!
* Add a travel path to a certain point, retract if needed and when avoiding boundary crossings:
* avoiding obstacles and comb along the boundary of parts.
@@ -144,75 +518,138 @@ public:
* \param path (optional) The travel path to which to add the point \p p
*/
void addTravel_simple(Point p, GCodePath* path = nullptr);
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
*
* \param p The point to extrude to
* \param config The config with which to extrude
* \param space_fill_type Of what space filling type this extrusion move is a part
* \param flow A modifier of the extrusion width which would follow from the \p config
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
*/
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST);
/*!
* Add polygon to the gcode starting at vertex \p startIdx
* \param polygon The polygon
* \param startIdx The index of the starting vertex of the \p polygon
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
*/
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
/*!
* Add polygons to the gcode with optimized order.
*
* When \p spiralize is true, each polygon will gradually increase from a z corresponding to this layer to the z corresponding to the next layer.
* Doing this for each polygon means there is a chance for the print head to crash into already printed parts,
* but doing it for the last polygon only would mean you are printing half of the layer in non-spiralize mode,
* while each layer starts with a different part.
* Two towers would result in alternating spiralize and non-spiralize layers.
*
* \param polygons The polygons
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param z_seam_type The seam type / poly start optimizer
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
*/
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
/*!
* Add lines to the gcode with optimized order.
* \param polygons The lines
* \param config The config of the lines
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
void getNaiveTimeEstimates(double& travelTime, double& extrudeTime);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
* Write the planned paths to gcode
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume_move The volume otherwise leaked during a normal move.
* \param coasting_speed_move The speed at which to move during move-coasting.
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
* \param coasting_speed_retract The speed at which to move during retract-coasting.
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
* \return Whether any GCode has been written for the path.
* \param gcode The gcode to write the planned paths to
*/
bool writePathWithCoasting(unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
*
* \param path The extrusion path to be written to GCode.
* \param path_next The next travel path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked.
* \param coasting_speed The speed at which to move during coasting.
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
void writeGCode(GCodeExport& gcode);
/*!
* Complete all GcodePathConfig s by
* - altering speed to conform to speed_layer_0
* - setting the layer_height (and thereby computing the extrusionMM3perMM)
*/
void completeConfigs();
/*!
* Interpolate between the initial layer speeds and the eventual speeds.
*/
void processInitialLayersSpeedup();
/*!
* Whether the current retracted path is to be an extruder switch retraction.
* This function is used to avoid a G10 S1 after a G10.
*
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index of the current retracted path
* \return Whether the path should be an extgruder switch retracted path
*/
bool makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked during a normal move.
* \param coasting_speed The speed at which to move during move-coasting.
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx_travel_after Index in GCodePlanner::paths to the travel move before which to do the retraction
*/
void writeRetraction(unsigned int path_idx_travel_after);
void writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the given retraction config.
* \param gcode The gcode to write the planned paths to
* \param extruder_switch_retract Whether to write an extruder switch retract
* \param retraction_config The config used.
*/
void writeRetraction(bool extruder_switch_retract, RetractionConfig* retraction_config);
void writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config);
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
void moveInsideCombBoundary(int arg1);
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*/
void processFanSpeedAndMinimalLayerTime();
/*!
* Add a travel move to the layer plan to move inside the current layer part by a given distance away from the outline.
* This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air.
*
* \param distance The distance to the comb boundary after we moved inside it.
*/
void moveInsideCombBoundary(int distance);
};
}//namespace cura
+153 -417
Ver Arquivo
@@ -2,12 +2,11 @@
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/AABB.h"
#include "utils/logoutput.h"
namespace cura {
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between)
void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
@@ -16,36 +15,21 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
switch(pattern)
{
case EFillMethod::GRID:
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
generateGridInfill(result_lines);
break;
case EFillMethod::LINES:
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
generateLineInfill(result_lines, line_distance, fill_angle);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
if (abs(extrusion_width - line_distance) < 10)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
}
else
{
generateConcentricInfill(*outline, result_polygons, line_distance);
}
outline_offsetted = in_outline.offset(outline_offset - infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
outline = &outline_offsetted;
generateConcentricInfill(*outline, result_polygons, line_distance);
break;
case EFillMethod::ZIG_ZAG:
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
break;
default:
logError("Fill pattern has unknown value.\n");
@@ -53,184 +37,105 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
}
}
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
PolygonRef r = outline[polyNr];
result.add(r);
}
Polygons next_outline;
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
}
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
PolygonRef r = outline[polyNr];
result.add(r);
}
result.add(outline);
outline = outline.offset(-inset_value);
}
}
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
void Infill::generateGridInfill(Polygons& result)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 90);
generateLineInfill(result, line_distance, fill_angle);
generateLineInfill(result, line_distance, fill_angle + 90);
}
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
void Infill::generateTriangleInfill(Polygons& result)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 60);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 120);
generateLineInfill(result, line_distance, fill_angle);
generateLineInfill(result, line_distance, fill_angle + 60);
generateLineInfill(result, line_distance, fill_angle + 120);
}
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
{
auto addLine = [&](Point from, Point to)
{
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
p.add(rotation_matrix.unapply(from));
p.add(rotation_matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0) return -1;
if (n > 0) return 1;
if (n < 0)
{
return -1;
}
if (n > 0)
{
return 1;
}
return 0;
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
{
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
{
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
{ // segment is too short to create infill
continue;
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
}
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
}
scanline_idx += 1;
}
}
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
*
* algorithm:
* 1) for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2) for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
*/
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
{
if (lineSpacing == 0) return;
if (in_outline.size() == 0) return;
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
bool safe_outline_offset = false;
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
bool safe_outline_offset = true;
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
{
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
if (connected_zigzags)
{
Point p1 = outline[poly_idx][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
}
p0 = p1;
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
else
{
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
{
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
}
/*!
* adapted from generateLineInfill(.)
*
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
* sigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
/*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
@@ -239,288 +144,119 @@ void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extr
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* rough explanation of the zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connect_zigzags])
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
*
*
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
*
* start boundary from even scanline! :D
*
*
* _____
* | | | ,
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
*/
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
// if (in_outline.size() == 0) return;
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
Polygons empty;
Polygons outline = in_outline.difference(empty); // copy
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
if (line_distance == 0)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
Point lastPoint = p0;
for(unsigned int i=0; i < outline[polyNr].size(); i++)
return;
}
if (in_outline.size() == 0)
{
return;
}
Polygons outline;
if (outline_offset != 0)
{
outline = in_outline.offset(outline_offset);
}
else
{
outline = in_outline;
}
outline = outline.offset(infill_overlap);
if (outline.size() == 0)
{
return;
}
outline.applyMatrix(rotation_matrix);
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / line_distance;
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
{
cut_list.push_back(std::vector<int64_t>());
}
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
{
PolygonRef poly = outline[poly_idx];
Point p0 = poly.back();
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
lastPoint = p1;
Point p1 = poly[point_idx];
if (p1.X == p0.X)
{
zigzag_connector_processor.registerVertex(p1);
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
}
else
{
int x = scanline_idx * lineSpacing;
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
{
int x = scanline_idx * line_distance;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
addLine(lastPoint, Point(x,y));
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
{ // add whole unevenBoundarySegment (including the just obtained point)
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
{
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
}
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
unevenBoundarySegment.clear();
}
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
unevenBoundarySegment.push_back(Point(x,y));
else
unevenBoundarySegment.clear();
}
lastPoint = Point(x,y);
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
}
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
}
if (!isFirstBoundarySegment)
{
if (isEvenScanSegment)
addLine(lastPoint, p1);
else if (connect_zigzags)
unevenBoundarySegment.push_back(p1);
}
lastPoint = p1;
zigzag_connector_processor.registerVertex(p1);
p0 = p1;
}
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
{
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
else if (!firstBoundarySegmentEndsInEven)
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
}
if (cutList.size() == 0) return;
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
zigzag_connector_processor.registerPolyFinished();
}
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
if (cut_list.size() == 0)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
else boundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && !isEvenScanSegment)
{ // add whole boundarySegment (including the just obtained point)
for (unsigned int p = 1; p < boundarySegment.size(); p++)
{
addLine(boundarySegment[p-1], boundarySegment[p]);
}
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
boundarySegment.clear();
}
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
{
boundarySegment.clear();
boundarySegment.emplace_back(x,y);
} else
boundarySegment.clear();
}
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
boundarySegment.emplace_back(x,y);
}
}
if (!isFirstBoundarySegment && isEvenScanSegment)
boundarySegment.push_back(p1);
p0 = p1;
}
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
return;
}
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
{
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
}
}//namespace cura
+149 -29
Ver Arquivo
@@ -3,49 +3,169 @@
#define INFILL_H
#include "utils/polygon.h"
#include "settings.h"
#include "settings/settings.h"
// #include "ZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessor.h"
#include "infill/NoZigZagConnectorProcessor.h"
#include "infill/ActualZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
namespace cura {
namespace cura
{
class Infill
{
EFillMethod pattern;
const Polygons& in_outline;
int outlineOffset;
bool avoidOverlappingPerimeters;
int extrusion_width;
int line_distance;
double infill_overlap;
double fill_angle;
bool connect_zigzags;
bool use_endPieces;
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
int infill_line_width; //!< The line width of the infill lines to generate
int line_distance; //!< The distance between two infill lines / polygons
int infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
: pattern(pattern)
, in_outline(in_outline)
, outlineOffset(outlineOffset)
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
, extrusion_width(extrusion_width)
, outline_offset(outline_offset)
, infill_line_width(infill_line_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connect_zigzags(connect_zigzags)
, use_endPieces(use_endPieces)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
}
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
};
/*!
* Generate the infill.
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \param result_lines (output) The resulting line segments (from linear infill types)
*/
void generate(Polygons& result_polygons, Polygons& result_lines);
private:
/*!
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
/*!
* Generate a rectangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateTriangleInfill(Polygons& result);
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
/*!
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
* \param result (output) The resulting lines
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
*/
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
*
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
*
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
*/
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
/*!
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
*
*
* v disconnected end piece: leave out last line segment
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
*
*
* v connected end piece
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
*/
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
};
}//namespace cura
#endif//INFILL_H
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
/*!
* In contrast to NoZigZagConnectorProcessor
*/
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
{
protected:
/*!
* The line segments belonging the zigzag connector to which the very first vertex belongs.
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
*
* Because the boundary polygon may start in in the middle of a zigzag connector,
*/
std::vector<Point> first_zigzag_connector;
/*!
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
*/
std::vector<Point> zigzag_connector;
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
, is_first_zigzag_connector(true)
, first_zigzag_connector_ends_in_even_scanline(true)
, last_scanline_is_even(false)
{
}
};
} // namespace cura
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
+25
Ver Arquivo
@@ -0,0 +1,25 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "NoZigZagConnectorProcessor.h"
namespace cura
{
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
{
}
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
}
void NoZigZagConnectorProcessor::registerPolyFinished()
{
}
} // namespace cura
+28
Ver Arquivo
@@ -0,0 +1,28 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
namespace cura
{
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
{
public:
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
+154
Ver Arquivo
@@ -0,0 +1,154 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
namespace cura
{
/*!
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
*
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
* at the same time as the lines are created.
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connected_zigzags])
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
* The inheritance structure is as such:
* ZigzagConnectorProcessor
* / \ .
* / \ .
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
* / \ for lines infill .
* / \ .
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
* / \ for zigzag infill (without end pieces) .
* / \ .
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
*
* v v zigzag connectors
* <--
* :___: : < scanlines
* | | |
* | | | < infill lines along scanlines
* | |___|
* : : :
* --> winding order of polygon
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
*
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
* ^ connected end piece
* include a boundary segment also if it starts in an odd scanline and ends odd,
* or starts in an even scanline and ends in an even scanline,
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
*
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
*/
class ZigzagConnectorProcessor
{
protected:
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
Polygons& result; //!< The result of the computation
virtual ~ZigzagConnectorProcessor()
{}
/*!
* Add a line to the result bu unapplying the rotation rotation_matrix.
*
* \param from The one end of the line segment
* \param to The other end of the line segment
*/
void addLine(Point from, Point to)
{
PolygonRef line_poly = result.newPoly();
line_poly.add(rotation_matrix.unapply(from));
line_poly.add(rotation_matrix.unapply(to));
}
/*!
* Basic constructor. Inheriting children should call this constructor.
*
* \param rotation_matrix The rotation matrix used to enforce the infill angle
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
*/
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: rotation_matrix(rotation_matrix)
, result(result)
{}
public:
/*!
* Handle the next vertex on the outer boundary.
* \param vertex The vertex
*/
virtual void registerVertex(const Point& vertex) = 0;
/*!
* Handle the next intersection between a scanline and the outer boundary.
*
* \param intersection The intersection
* \param scanline_is_even Whether the scanline was even
*/
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
/*!
* Handle the end of a polygon and prepare for the next.
* This function should reset all member variables.
*/
virtual void registerPolyFinished() = 0;
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
@@ -0,0 +1,75 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even)
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|| is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,27 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
@@ -0,0 +1,79 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole oddBoundarySegment (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
// skip the last segment to the [intersection]
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// write very last line segment if needed
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only add last element if boundary segment ends in odd scanline
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,26 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
@@ -0,0 +1,27 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
addLine(last_connector_point, vertex);
}
else
{ // it's yet unclear whether the line segment should be included, so we store it until we know
zigzag_connector.push_back(vertex);
}
last_connector_point = vertex;
}
} // namespace cura
@@ -0,0 +1,32 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
namespace cura
{
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
{
protected:
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
, last_connector_point(0,0)
{
}
public:
void registerVertex(const Point& vertex);
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
@@ -0,0 +1,72 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorNoEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{
zigzag_connector.push_back(vertex);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
{
zigzag_connector.push_back(intersection);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
{
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
{
public:
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
-74
Ver Arquivo
@@ -1,74 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "utils/polygonUtils.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
if (line_width_0 < nozzle_width)
{
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
else
{
PolygonUtils::offsetSafe(part->outline, - line_width_0/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
} else if (i == 1)
{
if (line_width_0 < nozzle_width)
{
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
PolygonUtils::offsetSafe(part->insets[0], nozzle_width/2 - line_width_0 - line_width_x/2, offset_from_first_boundary_for_edge_of_outer_wall, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
else
{
PolygonUtils::offsetSafe(part->insets[0], -line_width_0/2 - line_width_x/2, -line_width_0/2, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
} else
{
PolygonUtils::offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], nozzle_width, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
-41
Ver Arquivo
@@ -1,41 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by \p line_width_0 instead of the first,
* which leads to better results for a smaller \p line_width_0 than \p line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+11 -11
Ver Arquivo
@@ -1,8 +1,8 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "layerPart.h"
#include "settings.h"
#include "Progress.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "utils/SVG.h" // debug output
@@ -26,15 +26,15 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
if (union_all_remove_holes)
{
for(unsigned int i=0; i<layer->polygonList.size(); i++)
for(unsigned int i=0; i<layer->polygons.size(); i++)
{
if (layer->polygonList[i].orientation())
layer->polygonList[i].reverse();
if (layer->polygons[i].orientation())
layer->polygons[i].reverse();
}
}
std::vector<PolygonsPart> result;
result = layer->polygonList.splitIntoParts(union_layers || union_all_remove_holes);
result = layer->polygons.splitIntoParts(union_layers || union_all_remove_holes);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.emplace_back();
@@ -42,14 +42,14 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
storage.layers.push_back(SliceLayer());
storage.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
storage.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(storage.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
mesh.layers.push_back(SliceLayer());
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
}
}
+2 -2
Ver Arquivo
@@ -22,9 +22,9 @@ namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers = true, int layer_nr = -1);
void layerparts2HTML(SliceDataStorage& mesh, const char* filename, bool all_layers = true, int layer_nr = -1);
}//namespace cura
+116 -31
Ver Arquivo
@@ -16,7 +16,9 @@
#include "utils/string.h"
#include "FffProcessor.h"
#include "settingRegistry.h"
#include "settings/SettingRegistry.h"
#include "settings/SettingsToGV.h"
namespace cura
{
@@ -28,23 +30,25 @@ void print_usage()
cura::logError("CuraEngine help\n");
cura::logError("\tShow this help message\n");
cura::logError("\n");
cura::logError("CuraEngine connect <host>[:<port>] [-j <settings.json>]\n");
cura::logError("CuraEngine connect <host>[:<port>] [-j <settings.def.json>]\n");
cura::logError(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults\n");
cura::logError(" -j<settings.def.json>\n\tLoad settings.json file to register all settings and their defaults\n");
cura::logError("\n");
cura::logError("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
cura::logError("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e<extruder_nr>] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
cura::logError(" -v\n\tIncrease the verbose level (show log messages).\n");
cura::logError(" -p\n\tLog progress information.\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults.\n");
cura::logError(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n");
cura::logError(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
cura::logError(" -l <model_file>\n\tLoad an STL model. \n");
cura::logError(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
cura::logError(" -e\n\tAdd a new extruder train.\n");
cura::logError(" -e<extruder_nr>\n\tSwitch setting focus to the extruder train with the given number.\n");
cura::logError(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
cura::logError(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
cura::logError("\n");
cura::logError("The settings are appended to the last supplied object:\n");
cura::logError("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e [extruder train settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
cura::logError("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
cura::logError("\n");
cura::logError("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search paths delimited by a (semi-)colon.\n");
cura::logError("\n");
}
@@ -66,7 +70,7 @@ void print_call(int argc, char **argv)
void connect(int argc, char **argv)
{
CommandSocket* commandSocket = new CommandSocket();
CommandSocket::instantiate();
std::string ip;
int port = 49674;
@@ -77,7 +81,6 @@ void connect(int argc, char **argv)
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
for(int argn = 3; argn < argc; argn++)
{
char* str = argv[argn];
@@ -92,7 +95,7 @@ void connect(int argc, char **argv)
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], FffProcessor::getInstance()))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
@@ -107,7 +110,7 @@ void connect(int argc, char **argv)
}
}
commandSocket->connect(ip, port);
CommandSocket::getInstance()->connect(ip, port);
}
void slice(int argc, char **argv)
@@ -116,11 +119,12 @@ void slice(int argc, char **argv)
FMatrix3x3 transformation; // the transformation applied to a model when loaded
MeshGroup meshgroup(FffProcessor::getInstance());
MeshGroup* meshgroup = new MeshGroup(FffProcessor::getInstance());
int extruder_train_nr = 0;
SettingsBase* last_extruder_train = meshgroup.getExtruderTrain(0);
SettingsBase* last_extruder_train = meshgroup->createExtruderTrain(0);
// extruder defaults cannot be loaded yet cause no json has been parsed
SettingsBase* last_settings_object = FffProcessor::getInstance();
for(int argn = 2; argn < argc; argn++)
{
@@ -134,25 +138,29 @@ void slice(int argc, char **argv)
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
meshgroup.finalize();
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup.getExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // also initializes yet uninitialized extruder trains
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, train);
}
//start slicing
FffProcessor::getInstance()->processMeshGroup(&meshgroup);
FffProcessor::getInstance()->processMeshGroup(meshgroup);
// initialize loading of new meshes
FffProcessor::getInstance()->time_keeper.restart();
meshgroup = MeshGroup(FffProcessor::getInstance());
last_settings_object = &meshgroup;
delete meshgroup;
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_extruder_train = meshgroup->createExtruderTrain(0);
last_settings_object = meshgroup;
SettingRegistry::getInstance()->loadExtruderJSONsettings(0, last_extruder_train);
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
@@ -169,7 +177,7 @@ void slice(int argc, char **argv)
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], last_settings_object))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
@@ -177,8 +185,9 @@ void slice(int argc, char **argv)
case 'e':
str++;
extruder_train_nr = int(*str - '0'); // TODO: parse int instead (now "-e10"="-e:" , "-e11"="-e;" , "-e12"="-e<" .. etc)
last_settings_object = meshgroup.getExtruderTrain(extruder_train_nr);
last_extruder_train = meshgroup.getExtruderTrain(extruder_train_nr);
last_settings_object = meshgroup->createExtruderTrain(extruder_train_nr);
last_extruder_train = last_settings_object;
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_train_nr, last_extruder_train);
break;
case 'l':
argn++;
@@ -186,13 +195,13 @@ void slice(int argc, char **argv)
log("Loading %s from disk...\n", argv[argn]);
// transformation = // TODO: get a transformation from somewhere
if (!loadMeshIntoMeshGroup(&meshgroup, argv[argn], transformation, last_extruder_train))
if (!loadMeshIntoMeshGroup(meshgroup, argv[argn], transformation, last_extruder_train))
{
logError("Failed to load model: %s\n", argv[argn]);
}
else
{
last_settings_object = &(meshgroup.meshes.back()); // pointer is valid until a new object is added, so this is OK
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
}
break;
case 'o':
@@ -204,7 +213,7 @@ void slice(int argc, char **argv)
}
break;
case 'g':
last_settings_object = &meshgroup;
last_settings_object = meshgroup;
case 's':
{
//Parse the given setting and store it.
@@ -238,9 +247,11 @@ void slice(int argc, char **argv)
}
}
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_train_nr++)
int extruder_count = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
for (extruder_train_nr = 0; extruder_train_nr < extruder_count; extruder_train_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup.getExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // also initializes yet uninitialized extruder trains
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_train_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_train_nr, train);
}
@@ -249,12 +260,12 @@ void slice(int argc, char **argv)
#endif
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
meshgroup.finalize();
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
//start slicing
FffProcessor::getInstance()->processMeshGroup(&meshgroup);
FffProcessor::getInstance()->processMeshGroup(meshgroup);
#ifndef DEBUG
}catch(...){
cura::logError("Unknown exception\n");
@@ -263,7 +274,8 @@ void slice(int argc, char **argv)
#endif
//Finalize the processor, this adds the end.gcode. And reports statistics.
FffProcessor::getInstance()->finalize();
delete meshgroup;
}
}//namespace cura
@@ -317,6 +329,79 @@ int main(int argc, char **argv)
{
slice(argc, argv);
}
else if (stringcasecompare(argv[1], "help") == 0)
{
print_usage();
exit(0);
}
else if (stringcasecompare(argv[1], "analyse") == 0)
{ // CuraEngine analyse [json] [output.gv] [engine_settings] -[p|i|e|w]
// p = show parent-child relations
// i = show inheritance function
// e = show error functions
// w = show warning functions
// dot refl_ff.gv -Tpng > rafl_ff_dotted.png
// see meta/HOWTO.txt
bool parent_child_viz = false;
bool inherit_viz = false;
bool warning_viz = false;
bool error_viz = false;
if (argc >= 6)
{
char* str = argv[5];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'p':
parent_child_viz = true;
break;
case 'i':
inherit_viz = true;
break;
case 'e':
error_viz = true;
break;
case 'w':
warning_viz = true;
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
break;
}
}
}
}
else
{
cura::logError("\n");
cura::logError("usage:\n");
cura::logError("CuraEngine analyse <fdmPrinter.def.json> <output.gv> <engine_settings_list> -[p|i|e|w]\n");
cura::logError("\tGenerate a grpah to visualize the setting inheritance structure.\n");
cura::logError("\t<fdmPrinter.def.json>\n\tThe base seting definitions file.\n");
cura::logError("\t<output.gv>\n\tThe output file.\n");
cura::logError("\t<engine_settings_list>\n\tA text file with all setting keys used in the engine, separated by newlines.\n");
cura::logError("\t-[p|i|e|w]\n\tOptions for what to include in the visualization\n");
cura::logError("\t\tp\tVisualize the parent-child relationship.\n");
cura::logError("\t\ti\tVisualize inheritance function relationships.\n");
cura::logError("\t\te\tVisualize (max/min) error function relationships.\n");
cura::logError("\t\tw\tVisualize (max/min) warning function relationships.\n");
cura::logError("\n");
}
SettingsToGv gv_out(argv[3], argv[4], parent_child_viz, inherit_viz, error_viz, warning_viz);
if (gv_out.generate(std::string(argv[2])))
{
cura::logError("ERROR: Failed to analyse json file: %s\n", argv[2]);
}
exit(0);
}
else
{
cura::logError("Unknown command: %s\n", argv[1]);
+16 -7
Ver Arquivo
@@ -5,13 +5,17 @@ namespace cura
{
const int vertex_meld_distance = MM2INT(0.03);
static inline uint32_t pointHash(Point3& p)
/*!
* returns a hash for the location, but first divides by the vertex_meld_distance,
* so that any point within a box of vertex_meld_distance by vertex_meld_distance would get mapped to the same hash.
*/
static inline uint32_t pointHash(const Point3& p)
{
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
}
Mesh::Mesh(SettingsBaseVirtual* parent)
: SettingsBase(parent)
: SettingsBase(parent, std::string("mesh"))
{
}
@@ -55,16 +59,21 @@ void Mesh::finish()
}
}
Point3 Mesh::min()
Point3 Mesh::min() const
{
return aabb.min;
}
Point3 Mesh::max()
Point3 Mesh::max() const
{
return aabb.max;
}
AABB3D Mesh::getAABB() const
{
return aabb;
}
int Mesh::findIndexOfVertex(Point3& v)
int Mesh::findIndexOfVertex(const Point3& v)
{
uint32_t hash = pointHash(v);
@@ -107,7 +116,7 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
*/
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const
{
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
@@ -179,4 +188,4 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
return bestIdx;
}
}//namespace cura
}//namespace cura
+7 -6
Ver Arquivo
@@ -1,8 +1,8 @@
#ifndef MESH_H
#define MESH_H
#include "settings.h"
#include "utils/AABB.h"
#include "settings/settings.h"
#include "utils/AABB3D.h"
namespace cura
{
@@ -69,8 +69,9 @@ public:
void clear(); //!< clears all data
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
Point3 min() const; //!< min (in x,y and z) vertex of the bounding box
Point3 max() const; //!< max (in x,y and z) vertex of the bounding box
AABB3D getAABB() const; //!< Get the axis aligned bounding box
/*!
* Offset the whole mesh (all vertices and the bounding box).
@@ -85,12 +86,12 @@ public:
}
private:
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
int findIndexOfVertex(const Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
/*!
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*/
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx) const;
};
}//namespace cura
+49 -19
Ver Arquivo
@@ -6,15 +6,29 @@ namespace cura
void carveMultipleVolumes(std::vector<Slicer*> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int idx=0; idx < volumes.size(); idx++)
for (unsigned int volume_1_idx = 0; volume_1_idx < volumes.size(); volume_1_idx++)
{
for(unsigned int idx2=0; idx2<idx; idx2++)
Slicer& volume_1 = *volumes[volume_1_idx];
if (volume_1.mesh->getSettingBoolean("infill_mesh"))
{
for(unsigned int layerNr=0; layerNr < volumes[idx]->layers.size(); layerNr++)
continue;
}
for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++)
{
Slicer& volume_2 = *volumes[volume_2_idx];
if (volume_2.mesh->getSettingBoolean("infill_mesh"))
{
SlicerLayer& layer1 = volumes[idx]->layers[layerNr];
SlicerLayer& layer2 = volumes[idx2]->layers[layerNr];
layer1.polygonList = layer1.polygonList.difference(layer2.polygonList);
continue;
}
if (!volume_1.mesh->getAABB().hit(volume_2.mesh->getAABB()))
{
continue;
}
for (unsigned int layerNr = 0; layerNr < volume_1.layers.size(); layerNr++)
{
SlicerLayer& layer1 = volume_1.layers[layerNr];
SlicerLayer& layer2 = volume_2.layers[layerNr];
layer1.polygons = layer1.polygons.difference(layer2.polygons);
}
}
}
@@ -22,24 +36,40 @@ void carveMultipleVolumes(std::vector<Slicer*> &volumes)
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes, int overlap)
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0]->layers.size(); layerNr++)
if (volumes.size() < 2)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
return;
}
int offset_to_merge_other_merged_volumes = 20;
for (Slicer* volume : volumes)
{
int overlap = volume->mesh->getSettingInMicrons("multiple_mesh_overlap");
if (volume->mesh->getSettingBoolean("infill_mesh")
|| overlap == 0)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
fullLayer = fullLayer.unionPolygons(layer1.polygonList.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
continue;
}
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
for (unsigned int layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
layer1.polygonList = fullLayer.intersection(layer1.polygonList.offset(overlap / 2));
Polygons all_other_volumes;
for (Slicer* other_volume : volumes)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| !other_volume->mesh->getAABB().hit(volume->mesh->getAABB())
)
{
continue;
}
SlicerLayer& other_volume_layer = other_volume->layers[layer_nr];
all_other_volumes = all_other_volumes.unionPolygons(other_volume_layer.polygons.offset(offset_to_merge_other_merged_volumes));
}
all_other_volumes = all_other_volumes.offset(-offset_to_merge_other_merged_volumes);
SlicerLayer& volume_layer = volume->layers[layer_nr];
volume_layer.polygons.unionPolygons(all_other_volumes.intersection(volume_layer.polygons.offset(overlap / 2)));
}
}
}
+1 -1
Ver Arquivo
@@ -13,7 +13,7 @@ void carveMultipleVolumes(std::vector<Slicer*> &meshes);
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
* This generates some overlap in dual extrusion, for better bonding in touching parts.
*/
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes, int overlap);
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes);
}//namespace cura
+97 -106
Ver Arquivo
@@ -2,6 +2,7 @@
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/linearAlg2D.h"
#define INLINE static inline
@@ -15,17 +16,16 @@ void PathOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
float dist = vSize2f(poly[point_idx] - startPoint);
if (dist < bestDist)
{
best = i_point;
best = point_idx;
bestDist = dist;
}
}
@@ -37,46 +37,50 @@ void PathOrderOptimizer::optimize()
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
{
int best = -1;
int best_poly_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
continue;
}
assert (polygons[i_polygon].size() != 2);
assert (polygons[poly_idx].size() != 2);
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
if (dist < bestDist)
{
best = i_polygon;
best_poly_idx = poly_idx;
bestDist = dist;
}
}
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
{
assert(polygons[best].size() != 2);
assert(polygons[best_poly_idx].size() != 2);
prev_point = polygons[best][polyStart[best]];
prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];
picked[best] = true;
polyOrder.push_back(best);
picked[best_poly_idx] = true;
polyOrder.push_back(best_poly_idx);
}
else
{
logError("Failed to find next closest polygon.\n");
}
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
{
int poly_idx = polyOrder[n];
int poly_idx = polyOrder[order_idx];
int point_idx = getPolyStart(prev_point, poly_idx);
polyStart[poly_idx] = point_idx;
prev_point = polygons[poly_idx][point_idx];
@@ -99,22 +103,23 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
float best_point_score = std::numeric_limits<float>::infinity();
Point p0 = poly.back();
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
float dist = vSize2f(poly[i_point] - prev_point);
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
Point& p1 = poly[point_idx];
Point& p2 = poly[(point_idx + 1) % poly.size()];
int64_t dist = vSize2(p1 - prev_point);
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
// this score is in the order of 5 mm
if (dist + is_on_inside_corner_score < best_point_score)
{
best_point_idx = i_point;
bestDist = dist;
best_point_idx = point_idx;
best_point_score = dist + is_on_inside_corner_score;
}
p0 = p1;
}
return best_point_idx;
}
@@ -147,132 +152,118 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
*/
void LineOrderOptimizer::optimize()
{
int gridSize = 5000; // the size of the cells in the hash grid.
int gridSize = 5000; // the size of the cells in the hash grid. TODO
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
int best_point_idx = -1;
float best_point_dist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
float dist = vSize2f(poly[point_idx] - startPoint);
if (dist < best_point_dist)
{
best = i_point;
bestDist = dist;
best_point_idx = point_idx;
best_point_dist = dist;
}
}
polyStart.push_back(best);
polyStart.push_back(best_point_idx);
assert(poly.size() == 2);
line_bucket_grid.insert(poly[0], i_polygon);
line_bucket_grid.insert(poly[1], i_polygon);
line_bucket_grid.insert(poly[0], poly_idx);
line_bucket_grid.insert(poly[1], poly_idx);
}
Point incommingPerpundicularNormal(0, 0);
Point incoming_perpundicular_normal(0, 0);
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
int best_line_idx = -1;
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
{
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
{
continue;
}
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
}
if (best == -1) /// if single-line-polygon hasn't been found yet
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
{
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
{
continue;
assert(polygons[i_polygon].size() == 2);
}
assert(polygons[poly_idx].size() == 2);
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
}
}
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
{
assert(polygons[best].size() == 2);
PolygonRef best_line = polygons[best_line_idx];
assert(best_line.size() == 2);
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
prev_point = polygons[best][endIdx];
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
int line_start_point_idx = polyStart[best_line_idx];
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
Point& line_start = best_line[line_start_point_idx];
Point& line_end = best_line[line_end_point_idx];
prev_point = line_end;
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
picked[best] = true;
polyOrder.push_back(best);
picked[best_line_idx] = true;
polyOrder.push_back(best_line_idx);
}
else
logError("Failed to find next closest line.\n");
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int nr = polyOrder[n];
PolygonRef poly = polygons[nr];
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i=0;i<poly.size(); i++)
{
float dist = vSize2f(polygons[nr][i] - prev_point);
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
{
best = i;
bestDist = dist + dot_score;
}
logError("Failed to find next closest line.\n");
}
polyStart[nr] = best;
assert(poly.size() == 2);
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
}
}
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
{
Point& p0 = polygons[poly_idx][0];
Point& p1 = polygons[poly_idx][1];
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
{ /// check distance to first point on line (0)
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 0;
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 0;
}
}
{ /// check distance to second point on line (1)
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
if (score < best_score)
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 1;
best = poly_idx;
best_score = score;
polyStart[poly_idx] = 1;
}
}
}
float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
{
return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
}
}//namespace cura
+29 -2
Ver Arquivo
@@ -4,7 +4,7 @@
#include <stdint.h>
#include "utils/polygon.h"
#include "settings.h"
#include "settings/settings.h"
namespace cura {
@@ -81,8 +81,35 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
/*!
* Update LineOrderOptimizer::polyStart if the current line is better than the current best.
*
* Besides looking at the distance from the previous line segment, we also look at the angle we make.
*
* We prefer 90 degree angles; 180 degree turn arounds are slow on machines where the jerk is limited.
* 0 degree (straight ahead) 'corners' occur only when a single infill line is interrupted,
* in which case the travel move might involve combing, which makes it rather longer.
*
* \param poly_idx[in] The index in LineOrderOptimizer::polygons for the current line to test
* \param best[in, out] The index of current best line
* \param best_score[in, out] The distance score for the current best line
* \param prev_point[in] The previous point from which to find the next best line
* \param incoming_perpundicular_normal[in] The direction of movement when the print head arrived at \p prev_point, turned 90 degrees CCW
*/
void updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal);
/*!
* Get a score to modify the distance score for measuring how good two lines follow each other.
*
* The angle score is symmetric in \p from and \p to; they can be exchanged without altering the result. (Code relies on this property)
*
* \param incoming_perpundicular_normal The direction in which the head was moving while printing the previous line, turned 90 degrees CCW
* \param from The one end of the next line
* \param to The other end of the next line
* \return A score measuring how good the angle is of the line between \p from and \p to when the previous line had a direction given by \p incoming_perpundicular_normal
*
*/
static float getAngleScore(Point incoming_perpundicular_normal, Point from, Point to);
};
}//namespace cura
+17 -20
Ver Arquivo
@@ -1,30 +1,28 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "Progress.h"
#include "commandSocket.h"
#include "utils/gettime.h"
#include "../commandSocket.h"
#include "../utils/gettime.h"
namespace cura {
double Progress::times [] =
{
0.0,
5.269,
1.533,
22.953,
51.009,
48.858,
154.62,
0.1
0.0, // START = 0,
5.269, // SLICING = 1,
1.533, // PARTS = 2,
71.811, // INSET_SKIN = 3
51.009, // SUPPORT = 4,
154.62, // EXPORT = 5,
0.1 // FINISH = 6
};
std::string Progress::names [] =
{
"start",
"slice",
"layerparts",
"inset",
"inset+skin",
"support",
"skin",
"export",
"process"
};
@@ -39,9 +37,8 @@ const Progress::Stage Progress::stages[] =
Progress::Stage::START,
Progress::Stage::SLICING,
Progress::Stage::PARTS,
Progress::Stage::INSET,
Progress::Stage::INSET_SKIN,
Progress::Stage::SUPPORT,
Progress::Stage::SKIN,
Progress::Stage::EXPORT,
Progress::Stage::FINISH
};
@@ -64,22 +61,22 @@ void Progress::init()
total_timing = accumulated_time;
}
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
{
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
if (command_socket)
if (CommandSocket::getInstance())
{
command_socket->sendProgress(percentage);
CommandSocket::getInstance()->sendProgress(percentage);
}
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
}
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
{
if (command_socket)
if (CommandSocket::getInstance())
{
command_socket->sendProgressStage(stage);
CommandSocket::getInstance()->sendProgressStage(stage);
}
if (time_keeper)
+9 -12
Ver Arquivo
@@ -4,14 +4,14 @@
#include <string>
#include "utils/logoutput.h"
#include "utils/gettime.h"
#include "../utils/logoutput.h"
#include "../utils/gettime.h"
namespace cura {
class CommandSocket;
#define N_PROGRESS_STAGES 8
#define N_PROGRESS_STAGES 7
/*!
* Class for handling the progress bar and the progress logging.
@@ -30,11 +30,10 @@ public:
START = 0,
SLICING = 1,
PARTS = 2,
INSET = 3,
INSET_SKIN = 3,
SUPPORT = 4,
SKIN = 5,
EXPORT = 6,
FINISH = 7
EXPORT = 5,
FINISH = 6
};
private:
static double times [N_PROGRESS_STAGES]; //!< Time estimates per stage
@@ -52,22 +51,20 @@ private:
public:
static void init(); //!< Initialize some values needed in a fast computation of the progress
/*!
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
*
* \param stage The current stage of processing
* \param progress_in_stage Any number giving the progress within the stage
* \param progress_in_stage_max The maximal value of \p progress_in_stage
* \param commandSocket The command socket over which to communicate the progress.
*/
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
/*!
* Message the progress stage over the command socket.
*
* \param stage The current stage
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
* \param commandSocket The command socket over which to communicate (optional)
*/
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
};
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_ESTIMATOR_H
#define PROGRESS_PROGRESS_ESTIMATOR_H
#include <vector>
namespace cura
{
/*
* ProgressEstimator is a finger-tree with ProgressEstimatorLinear as leaves.
*
* Each (non-leaf) node consists of a ProgressStageEstimator which consists of several stages.
*
* The structure of this tree is an oversimplification of the call graph of CuraEngine.
*
*/
class ProgressEstimator
{
public:
virtual double progress(int current_step) = 0;
virtual ~ProgressEstimator()
{
}
};
} // namespace cura
#endif // PROGRESS_PROGRESS_ESTIMATOR_H
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
#define PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
#include <vector>
#include "ProgressEstimator.h"
namespace cura
{
class ProgressEstimatorLinear : public ProgressEstimator
{
unsigned int total_steps;
public:
ProgressEstimatorLinear(unsigned int total_steps)
: total_steps(total_steps)
{
}
double progress(int current_step)
{
return double(current_step) / double(total_steps);
}
};
} // namespace cura
#endif // PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
+52
Ver Arquivo
@@ -0,0 +1,52 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ProgressStageEstimator.h"
namespace cura
{
ProgressStageEstimator::ProgressStageEstimator(std::vector< double >& relative_time_estimates)
: total_estimated_time(0)
, accumulated_estimate(0)
, current_stage_idx(-1)
{
stages.reserve(relative_time_estimates.size());
for (double relative_estimated_time : relative_time_estimates)
{
stages.emplace_back(relative_estimated_time);
total_estimated_time += relative_estimated_time;
}
}
ProgressStageEstimator::~ProgressStageEstimator()
{
for (ProgressStage& stage : stages)
{
delete stage.stage;
}
}
double ProgressStageEstimator::progress(int current_step)
{
ProgressStage& current_stage = stages[current_stage_idx];
return (accumulated_estimate + current_stage.stage->progress(current_step) * current_stage.relative_estimated_time) / total_estimated_time;
}
void ProgressStageEstimator::nextStage(ProgressEstimator* stage)
{
if (current_stage_idx >= int(stages.size()) - 1)
{
return;
}
if (current_stage_idx >= 0)
{
ProgressStage& current_stage = stages[current_stage_idx];
accumulated_estimate += current_stage.relative_estimated_time;
}
current_stage_idx++;
stages[current_stage_idx].stage = stage;
}
} // namespace cura
+54
Ver Arquivo
@@ -0,0 +1,54 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
#define PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
#include <vector>
#include "ProgressEstimator.h"
namespace cura
{
/*!
* A staged progress estimator which estimates each stage to have different times.
*/
class ProgressStageEstimator : public ProgressEstimator
{
struct ProgressStage
{
double relative_estimated_time;
ProgressEstimator* stage;
ProgressStage(double relative_estimated_time)
: relative_estimated_time(relative_estimated_time)
, stage(nullptr)
{
}
};
protected:
std::vector<ProgressStage> stages;
double total_estimated_time;
private:
double accumulated_estimate;
int current_stage_idx;
public:
ProgressStageEstimator(std::vector<double>& relative_time_estimates);
double progress(int current_step);
/*!
*
* \warning This class is responsible for deleting the \p stage
*
*/
void nextStage(ProgressEstimator* stage);
~ProgressStageEstimator();
};
} // namespace cura
#endif // PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
+1 -1
Ver Arquivo
@@ -16,7 +16,7 @@ void generateRaft(SliceDataStorage& storage, int distance)
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true);
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
}
}
-312
Ver Arquivo
@@ -1,312 +0,0 @@
#include "settingRegistry.h"
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include "utils/logoutput.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "utils/logoutput.h"
namespace cura
{
SettingRegistry SettingRegistry::instance; // define settingRegistry
std::string SettingRegistry::toString(rapidjson::Type type)
{
switch (type)
{
case rapidjson::Type::kNullType: return "null";
case rapidjson::Type::kFalseType: return "false";
case rapidjson::Type::kTrueType: return "true";
case rapidjson::Type::kObjectType: return "object";
case rapidjson::Type::kArrayType: return "array";
case rapidjson::Type::kStringType: return "string";
case rapidjson::Type::kNumberType: return "number";
default: return "Unknown";
}
}
SettingContainer::SettingContainer(std::string key, std::string label)
: key(key)
, label(label)
{
}
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
{
children.emplace_back(key, label, nullptr);
return &children.back();
}
SettingConfig::SettingConfig(std::string key, std::string label, SettingContainer* parent)
: SettingContainer(key, label)
, parent(parent)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
bool SettingRegistry::settingExists(std::string key) const
{
return settings.find(key) != settings.end();
}
SettingConfig* SettingRegistry::getSettingConfig(std::string key)
{
auto it = settings.find(key);
if (it == settings.end())
return nullptr;
return it->second;
}
SettingContainer* SettingRegistry::getCategory(std::string key)
{
for (SettingContainer& cat : categories)
if (cat.getKey().compare(key) == 0)
return &cat;
return nullptr;
}
SettingRegistry::SettingRegistry()
{
}
bool SettingRegistry::settingsLoaded()
{
return settings.size() > 0;
}
int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
cura::logError("Couldn't open JSON file.\n");
return 1;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return 2;
}
return 0;
}
int SettingRegistry::loadJSONsettings(std::string filename)
{
rapidjson::Document json_document;
int err = loadJSON(filename, json_document);
if (err) { return err; }
if (json_document.HasMember("inherits"))
{
std::string filename_copy = std::string(filename.c_str()); // copy the string because dirname(.) changes the input string!!!
char* filename_cstr = (char*)filename_copy.c_str();
int err = loadJSONsettings(std::string(dirname(filename_cstr)) + std::string("/") + json_document["inherits"].GetString());
if (err) { return err; }
return loadJSONsettingsFromDoc(json_document, false);
}
else
{
return loadJSONsettingsFromDoc(json_document, true);
}
}
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates)
{
if (!json_document.IsObject())
{
cura::logError("JSON file is not an object.\n");
return 3;
}
if (json_document.HasMember("machine_extruder_trains"))
{
categories.emplace_back("machine_extruder_trains", "Extruder Trains Settings Objects");
SettingContainer* category_trains = &categories.back();
const rapidjson::Value& trains = json_document["machine_extruder_trains"];
if (trains.IsArray())
{
if (trains.Size() > 0 && trains[0].IsObject())
{
unsigned int idx = 0;
for (auto it = trains.Begin(); it != trains.End(); ++it)
{
SettingConfig* child = category_trains->addChild(std::to_string(idx), std::to_string(idx));
for (rapidjson::Value::ConstMemberIterator setting_iterator = it->MemberBegin(); setting_iterator != it->MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(child, setting_iterator, warn_duplicates, false);
}
idx++;
}
}
}
else
{
logError("Error: JSON machine_extruder_trains is not an array!\n");
}
}
if (json_document.HasMember("machine_settings"))
{
categories.emplace_back("machine_settings", "Machine Settings");
SettingContainer* category_machine_settings = &categories.back();
const rapidjson::Value& json_object_container = json_document["machine_settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category_machine_settings, setting_iterator, warn_duplicates);
}
}
if (json_document.HasMember("categories"))
{
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
{
if (!category_iterator->value.IsObject())
{
continue;
}
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
{
continue;
}
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
{
continue;
}
categories.emplace_back(category_iterator->name.GetString(), category_iterator->value["label"].GetString());
SettingContainer* category = &categories.back();
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category, setting_iterator, warn_duplicates);
}
}
}
if (false && json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
SettingConfig* conf = getSettingConfig(override_iterator->name.GetString());
_addSettingToContainer(conf, override_iterator, false);
}
}
return 0;
}
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
const rapidjson::Value& data = json_object_it->value;
if (data.HasMember("type") && data["type"].IsString() &&
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
{
logWarning("Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
/// When this setting has children, add those children to the parent setting.
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
}
}
return;
}
std::string label;
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
{
label = "N/A";
}
else
{
label = data["label"].GetString();
}
/// Create the new setting config object.
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (data.HasMember("default"))
{
const rapidjson::Value& dflt = data["default"];
if (dflt.IsString())
{
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
/// Register the setting in the settings map lookup.
if (warn_duplicates && settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
}
if (add_to_settings)
{
settings[config->getKey()] = config;
}
/// When this setting has children, add those children to this setting.
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
}
}
}
}//namespace cura
-191
Ver Arquivo
@@ -1,191 +0,0 @@
#ifndef SETTING_REGISTRY_H
#define SETTING_REGISTRY_H
#include <vector>
#include <list>
#include <unordered_map>
#include <string>
#include "utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
// Forward declaration
class SettingConfig;
/*!
* Setting category.
* Filled from the fdmprinter.json file. Contains one or more children settings.
*/
class SettingContainer
{
friend class SettingConfig;
private:
std::string key;
std::string label;
std::list<SettingConfig> children;
public:
std::string getKey() const { return key; }
std::string getLabel() const { return label; }
SettingContainer(std::string key, std::string label);
SettingConfig* addChild(std::string key, std::string label);
/*!
* Get the \p idx th child.
*
* This is used to get a specific extruder train in Settingsbase::setExtruderTrainDefaults
*
* \param idx The index in the list of children
* \return The \p idx th child
*/
const SettingConfig* getChild(unsigned int idx) const
{
if (idx < children.size())
{
auto it = children.begin();
while (idx > 0) { ++it; idx--; }
return &*it;
}
else
return nullptr;
}
};
/*!
* Single setting data.
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
* setting registry with it's key.
*/
class SettingConfig : public SettingContainer
{
private:
std::string type;
std::string default_value;
std::string unit;
SettingContainer* parent;
public:
SettingConfig(std::string key, std::string label, SettingContainer* parent);
/*!
* Get the SettingConfig::children.
*
* This is used to get the extruder trains; see Settingsbase::setExtruderTrainDefaults
*
* \return SettingConfig::children
*/
const std::list<SettingConfig>& getChildren() const { return children; }
std::string getKey() const
{
return key;
}
void setType(std::string type)
{
this->type = type;
}
std::string getType() const
{
return type;
}
void setDefault(std::string default_value)
{
this->default_value = default_value;
}
std::string getDefaultValue() const
{
return default_value;
}
void setUnit(std::string unit)
{
this->unit = unit;
}
std::string getUnit() const
{
return unit;
}
};
/*!
* Setting registry.
* There is a single global setting registry.
* This registry contains all known setting keys.
* The registry also contains the settings categories to build up the setting hiarcy from the json file.
*/
class SettingRegistry : NoCopy
{
private:
static SettingRegistry instance;
SettingRegistry();
std::unordered_map<std::string, SettingConfig*> settings;
std::list<SettingContainer> categories;
public:
/*!
* Get the SettingRegistry.
*
* This is a singleton class.
*
* \return The SettingRegistry
*/
static SettingRegistry* getInstance() { return &instance; }
bool settingExists(std::string key) const;
SettingConfig* getSettingConfig(std::string key);
/*!
* Return the first category with the given key as name, or a null pointer.
*
* \param key the key as it is in the JSON file
* \return The first category in the list having the \p key
*/
SettingContainer* getCategory(std::string key);
bool settingsLoaded();
/*!
* Load settings from a json file and all the parents it inherits from.
*
* Uses recursion to load the parent json file.
*
* \param filename The filename of the json file to parse
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename);
private:
std::string toString(rapidjson::Type type);
/*!
* Load a json document.
*
* \param filename The filename of the json file to parse
* \param json_document (output) the document to be loaded
* \return an error code or zero of succeeded
*/
int loadJSON(std::string filename, rapidjson::Document& json_document);
/*!
* Load settings from a single json file.
*
* \param filename The filename of the json file to parse
* \param warn_duplicates whether to warn for duplicate definitions
* \return an error code or zero of succeeded
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates);
/*!
* \param warn_duplicates whether to warn for duplicate definitions
*/
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
};
}//namespace cura
#endif//SETTING_REGISTRY_H
-262
Ver Arquivo
@@ -1,262 +0,0 @@
#include <cctype>
#include <fstream>
#include <stdio.h>
#include <sstream> // ostringstream
#include "utils/logoutput.h"
#include "settings.h"
#include "settingRegistry.h"
namespace cura
{
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
}
SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
: parent(parent)
{
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
{
}
SettingsBase::SettingsBase(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
SettingsMessenger::SettingsMessenger(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
void SettingsBase::setSetting(std::string key, std::string value)
{
if (SettingRegistry::getInstance()->settingExists(key))
{
setting_values[key] = value;
}
else
{
cura::logError("Warning: setting an unregistered setting %s\n", key.c_str() );
setting_values[key] = value; // Handy when programmers are in the process of introducing a new setting
}
}
std::string SettingsBase::getSettingString(std::string key)
{
if (setting_values.find(key) != setting_values.end())
{
return setting_values[key];
}
if (parent)
{
return parent->getSettingString(key);
}
if (SettingRegistry::getInstance()->settingExists(key))
{
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
}
else
{
setting_values[key] = "";
cura::logError("Unregistered setting %s\n", key.c_str());
}
return setting_values[key];
}
void SettingsMessenger::setSetting(std::string key, std::string value)
{
parent->setSetting(key, value);
}
std::string SettingsMessenger::getSettingString(std::string key)
{
return parent->getSettingString(key);
}
void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
{
const SettingContainer* machine_extruder_trains = SettingRegistry::getInstance()->getCategory(std::string("machine_extruder_trains"));
if (!machine_extruder_trains)
{
logWarning("Error: no machine_extruder_trains category found in JSON!\n");
return;
}
const SettingConfig* train = machine_extruder_trains->getChild(extruder_nr);
if (!train)
{
logError("Not enough extruder trains specified in JSON: %i\n", extruder_nr);
return;
}
for (const SettingConfig& setting : train->getChildren())
{
if (setting_values.find(setting.getKey()) == setting_values.end())
{
setSetting(setting.getKey(), setting.getDefaultValue());
}
}
}
int SettingsBaseVirtual::getSettingAsIndex(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingAsCount(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) * 1000.0;
}
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 180.0 * M_PI;
}
bool SettingsBaseVirtual::getSettingBoolean(std::string key)
{
std::string value = getSettingString(key);
if (value == "on")
return true;
if (value == "yes")
return true;
if (value == "true" or value == "True") //Python uses "True"
return true;
int num = atoi(value.c_str());
return num != 0;
}
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key)
{
std::string value = getSettingString(key);
return std::max(1.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInPercentage(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInSeconds(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
{
std::string value = getSettingString(key);
if (value == "RepRap")
return EGCodeFlavor::REPRAP;
else if (value == "UltiGCode")
return EGCodeFlavor::ULTIGCODE;
else if (value == "Makerbot")
return EGCodeFlavor::MAKERBOT;
else if (value == "BFB")
return EGCodeFlavor::BFB;
else if (value == "MACH3")
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return EGCodeFlavor::REPRAP_VOLUMATRIC;
return EGCodeFlavor::REPRAP;
}
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
{
std::string value = getSettingString(key);
if (value == "lines")
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
}
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key)
{
std::string value = getSettingString(key);
if (value == "brim")
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
return EPlatformAdhesion::SKIRT;
}
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
{
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "touching_buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
{
std::string value = getSettingString(key);
if (value == "random")
return EZSeamType::RANDOM;
if (value == "shortest")
return EZSeamType::SHORTEST;
if (value == "back")
return EZSeamType::BACK;
return EZSeamType::SHORTEST;
}
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "normal")
return ESurfaceMode::NORMAL;
if (value == "surface")
return ESurfaceMode::SURFACE;
if (value == "both")
return ESurfaceMode::BOTH;
return ESurfaceMode::NORMAL;
}
}//namespace cura
+14
Ver Arquivo
@@ -0,0 +1,14 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingConfig.h"
namespace cura
{
SettingConfig::SettingConfig(std::string key, std::string label)
: SettingContainer(key, label)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
}//namespace cura
+76
Ver Arquivo
@@ -0,0 +1,76 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_CONFIG_H
#define SETTINGS_SETTING_CONFIG_H
#include <string>
#include <iostream> // debug out
#include "SettingContainer.h"
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
/*!
* Single setting data.
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
* setting registry with it's key.
*/
class SettingConfig : public SettingContainer
{
private:
std::string type; //!< The type of the default_value, e.g. str, int, bool
std::string default_value; //!< The default value for this setting
std::string unit; //!< The unit of the physical quantity in which this setting is measured, e.g. "mm", "mm/s", ""
public:
SettingConfig(std::string key, std::string label);
std::string getKey() const
{
return key;
}
void setType(std::string type)
{
this->type = type;
}
std::string getType() const
{
return type;
}
void setDefault(std::string default_value)
{
this->default_value = default_value;
}
std::string getDefaultValue() const
{
return default_value;
}
void setUnit(std::string unit)
{
this->unit = unit;
}
std::string getUnit() const
{
return unit;
}
void debugOutputAllSettings() const
{
std::cerr << key << "(" << default_value << ")" << std::endl;
for (const SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
};
}//namespace cura
#endif//SETTINGS_SETTING_CONFIG_H
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingContainer.h"
#include "SettingConfig.h"
#include <string>
#include <algorithm> // find_if
namespace cura
{
SettingContainer::SettingContainer(std::string key, std::string label)
: key(key)
, label(label)
{
}
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
{
children.emplace_back(key, label);
return &children.back();
}
SettingConfig& SettingContainer::getOrCreateChild(std::string key, std::string label)
{
auto child_it = std::find_if(children.begin(), children.end(), [&key](SettingConfig& child) { return child.key == key; } );
if (child_it == children.end())
{
children.emplace_back(key, label);
return children.back();
}
else
{
return *child_it;
}
}
void SettingContainer::debugOutputAllSettings() const
{
std::cerr << "\nSETTINGS BASE: " << key << std::endl;
for (const SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
}//namespace cura
+83
Ver Arquivo
@@ -0,0 +1,83 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_CONTAINER_H
#define SETTINGS_SETTING_CONTAINER_H
#include <vector>
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
// Forward declaration
class SettingConfig;
class SettingRegistry;
/*!
* Setting container for a settings base of definitions and default values.
* Filled from the .def.json files. Contains one or more children settings.
*/
class SettingContainer
{
friend class SettingConfig;
friend class SettingRegistry;
private:
std::string key;
std::string label;
std::list<SettingConfig> children; // must be a list cause the pointers to individual children are mapped to in SettingRegistry::settings.
std::list<std::string> path; //!< The path of parents (internal names) to this container
public:
std::string getKey() const { return key; }
std::string getLabel() const { return label; }
SettingContainer(std::string key, std::string label);
/*!
* Get the SettingConfig::children.
*
* This is used to get the extruder trains; see Settingsbase::setExtruderTrainDefaults
*
* \return SettingConfig::children
*/
const std::list<SettingConfig>& getChildren() const { return children; }
SettingConfig* addChild(std::string key, std::string label);
/*!
* Get the \p idx th child.
*
* This is used to get a specific extruder train in Settingsbase::setExtruderTrainDefaults
*
* \param idx The index in the list of children
* \return The \p idx th child
*/
const SettingConfig* getChild(unsigned int idx) const
{
if (idx < children.size())
{
auto it = children.begin();
while (idx > 0) { ++it; idx--; }
return &*it;
}
else
return nullptr;
}
private:
/*!
* Get the (direct) child with key \p key, or create one with key \p key and label \p label as well.
*
* \param key the key
* \param label the label for creating a new child
* \return The existing or newly created child setting.
*/
SettingConfig& getOrCreateChild(std::string key, std::string label);
public:
void debugOutputAllSettings() const;
};
}//namespace cura
#endif//SETTINGS_SETTING_CONTAINER_H
+392
Ver Arquivo
@@ -0,0 +1,392 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingRegistry.h"
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <cstring> // strtok (split string using delimiters) strcpy
#include <fstream> // ifstream (to see if file exists)
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"
namespace cura
{
SettingRegistry SettingRegistry::instance; // define settingRegistry
std::string SettingRegistry::toString(rapidjson::Type type)
{
switch (type)
{
case rapidjson::Type::kNullType: return "null";
case rapidjson::Type::kFalseType: return "false";
case rapidjson::Type::kTrueType: return "true";
case rapidjson::Type::kObjectType: return "object";
case rapidjson::Type::kArrayType: return "array";
case rapidjson::Type::kStringType: return "string";
case rapidjson::Type::kNumberType: return "number";
default: return "Unknown";
}
}
SettingConfig::SettingConfig(std::string key, std::string label)
: SettingContainer(key, label)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
bool SettingRegistry::settingExists(std::string key) const
{
return setting_key_to_config.find(key) != setting_key_to_config.end();
}
SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
{
auto it = setting_key_to_config.find(key);
if (it == setting_key_to_config.end())
return nullptr;
return it->second;
}
SettingRegistry::SettingRegistry()
: setting_definitions("settings", "Settings")
{
// load search paths from environment variable CURA_ENGINE_SEARCH_PATH
char* paths = getenv("CURA_ENGINE_SEARCH_PATH");
if (paths)
{
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
char delims[] = ":"; // colon
#else
char delims[] = ";"; // semicolon
#endif
char* path = strtok(paths, delims); // search for next path delimited by any of the characters in delims
while (path != NULL)
{
search_paths.emplace(path);
path = strtok(NULL, ";:,"); // continue searching in last call to strtok
}
}
}
int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
cura::logError("Couldn't open JSON file.\n");
return 1;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return 2;
}
return 0;
}
/*!
* Check whether a file exists.
* from https://techoverflow.net/blog/2013/01/11/cpp-check-if-file-exists/
*
* \param filename The path to a filename to check if it exists
* \return Whether the file exists.
*/
bool fexists(const char *filename)
{
std::ifstream ifile(filename);
return (bool)ifile;
}
bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::string& result)
{
for (const std::string& search_path : search_paths)
{
result = search_path + std::string("/") + machine_id + std::string(".def.json");
if (fexists(result.c_str()))
{
return true;
}
}
return false;
}
int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
{
if (extruder_nr >= extruder_train_ids.size())
{
return -1;
}
std::string definition_file;
bool found = getDefinitionFile(extruder_train_ids[extruder_nr], definition_file);
if (!found)
{
return -1;
}
bool warn_base_file_duplicates = false;
return loadJSONsettings(definition_file, settings_base, warn_base_file_duplicates);
}
int SettingRegistry::loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates)
{
rapidjson::Document json_document;
log("Loading %s...\n", filename.c_str());
int err = loadJSON(filename, json_document);
if (err) { return err; }
{ // add parent folder to search paths
char filename_cstr[500];
std::strcpy(filename_cstr, filename.c_str()); // copy the string because dirname(.) changes the input string!!!
std::string folder_name = std::string(dirname(filename_cstr));
search_paths.emplace(folder_name);
}
if (json_document.HasMember("inherits") && json_document["inherits"].IsString())
{
std::string child_filename;
bool found = getDefinitionFile(json_document["inherits"].GetString(), child_filename);
if (!found)
{
return -1;
}
err = loadJSONsettings(child_filename, settings_base, warn_base_file_duplicates); // load child first
if (err)
{
return err;
}
err = loadJSONsettingsFromDoc(json_document, settings_base, false);
}
else
{
err = loadJSONsettingsFromDoc(json_document, settings_base, warn_base_file_duplicates);
}
if (json_document.HasMember("metadata") && json_document["metadata"].IsObject())
{
const rapidjson::Value& json_metadata = json_document["metadata"];
if (json_metadata.HasMember("machine_extruder_trains") && json_metadata["machine_extruder_trains"].IsObject())
{
const rapidjson::Value& json_machine_extruder_trains = json_metadata["machine_extruder_trains"];
for (rapidjson::Value::ConstMemberIterator extr_train_iterator = json_machine_extruder_trains.MemberBegin(); extr_train_iterator != json_machine_extruder_trains.MemberEnd(); ++extr_train_iterator)
{
int extruder_train_nr = atoi(extr_train_iterator->name.GetString());
if (extruder_train_nr < 0)
{
continue;
}
const rapidjson::Value& json_id = extr_train_iterator->value;
if (!json_id.IsString())
{
continue;
}
const char* id = json_id.GetString();
if (extruder_train_nr >= (int) extruder_train_ids.size())
{
extruder_train_ids.resize(extruder_train_nr + 1);
}
extruder_train_ids[extruder_train_nr] = std::string(id);
}
}
}
return err;
}
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates)
{
if (!json_document.IsObject())
{
cura::logError("JSON file is not an object.\n");
return 3;
}
{ // handle machine name
std::string machine_name = "Unknown";
if (json_document.HasMember("name"))
{
const rapidjson::Value& machine_name_field = json_document["name"];
if (machine_name_field.IsString())
{
machine_name = machine_name_field.GetString();
}
}
SettingConfig& machine_name_setting = addSetting("machine_name", "Machine Name");
machine_name_setting.setDefault(machine_name);
machine_name_setting.setType("string");
settings_base->_setSetting(machine_name_setting.getKey(), machine_name_setting.getDefaultValue());
}
if (json_document.HasMember("settings"))
{
std::list<std::string> path;
handleChildren(json_document["settings"], path, settings_base, warn_duplicates);
}
if (json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
if (!conf) //Setting could not be found.
{
logWarning("Trying to override unknown setting %s.\n", setting.c_str());
continue;
}
_loadSettingValues(conf, override_iterator, settings_base);
}
}
return 0;
}
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
{
if (!settings_list.IsObject())
{
logError("ERROR: json settings list is not an object!\n");
return;
}
for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
{
handleSetting(setting_iterator, path, settings_base, warn_duplicates);
if (setting_iterator->value.HasMember("children"))
{
std::list<std::string> path_here = path;
path_here.push_back(setting_iterator->name.GetString());
handleChildren(setting_iterator->value["children"], path_here, settings_base, warn_duplicates);
}
}
}
bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
{
if (setting.HasMember("children"))
{
return false;
}
else
{
return true;
}
}
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
{
const rapidjson::Value& json_setting = json_setting_it->value;
if (!json_setting.IsObject())
{
logError("ERROR: json setting is not an object!\n");
return;
}
std::string name = json_setting_it->name.GetString();
if (json_setting.HasMember("type") && json_setting["type"].IsString() && json_setting["type"].GetString() == std::string("category"))
{ // skip category objects
return;
}
if (settingIsUsedByEngine(json_setting))
{
if (!json_setting.HasMember("label") || !json_setting["label"].IsString())
{
logError("ERROR: json setting \"%s\" has no label!\n", name.c_str());
return;
}
std::string label = json_setting["label"].GetString();
SettingConfig* setting = getSettingConfig(name);
if (warn_duplicates && setting)
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", name.c_str(), label.c_str(), getSettingConfig(name)->getLabel().c_str());
}
if (!setting)
{
setting = &addSetting(name, label);
}
_loadSettingValues(setting, json_setting_it, settings_base);
}
}
SettingConfig& SettingRegistry::addSetting(std::string name, std::string label)
{
SettingConfig* config = setting_definitions.addChild(name, label);
setting_key_to_config[name] = config;
return *config;
}
void SettingRegistry::loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config)
{
const rapidjson::Value& setting_content = json_object_it->value;
if (setting_content.HasMember("default_value"))
{
const rapidjson::Value& dflt = setting_content["default_value"];
if (dflt.IsString())
{
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logWarning("WARNING: Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
}
void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingsBase* settings_base)
{
const rapidjson::Value& data = json_object_it->value;
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (config->getType() == std::string("polygon") || config->getType() == std::string("polygons"))
{ // skip polygon settings : not implemented yet and not used yet (TODO)
// logWarning("WARNING: Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
return;
}
loadDefault(json_object_it, config);
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
settings_base->_setSetting(config->getKey(), config->getDefaultValue());
}
}//namespace cura
+191
Ver Arquivo
@@ -0,0 +1,191 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_REGISTRY_H
#define SETTINGS_SETTING_REGISTRY_H
#include <vector>
#include <unordered_set>
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "SettingConfig.h"
#include "SettingContainer.h"
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
#include "settings.h"
namespace cura
{
/*!
* Setting registry.
* There is a single global setting registry.
* This registry contains all known setting keys and (some of) their attributes.
* The default values are stored and retrieved in case a given setting doesn't get a value from the command line or the frontend.
*/
class SettingRegistry : NoCopy
{
private:
static SettingRegistry instance;
SettingRegistry();
std::unordered_map<std::string, SettingConfig*> setting_key_to_config; //!< Mapping from setting keys to their configurations
SettingContainer setting_definitions; //!< All setting configurations (A flat list)
std::vector<std::string> extruder_train_ids; //!< The internal id's of each extruder (the filename without the extension)
std::unordered_set<std::string> search_paths; //!< The paths to search for json files.
public:
/*!
* Get the SettingRegistry.
*
* This is a singleton class.
*
* \return The SettingRegistry
*/
static SettingRegistry* getInstance() { return &instance; }
/*!
* Check whether a setting exists, according to the settings json files.
*
* \param key The internal key for the setting to test
* \return Whether a definition of the setting is recorded in this registry.
*/
bool settingExists(std::string key) const;
/*!
* Get the config of a setting with a given key.
*
* \param key the (internal) key for a setting
* \return the setting definition values
*/
SettingConfig* getSettingConfig(std::string key) const;
protected:
/*!
* Whether this json settings object is a definition of a CuraEngine setting,
* or only a shorthand setting to control other settings.
* Only settings used by the engine will be recordedd in the registry.
*
* \param setting The setting to check whether CuraEngine uses it.
* \return Whether CuraEngine uses the setting.
*/
bool settingIsUsedByEngine(const rapidjson::Value& setting);
/*!
* Get the filename for the machine definition with the given id.
* Check the directories in SettingRegistry::search_paths.
*
* \param machine_id The id and base filename (without extensions) of the machine definition to search for.
* \param result The filename of the machine definition
* \return Whether we found the file.
*/
bool getDefinitionFile(const std::string machine_id, std::string& result);
/*!
* Get the default value of a json setting object in the format used internally (c style).
*
* \param[in] json_object_it An iterator for a given setting json object
* \param[out] config Where the default value is stored
*/
static void loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config);
public:
/*!
* Load settings from a json file and all the parents it inherits from.
*
* Uses recursion to load the parent json file.
*
* \param filename The filename of the json file to parse
* \param settings_base The settings base where to store the default values.
* \param warn_base_file_duplicates Whether to warn if there are duplicate definitions in the base file (the .def.json which has no inherits).
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates = true);
void debugOutputAllSettings() const
{
setting_definitions.debugOutputAllSettings();
}
/*!
* Load settings from the extruder definition json file and all the parents it inherits from.
* Use the json file refered to in the machine_extruder_trains attribute of the last loaded machine json file.
*
* Uses recursion to load the parent json file.
*
* \param extruder_nr The number of the extruder to load
* \param settings_base The settings base where to store the default values. (The extruder settings base)
* \return an error code or zero of succeeded
*/
int loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base);
private:
/*!
* \param type type to convert to string
* \return human readable version of json type
*/
static std::string toString(rapidjson::Type type);
public:
/*!
* Load a json document.
*
* \param filename The filename of the json file to parse
* \param json_document (output) the document to be loaded
* \return an error code or zero of succeeded
*/
static int loadJSON(std::string filename, rapidjson::Document& json_document);
private:
/*!
* Load settings from a single json file.
*
* \param filename The filename of the json file to parse
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate definitions
* \return an error code or zero of succeeded
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Create a new SettingConfig and add it to the registry.
*
* \param name The internal key of the setting
* \param label The human readable name for the frontend
* \return The config created
*/
SettingConfig& addSetting(std::string name, std::string label);
/*!
* Load inessential data about the setting, like its type and unit.
*
* \param[out] config Where to store the data
* \param[in] json_object_it Iterator to a setting json object
* \param[out] settings_base The settings base where to store the default values.
*/
void _loadSettingValues(SettingConfig* config, const rapidjson::Value::ConstMemberIterator& json_object_it, SettingsBase* settings_base);
/*!
* Handle a json object which contains a list of settings.
*
* \param settings_list The object containing one or more setting definitions
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Handle a json object for a setting.
*
* \param json_setting_it Iterator for the setting which contains the key (setting name) and attributes info
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
};
}//namespace cura
#endif//SETTINGS_SETTING_REGISTRY_H
+238
Ver Arquivo
@@ -0,0 +1,238 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_TO_GV_H
#define SETTINGS_TO_GV_H
#include <stdio.h> // for file output
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <algorithm> // find_if
#include <regex> // regex_search
#include <cassert>
#include <fstream>
#include <set>
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"
#include "SettingRegistry.h"
namespace cura
{
class SettingsToGv
{
enum class RelationType
{
PARENT_CHILD,
INHERIT_FUNCTION,
ERROR_FUNCTION,
WARNING_FUNCTION
};
FILE* out;
std::set<std::string> engine_settings;
bool parent_child_viz, inherit_viz, error_viz, warning_viz;
public:
SettingsToGv(std::string output_filename, std::string engine_settings_filename, bool parent_child_viz, bool inherit_viz, bool error_viz, bool warning_viz)
: parent_child_viz(parent_child_viz)
, inherit_viz(inherit_viz)
, error_viz(error_viz)
, warning_viz(warning_viz)
{
out = fopen(output_filename.c_str(), "w");
fprintf(out, "digraph G {\n");
std::ifstream engine_settings_file(engine_settings_filename.c_str());
std::string line;
while (std::getline(engine_settings_file, line))
{
engine_settings.insert(line);
//fprintf(out, "%s [color=green];\n", line.c_str());
}
engine_settings_file.close();
}
private:
void generateEdge(const std::string& parent, const std::string& child, RelationType relation_type)
{
if (engine_settings.find(parent) != engine_settings.end())
{
fprintf(out, "%s [color=green];\n", parent.c_str());
}
if (engine_settings.find(child) != engine_settings.end())
{
fprintf(out, "%s [color=green];\n", child.c_str());
}
std::string color;
switch (relation_type)
{
case SettingsToGv::RelationType::INHERIT_FUNCTION:
if (!inherit_viz)
{
return;
}
color = "blue";
break;
case SettingsToGv::RelationType::PARENT_CHILD:
if (!parent_child_viz)
{
return;
}
color = "black";
break;
case SettingsToGv::RelationType::ERROR_FUNCTION:
if (!error_viz)
{
return;
}
color = "red";
break;
case SettingsToGv::RelationType::WARNING_FUNCTION:
if (!warning_viz)
{
return;
}
color = "orange";
break;
}
fprintf(out, "edge [color=%s];\n", color.c_str());
fprintf(out, "%s -> %s;\n", parent.c_str(), child.c_str());
}
bool createFunctionEdges(const rapidjson::Value& data, std::string function_key, const std::string& parent, const std::string& name, const RelationType relation_type)
{
bool generated_edge = false;
if (data.HasMember(function_key.c_str()) && data[function_key.c_str()].IsString())
{
std::string function = data[function_key.c_str()].GetString();
std::regex setting_name_regex("[a-zA-Z0-9_]+"); // matches mostly with setting names
std::smatch regex_match;
while (std::regex_search (function, regex_match, setting_name_regex))
{
std::string inherited_setting_string = regex_match[0];
if (inherited_setting_string == "parent_value")
{
generateEdge(parent, name, RelationType::PARENT_CHILD);
generated_edge = true;
}
else if ( ! std::regex_match(inherited_setting_string, std::regex("[0-9]+")) && // exclude numbers
// result != "parent_value" &&
inherited_setting_string != "if" && inherited_setting_string != "else" && inherited_setting_string != "and"
&& inherited_setting_string != "or" && inherited_setting_string != "math" && inherited_setting_string != "ceil"
&& inherited_setting_string != "int" && inherited_setting_string != "round" && inherited_setting_string != "max" // exclude operators and functions
&& inherited_setting_string != "grid" && inherited_setting_string != "triangles" // exclude enum values
&& function.c_str()[regex_match.position() + regex_match.length()] != '\'') // exclude enum terms
{
if (inherited_setting_string == parent)
{
generated_edge = true;
generateEdge(inherited_setting_string, name, RelationType::PARENT_CHILD);
}
else
{
generateEdge(inherited_setting_string, name, relation_type);
}
}
function = regex_match.suffix().str();
}
}
return generated_edge;
}
void parseSetting(const std::string& parent, rapidjson::Value::ConstMemberIterator json_object_it)
{
std::string name = json_object_it->name.GetString();
// std::cerr << "parsed: " << name <<"\n";
bool generated_edge = false;
const rapidjson::Value& data = json_object_it->value;
if (data.HasMember("type") && data["type"].IsString() && data["type"].GetString() != std::string("category"))
{
bool generated_edge_inherit = createFunctionEdges(data, "inherit_function", parent, name, RelationType::INHERIT_FUNCTION);
bool generated_edge_max = createFunctionEdges(data, "max_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_min = createFunctionEdges(data, "min_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_max_warn = createFunctionEdges(data, "max_value_warning", parent, name, RelationType::WARNING_FUNCTION);
bool generated_edge_min_warn = createFunctionEdges(data, "min_value_warning", parent, name, RelationType::WARNING_FUNCTION);
if (generated_edge_inherit || generated_edge_max_warn || generated_edge_min_warn || generated_edge_max || generated_edge_min)
{
generated_edge = true;
}
if (!generated_edge && parent != "")
{
generateEdge(parent, name, RelationType::PARENT_CHILD);
}
}
else
{
name = "";
}
// recursive part
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
parseSetting(name, setting_iterator);
}
}
}
void parseJson(const rapidjson::Document& json_document)
{
if (json_document.HasMember("settings"))
{
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_document["settings"].MemberBegin(); setting_iterator != json_document["settings"].MemberEnd(); ++setting_iterator)
{
parseSetting("", setting_iterator);
}
}
}
int generateRecursive(std::string filename)
{
rapidjson::Document json_document;
int err = SettingRegistry::loadJSON(filename, json_document);
if (err) { return err; }
if (json_document.HasMember("inherits"))
{
std::string filename_copy = std::string(filename.c_str()); // copy the string because dirname(.) changes the input string!!!
char* filename_cstr = (char*)filename_copy.c_str();
int err = generate(std::string(dirname(filename_cstr)) + std::string("/") + json_document["inherits"].GetString());
if (err)
{
return err;
}
}
parseJson(json_document);
return 0;
}
public:
int generate(std::string json_filename)
{
int err = generateRecursive(json_filename);
fprintf(out, "}\n");
fclose(out);
return err;
}
};
} // namespace cura
#endif // SETTINGS_TO_GV_H
+368
Ver Arquivo
@@ -0,0 +1,368 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include <cctype>
#include <fstream>
#include <stdio.h>
#include <sstream> // ostringstream
#include "../utils/logoutput.h"
#include "settings.h"
#include "SettingRegistry.h"
namespace cura
{
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
std::string toString(EGCodeFlavor flavor)
{
switch (flavor)
{
case EGCodeFlavor::BFB:
return "BFB";
case EGCodeFlavor::MACH3:
return "Mach3";
case EGCodeFlavor::MAKERBOT:
return "Makerbot";
case EGCodeFlavor::ULTIGCODE:
return "UltiGCode";
case EGCodeFlavor::REPRAP_VOLUMATRIC:
return "RepRap(Volumetric)";
case EGCodeFlavor::GRIFFIN:
return "Griffin";
case EGCodeFlavor::REPRAP:
default:
return "RepRap";
}
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
}
SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
: parent(parent)
{
}
SettingsBase::SettingsBase(std::string level_id)
: SettingsBaseVirtual(NULL)
, level_id(level_id)
{
}
SettingsBase::SettingsBase(SettingsBaseVirtual* parent, std::string level_id)
: SettingsBaseVirtual(parent)
, level_id(level_id)
{
}
SettingsMessenger::SettingsMessenger(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
void SettingsBase::_setSetting(std::string key, std::string value)
{
setting_values[key] = value;
}
void SettingsBase::setSetting(std::string key, std::string value)
{
if (SettingRegistry::getInstance()->settingExists(key))
{
_setSetting(key, value);
}
else
{
cura::logError("Warning: setting an unregistered setting %s\n", key.c_str() );
_setSetting(key, value); // Handy when programmers are in the process of introducing a new setting
}
}
std::string SettingsBase::getSettingString(std::string key) const
{
// logError(">>>%s, %s\n", key.c_str(), level_id.c_str());
std::cout << key << "\t\t : " << level_id << "\n";
// if (level_id == std::string("global"))
// {
// logError("WARNING: %s retrieved globally!\n", key.c_str());
// }
return _getSettingString(key);
}
std::string SettingsBase::_getSettingString(std::string key) const
{
if (setting_values.find(key) != setting_values.end())
{
return setting_values.at(key);
}
if (parent)
{
return parent->_getSettingString(key);
}
const_cast<SettingsBase&>(*this).setting_values[key] = "";
cura::logError("Unregistered setting %s\n", key.c_str());
return "";
}
void SettingsMessenger::setSetting(std::string key, std::string value)
{
parent->setSetting(key, value);
}
std::string SettingsMessenger::_getSettingString(std::string key) const
{
return parent->_getSettingString(key);
}
std::string SettingsMessenger::getSettingString(std::string key) const
{
return parent->getSettingString(key);
}
int SettingsBaseVirtual::getSettingAsIndex(std::string key) const
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingAsCount(std::string key) const
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
double SettingsBaseVirtual::getSettingInMillimeters(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
{
return getSettingInMillimeters(key) * 1000.0;
}
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 180.0 * M_PI;
}
bool SettingsBaseVirtual::getSettingBoolean(std::string key) const
{
std::string value = getSettingString(key);
if (value == "on")
return true;
if (value == "yes")
return true;
if (value == "true" or value == "True") //Python uses "True"
return true;
int num = atoi(value.c_str());
return num != 0;
}
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key) const
{
std::string value = getSettingString(key);
return std::max(1.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInPercentage(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) const
{
FlowTempGraph ret;
const char* c_str = getSettingString(key).c_str();
char const* char_p = c_str;
while (*char_p != '[')
{
if (*char_p == '\0') //We've reached the end of string without encountering the first opening bracket.
{
return ret; //Empty at this point.
}
char_p++;
}
char_p++; // skip the '['
for (; *char_p != '\0'; char_p++)
{
while (*char_p != '[')
{
if (*char_p == '\0') //We've reached the end of string without finding the next opening bracket.
{
return ret; //Don't continue parsing this item then. Just stop and return.
}
char_p++;
}
char_p++; // skip the '['
char* end;
double first = strtod(char_p, &end); //If not a valid number, this becomes zero.
char_p = end;
while (*char_p != ',')
{
if (*char_p == '\0') //We've reached the end of string without finding the comma.
{
return ret; //This entry is incomplete.
}
char_p++;
}
char_p++; // skip the ','
double second = strtod(char_p, &end); //If not a valid number, this becomes zero.
ret.data.emplace_back(first, second);
char_p = end;
while (*char_p != ']')
{
if (*char_p == '\0') //We've reached the end of string without finding the closing bracket.
{
return ret; //This entry is probably complete and has been added, but stop searching.
}
char_p++;
}
char_p++; // skip the ']'
if (*char_p == ']' || *char_p == '\0')
{
break;
}
}
return ret;
}
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
{
std::string value = getSettingString(key);
if (value == "Griffin")
return EGCodeFlavor::GRIFFIN;
else if (value == "UltiGCode")
return EGCodeFlavor::ULTIGCODE;
else if (value == "Makerbot")
return EGCodeFlavor::MAKERBOT;
else if (value == "BFB")
return EGCodeFlavor::BFB;
else if (value == "MACH3")
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return EGCodeFlavor::REPRAP_VOLUMATRIC;
return EGCodeFlavor::REPRAP;
}
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
{
std::string value = getSettingString(key);
if (value == "lines")
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
}
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key) const
{
std::string value = getSettingString(key);
if (value == "brim")
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
return EPlatformAdhesion::SKIRT;
}
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key) const
{
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key) const
{
std::string value = getSettingString(key);
if (value == "random")
return EZSeamType::RANDOM;
if (value == "shortest")
return EZSeamType::SHORTEST;
if (value == "back")
return EZSeamType::BACK;
return EZSeamType::SHORTEST;
}
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
{
std::string value = getSettingString(key);
if (value == "normal")
return ESurfaceMode::NORMAL;
if (value == "surface")
return ESurfaceMode::SURFACE;
if (value == "both")
return ESurfaceMode::BOTH;
return ESurfaceMode::NORMAL;
}
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "off")
{
return CombingMode::OFF;
}
if (value == "all")
{
return CombingMode::ALL;
}
if (value == "noskin")
{
return CombingMode::NO_SKIN;
}
return CombingMode::ALL;
}
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key)
{
std::string value = getSettingString(key);
if (value == "xy_overrides_z")
{
return SupportDistPriority::XY_OVERRIDES_Z;
}
if (value == "z_overrides_xy")
{
return SupportDistPriority::Z_OVERRIDES_XY;
}
return SupportDistPriority::XY_OVERRIDES_Z;
}
}//namespace cura
+81 -36
Ver Arquivo
@@ -1,12 +1,15 @@
#ifndef SETTINGS_H
#define SETTINGS_H
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTINGS_H
#define SETTINGS_SETTINGS_H
#include <vector>
#include <map>
#include <unordered_map>
#include <sstream>
#include "utils/floatpoint.h"
#include "../utils/floatpoint.h"
#include "../FlowTempGraph.h"
namespace cura
{
@@ -73,8 +76,23 @@ enum class EGCodeFlavor
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
REPRAP_VOLUMATRIC = 5,
/**
* Griffin flavored is Marlin based GCode.
* This is a type of RepRap used for machines with multiple extruder trains.
* G0 for moves, G1 for extrusion.
* E values give mm of filament extrusion.
* E values are stored separately per extruder train.
* Retraction is done on E values with G1. Start/end code is added.
* M227 is used to initialize a single extrusion train.
**/
GRIFFIN = 6,
};
/*!
* Converts a gcode flavor type to string so that it can be included in the gcode.
*/
std::string toString(EGCodeFlavor flavor);
/*!
* In Cura different infill methods are available.
* This enum defines which fill patterns are available to get a uniform naming troughout the engine.
@@ -124,6 +142,19 @@ enum class ESurfaceMode
BOTH
};
enum class CombingMode
{
OFF,
ALL,
NO_SKIN
};
enum class SupportDistPriority
{
XY_OVERRIDES_Z,
Z_OVERRIDES_XY
};
#define MAX_EXTRUDERS 16
//Maximum number of infill layers that can be combined into a single infill extrusion area.
@@ -141,7 +172,8 @@ class SettingsBaseVirtual
protected:
SettingsBaseVirtual* parent;
public:
virtual std::string getSettingString(std::string key) = 0;
virtual std::string getSettingString(std::string key) const = 0;
virtual std::string _getSettingString(std::string key) const = 0;
virtual void setSetting(std::string key, std::string value) = 0;
@@ -153,26 +185,32 @@ public:
void setParent(SettingsBaseVirtual* parent) { this->parent = parent; }
SettingsBaseVirtual* getParent() { return parent; }
int getSettingAsIndex(std::string key);
int getSettingAsCount(std::string key);
int getSettingAsIndex(std::string key) const;
int getSettingAsCount(std::string key) const;
double getSettingInAngleRadians(std::string key);
int getSettingInMicrons(std::string key);
bool getSettingBoolean(std::string key);
double getSettingInDegreeCelsius(std::string key);
double getSettingInMillimetersPerSecond(std::string key);
double getSettingInCubicMillimeters(std::string key);
double getSettingInPercentage(std::string key);
double getSettingInSeconds(std::string key);
double getSettingInAngleRadians(std::string key) const;
double getSettingInMillimeters(std::string key) const;
int getSettingInMicrons(std::string key) const;
bool getSettingBoolean(std::string key) const;
double getSettingInDegreeCelsius(std::string key) const;
double getSettingInMillimetersPerSecond(std::string key) const;
double getSettingInCubicMillimeters(std::string key) const;
double getSettingInPercentage(std::string key) const;
double getSettingInSeconds(std::string key) const;
EGCodeFlavor getSettingAsGCodeFlavor(std::string key);
EFillMethod getSettingAsFillMethod(std::string key);
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key);
ESupportType getSettingAsSupportType(std::string key);
EZSeamType getSettingAsZSeamType(std::string key);
ESurfaceMode getSettingAsSurfaceMode(std::string key);
FlowTempGraph getSettingAsFlowTempGraph(std::string key) const;
EGCodeFlavor getSettingAsGCodeFlavor(std::string key) const;
EFillMethod getSettingAsFillMethod(std::string key) const;
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key) const;
ESupportType getSettingAsSupportType(std::string key) const;
EZSeamType getSettingAsZSeamType(std::string key) const;
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
CombingMode getSettingAsCombingMode(std::string key);
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
};
class SettingRegistry;
/*!
* Base class for every object that can hold settings.
* The SettingBase object can hold multiple key-value pairs that define settings.
@@ -182,26 +220,24 @@ public:
*/
class SettingsBase : public SettingsBaseVirtual
{
friend class SettingRegistry;
private:
std::unordered_map<std::string, std::string> setting_values;
std::string level_id; //!< The name of the level at which this is retrieved "global", "extruder-1", etc.
public:
SettingsBase(); //!< SettingsBase without a parent settings object
SettingsBase(SettingsBaseVirtual* parent); //!< construct a SettingsBase with a parent settings object
SettingsBase(std::string level_id); //!< SettingsBase without a parent settings object
SettingsBase(SettingsBaseVirtual* parent, std::string level_id); //!< construct a SettingsBase with a parent settings object
/*!
* Retrieve the defaults for each extruder train from the machine_extruder_trains settings
* and set the general settings to those defaults if they haven't been set yet.
*
* Only sets those settings which haven't already been set on that level - not looking at its parent (FffProcessor, meshgroup) or children (meshes).
*
* \param extruder_nr The index of which extruder train in machine_extruder_trains to get the settings from
* Set a setting to a value.
* \param key the setting
* \param value the value
*/
void setExtruderTrainDefaults(unsigned int extruder_nr);
void setSetting(std::string key, std::string value);
std::string getSettingString(std::string key); //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
std::string getSettingString(std::string key) const; //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
std::string _getSettingString(std::string key) const; //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
std::string getAllLocalSettingsString()
std::string getAllLocalSettingsString() const
{
std::stringstream sstream;
for (auto pair : setting_values)
@@ -214,11 +250,18 @@ public:
return sstream.str();
}
void debugOutputAllLocalSettings()
void debugOutputAllLocalSettings() const
{
for (auto pair : setting_values)
std::cerr << pair.first << " : " << pair.second << std::endl;
}
protected:
/*!
* Set a setting without checking if it's registered.
*
* Used in SettingsRegistry
*/
void _setSetting(std::string key, std::string value);
};
/*!
@@ -232,9 +275,11 @@ public:
SettingsMessenger(SettingsBaseVirtual* parent); //!< construct a SettingsMessenger with a parent settings object
void setSetting(std::string key, std::string value); //!< Set a setting of the parent SettingsBase to a given value
std::string getSettingString(std::string key); //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
std::string getSettingString(std::string key) const; //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
std::string _getSettingString(std::string key) const; //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
};
}//namespace cura
#endif//SETTINGS_H
#endif//SETTINGS_SETTINGS_H
+118 -83
Ver Arquivo
@@ -2,57 +2,97 @@
#include "skin.h"
#include "utils/polygonUtils.h"
#define MIN_AREA_SIZE (INT2MM(extrusionWidth) * INT2MM(extrusionWidth))
#define MIN_AREA_SIZE (0.4 * 0.4)
namespace cura
{
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
void generateSkins(int layerNr, SliceMeshStorage& mesh, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic)
{
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
generateSkinAreas(layerNr, mesh, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
SliceLayer* layer = &storage.layers[layerNr];
SliceLayer* layer = &mesh.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
generateSkinInsets(part, extrusionWidth, insetCount);
}
}
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
SliceLayer& layer = storage.layers[layerNr];
SliceLayer& layer = mesh.layers[layer_nr];
if (downSkinCount == 0 && upSkinCount == 0)
{
return;
}
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
{
SliceLayerPart& part = layer.parts[partNr];
Polygons upskin = part.insets.back().offset(-extrusionWidth/2);
Polygons downskin = upskin;
if (static_cast<int>(layerNr - downSkinCount) >= 0)
if (int(part.insets.size()) < wall_line_count)
{
SliceLayer& layer2 = storage.layers[layerNr - downSkinCount];
for(SliceLayerPart& part2 : layer2.parts)
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin.
}
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
if (upSkinCount == 0) upskin = Polygons();
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
{
if (part.boundaryBox.hit(part2.boundaryBox))
downskin = downskin.difference(part2.insets.back());
Polygons result;
for(SliceLayerPart& part2 : layer2.parts)
{
if (part.boundaryBox.hit(part2.boundaryBox))
{
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
result.add(part2.insets[wall_idx]);
}
}
return result;
};
if (no_small_gaps_heuristic)
{
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
{
downskin = downskin.difference(getInsidePolygons(mesh.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
}
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(mesh.layers.size()))
{
upskin = upskin.difference(getInsidePolygons(mesh.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
}
}
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
else
{
SliceLayer& layer2 = storage.layers[layerNr + upSkinCount];
for(SliceLayerPart& part2 : layer2.parts)
if (layer_nr >= downSkinCount && downSkinCount > 0)
{
if (part.boundaryBox.hit(part2.boundaryBox))
upskin = upskin.difference(part2.insets.back());
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr - 1]);
for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(mesh.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(mesh.layers.size()) - 1 && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(mesh.layers[upskin_layer_nr]));
}
upskin = upskin.difference(not_air); // skin overlaps with the walls
}
}
Polygons skin = upskin.unionPolygons(downskin);
skin.removeSmallAreas(MIN_AREA_SIZE);
for (PolygonsPart& skin_area_part : skin.splitIntoParts())
@@ -64,7 +104,7 @@ void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidt
}
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount)
{
if (insetCount == 0)
{
@@ -78,12 +118,10 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
skin_part.insets.push_back(Polygons());
if (i == 0)
{
PolygonUtils::offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters_0);
Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
skin_part.perimeterGaps.add(in_between);
skin_part.insets[0] = skin_part.outline.offset(- extrusionWidth/2);
} else
{
PolygonUtils::offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
skin_part.insets[i] = skin_part.insets[i - 1].offset(-extrusionWidth);
}
// optimize polygons: remove unnnecesary verts
@@ -97,13 +135,18 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap)
void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = storage.layers[layerNr];
SliceLayer& layer = mesh.layers[layerNr];
for(SliceLayerPart& part : layer.parts)
{
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
if (int(part.insets.size()) < wall_line_count)
{
part.infill_area_per_combine.emplace_back(); // put empty polygons as initial infill_per_combine
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
@@ -117,69 +160,61 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
}
infill.removeSmallAreas(MIN_AREA_SIZE);
part.infill_area.push_back(infill.offset(infill_skin_overlap));
part.infill_area = infill.offset(infill_skin_overlap);
part.infill_area_per_combine.push_back(part.infill_area);
}
}
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount)
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount)
{
SliceLayer* layer = &storage.layers[layerNr];
for(int n=1; n<amount; n++)
// Note that *all* parts should have an [infill_area_per_combine] with one element in it, which up till now only contains the exact same polygons as [infill].
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
{
if (layerNr < n)
break;
SliceLayer* layer2 = &storage.layers[layerNr - n];
for(SliceLayerPart& part : layer->parts)
return;
}
if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
{
return;
}
/* We need to round down the layer index we start at to the nearest
divisible index. Otherwise we get some parts that have infill at divisible
layers and some at non-divisible layers. Those layers would then miss each
other. */
size_t min_layer = mesh.getSettingAsCount("bottom_layers") + amount - 1;
min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
{
SliceLayer* layer = &mesh.layers[layer_idx];
for(unsigned int n = 1;n < amount;n++)
{
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
if(layer_idx < n)
{
if (part.boundaryBox.hit(part2.boundaryBox))
{
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
}
break;
}
SliceLayer* layer2 = &mesh.layers[layer_idx - n];
for(SliceLayerPart& part : layer->parts)
{
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
{
if(part.boundaryBox.hit(part2.boundaryBox))
{
Polygons intersection = part.infill_area_per_combine[n - 1].intersection(part2.infill_area_per_combine[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area_per_combine[n - 1] = part.infill_area_per_combine[n - 1].difference(intersection);
part2.infill_area_per_combine[0] = part2.infill_area_per_combine[0].difference(intersection);
}
}
part.infill_area_per_combine.push_back(result);
}
part.infill_area.push_back(result);
}
}
}
void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
SliceLayer& layer = storage.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{ // handle gaps between perimeters etc.
if (downSkinCount > 0 && upSkinCount > 0 && // note: if both are zero or less, then all gaps will be used
layer_nr >= downSkinCount && layer_nr < static_cast<int>(storage.layers.size() - upSkinCount)) // remove gaps which appear within print, i.e. not on the bottom most or top most skin
{
Polygons outlines_above;
for (SliceLayerPart& part_above : storage.layers[layer_nr + upSkinCount].parts)
{
if (part.boundaryBox.hit(part_above.boundaryBox))
{
outlines_above.add(part_above.outline);
}
}
Polygons outlines_below;
for (SliceLayerPart& part_below : storage.layers[layer_nr - downSkinCount].parts)
{
if (part.boundaryBox.hit(part_below.boundaryBox))
{
outlines_below.add(part_below.outline);
}
}
part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
}
part.perimeterGaps.removeSmallAreas(MIN_AREA_SIZE);
}
}
}//namespace cura
+32 -25
Ver Arquivo
@@ -6,42 +6,33 @@
namespace cura
{
/*!
* Generate the gap areas which occur between consecutive insets.
*
* \param layerNr The index of the layer for which to generate the gaps.
* \param storage The storage where the layer outline information (input) is stored and where the gap areas (output) are stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom gaps
* \param upSkinCount The number of layers of top gaps
*/
void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
/*!
* Generate the skin areas and its insets.
*
* \param layerNr The index of the layer for which to generate the skins.
* \param storage The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
* \param mesh The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param innermost_wall_extrusion_width The line width of the inner most wall
* \param insetCount The number of perimeters to surround the skin
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
*/
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
void generateSkins(int layerNr, SliceMeshStorage& mesh, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic);
/*!
* Generate the skin areas (outlines)
*
* \param layerNr The index of the layer for which to generate the skins.
* \param storage The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param mesh The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
*/
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
void generateSkinAreas(int layerNr, SliceMeshStorage& mesh, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic);
/*!
* Generate the skin insets.
@@ -50,20 +41,36 @@ void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidt
* \param part The part where the skin outline information (input) is stored and where the skin insets (output) are stored.
* \param extrusionWidth extrusionWidth
* \param insetCount The number of perimeters to surround the skin
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
*/
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount);
/*!
* Generate Infill
* Generate Infill by offsetting from the last wall.
*
* The walls should already be generated.
*
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
*
* \param layerNr The index of the layer for which to generate the infill
* \param mesh The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
* \param extrusionWidth width of the wall lines
* \param innermost_wall_extrusion_width width of the innermost wall lines
* \param infill_skin_overlap overlap distance between infill and skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
*/
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount);
void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
/*!
* \brief Combines the infill of multiple layers for a specified mesh.
*
* The infill layers are combined while the thickness of each layer is
* multiplied such that the infill should fill up again to the full height of
* all combined layers.
*
* \param mesh The mesh to combine the infill layers of.
* \param amount The number of layers to combine.
*/
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount);
}//namespace cura
+249 -27
Ver Arquivo
@@ -1,53 +1,278 @@
#include "sliceDataStorage.h"
#include "FffProcessor.h" //To create a mesh group with if none is provided.
namespace cura
{
Polygons SliceLayer::getOutlines(bool external_polys_only)
Polygons SliceLayer::getOutlines(bool external_polys_only) const
{
Polygons ret;
getOutlines(ret, external_polys_only);
return ret;
}
void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
{
for (SliceLayerPart& part : parts)
for (const SliceLayerPart& part : parts)
{
if (external_polys_only)
{
result.add(part.outline.outerPolygon());
result.add(const_cast<SliceLayerPart&>(part).outline.outerPolygon()); // TODO: make a const version of outerPolygon()
}
else
{
result.add(part.outline);
result.add(part.print_outline);
}
}
}
Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only)
Polygons SliceLayer::getSecondOrInnermostWalls() const
{
Polygons total;
Polygons ret;
getSecondOrInnermostWalls(ret);
return ret;
}
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
{
for (const SliceLayerPart& part : parts)
{
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[1]); // TODO const cast!
continue;
}
// but we'll also take the inner wall if the 2nd doesn't exist
if (part.insets.size() == 1) {
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[0]); // TODO const cast!
continue;
}
// offset_from_outlines was so large that it completely destroyed our isle,
// so we'll just use the regular outline
layer_walls.add(part.outline);
continue;
}
}
std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
{
std::vector<RetractionConfig> ret;
ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeTravelConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* retraction_config = nullptr;
travel_config_per_extruder.emplace_back(retraction_config, PrintFeatureType::MoveCombing);
}
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeSkirtConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
}
return ret;
}
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config_per_extruder(initializeTravelConfigs()),
skirt_config(initializeSkirtConfigs()),
raft_base_config(&retraction_config_per_extruder[getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_interface_config(&retraction_config_per_extruder[getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_surface_config(&retraction_config_per_extruder[getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
support_config(&retraction_config_per_extruder[getSettingAsIndex("support_infill_extruder_nr")], PrintFeatureType::Support),
support_roof_config(&retraction_config_per_extruder[getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
max_object_height_second_to_last_extruder(-1)
{
}
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
{
if (layer_nr < 0)
{ // when processing raft
if (include_helper_parts)
{
if (external_polys_only)
{
std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
Polygons result;
for (PolygonsPart& part : parts)
{
result.add(part.outerPolygon());
}
return result;
}
else
{
return raftOutline;
}
}
else
{
return Polygons();
}
}
else
{
Polygons total;
for (const SliceMeshStorage& mesh : meshes)
{
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make all getSetting functions const??
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
}
total.add(primeTower.ground_poly);
}
return total;
}
}
Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const
{
if (layer_nr < 0)
{ // when processing raft
if (include_helper_parts)
{
return raftOutline;
}
else
{
return Polygons();
}
}
else
{
Polygons total;
for (const SliceMeshStorage& mesh : meshes)
{
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(total);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make getSetting const? make settings.setting_values mapping mutable??
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
}
total.add(primeTower.ground_poly);
}
return total;
}
}
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr)
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
if (layer_nr < 0)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true; // raft
}
else
{
if (layer_nr == 0)
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield
// support
if (support.supportLayers[layer_nr].supportAreas.size() > 0)
{
if (layer_nr == 0)
{
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
}
else
{
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
}
}
if (support.supportLayers[layer_nr].roofs.size() > 0)
{
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
}
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
int extr_nr = mesh.getSettingAsIndex("extruder_nr");
if (layer.parts.size() > 0)
{
ret[extr_nr] = true;
}
}
}
return ret;
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield ..?
// support
// support is presupposed to be present...
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
}
total.add(primeTower.ground_poly);
}
return total;
return ret;
}
@@ -72,9 +297,6 @@ Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_
+95 -39
Ver Arquivo
@@ -4,6 +4,8 @@
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "utils/NoCopy.h"
#include "utils/AABB.h"
#include "mesh.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
@@ -21,7 +23,6 @@ class SkinPart
public:
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
/*!
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
@@ -33,10 +34,11 @@ class SliceLayerPart
public:
AABB boundaryBox; //!< The boundaryBox is an axis-aligned bounardy box which is used to quickly check for possible collision between different parts on different layers. It's an optimalization used during skin calculations.
PolygonsPart outline; //!< The outline is the first member that is filled, and it's filled with polygons that match a cross section of the 3D model. The first polygon is the outer boundary polygon and the rest are holes.
Polygons print_outline; //!< An approximation to the outline of what's actually printed, based on the outer wall. Too small parts will be omitted compared to the outline.
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
Polygons infill_area; //!< The areas which need to be filled with sparse (0-99%) infill. Like SliceLayerPart::outline, this class member is not used to actually determine the feature area, but is used to compute the infill_area_per_combine and the inside comb boundary.
std::vector<Polygons> infill_area_per_combine; //!< The areas which need to be filled with sparse (0-99%) infill for different thicknesses. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
};
/*!
@@ -51,8 +53,35 @@ public:
std::vector<SliceLayerPart> parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model.
Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
Polygons getOutlines(bool external_polys_only = false);
void getOutlines(Polygons& result, bool external_polys_only = false);
/*!
* Get the all outlines of all layer parts in this layer.
*
* \param external_polys_only Whether to only include the outermost outline of each layer part
* \return A collection of all the outline polygons
*/
Polygons getOutlines(bool external_polys_only = false) const;
/*!
* Get the all outlines of all layer parts in this layer.
* Add those polygons to @p result.
*
* \param external_polys_only Whether to only include the outermost outline of each layer part
* \param result The result: a collection of all the outline polygons
*/
void getOutlines(Polygons& result, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* \return The collection of all polygons thus obtained
*/
Polygons getSecondOrInnermostWalls() const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* Add those polygons to @p result.
* \param result The result: the collection of all polygons thus obtained
*/
void getSecondOrInnermostWalls(Polygons& result) const;
};
/******************/
@@ -91,15 +120,15 @@ public:
std::vector<GCodePathConfig> infill_config;
SliceMeshStorage(SettingsBaseVirtual* settings)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN")
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
{
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(&retraction_config, "FILL");
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
}
};
class SliceDataStorage : public SettingsMessenger
class SliceDataStorage : public SettingsMessenger, NoCopy
{
public:
MeshGroup* meshgroup; // needed to pass on the per extruder settings.. (TODO: put this somewhere else? Put the per object settings here directly, or a pointer only to the per object settings.)
@@ -108,11 +137,16 @@ public:
std::vector<SliceMeshStorage> meshes;
std::vector<RetractionConfig> retraction_config_per_extruder; //!< used for support, skirt, etc.
RetractionConfig retraction_config; //!< The retraction config used as fallback when getting the per_extruder_config or the mesh config was impossible (for travelConfig)
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
std::vector<GCodePathConfig> skirt_config; //!< config for skirt per extruder
std::vector<CoastingConfig> coasting_config; //!< coasting config per extruder
GCodePathConfig raft_base_config;
GCodePathConfig raft_interface_config;
GCodePathConfig raft_surface_config;
GCodePathConfig support_config;
GCodePathConfig support_roof_config;
@@ -128,33 +162,30 @@ public:
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
Point wipePoint;
std::vector<RetractionConfig> initializeRetractionConfigs()
{
std::vector<RetractionConfig> ret;
ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
return ret;
}
std::vector<GCodePathConfig> initializeSkirtConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
}
return ret;
}
SliceDataStorage(MeshGroup* meshgroup)
: SettingsMessenger(meshgroup)
, meshgroup(meshgroup)
, retraction_config_per_extruder(initializeRetractionConfigs())
, skirt_config(initializeSkirtConfigs())
, support_config(&retraction_config_per_extruder[meshgroup->getSettingAsIndex("support_extruder_nr")], "SUPPORT")
, support_roof_config(&retraction_config_per_extruder[meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN")
, max_object_height_second_to_last_extruder(-1)
// , primeTower()
{
}
/*!
* Construct the initial retraction_config_per_extruder
*/
std::vector<RetractionConfig> initializeRetractionConfigs();
/*!
* Construct the initial travel_config_per_extruder
*/
std::vector<GCodePathConfig> initializeTravelConfigs();
/*!
* Construct the initial skirt_config s for each extruder
*/
std::vector<GCodePathConfig> initializeSkirtConfigs();
/*!
* \brief Creates a new slice data storage that stores the slice data of the
* specified mesh group.
*
* It will obtain the settings from the mesh group too. The mesh group is
* not yet sliced in this constructor. If no mesh group is provided, an
* empty one will be created.
*
* \param meshgroup The mesh group to load into this data storage, if any.
*/
SliceDataStorage(MeshGroup* meshgroup);
~SliceDataStorage()
{
@@ -163,11 +194,36 @@ public:
/*!
* Get all outlines within a given layer.
*
* \param layer_nr the index of the layer for which to get the outlines
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
* \param include_helper_parts whether to include support and prime tower
* \param external_polys_only whether to disregard all hole polygons
*/
Polygons getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only = false);
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
*
* For helper parts the outlines are used.
*
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
* \param include_helper_parts whether to include support and prime tower
*/
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const;
/*!
* Get the extruder numbers of all extruders used in a given layer.
*
* \param layer_nr the index of the layer for which to get the extruders used (negative layer numbers indicate the raft)
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed(int layer_nr);
/*!
* Get the extruders used.
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
};
}//namespace cura
+367 -231
Ver Arquivo
@@ -1,323 +1,459 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include <algorithm> // remove_if
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "slicer.h"
#include "debug.h" // TODO remove
namespace cura {
void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
{
Polygons openPolygonList;
namespace cura {
// connect line segments
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
int largest_neglected_gap_first_phase = MM2INT(0.01); //!< distance between two line segments regarded as connected
int largest_neglected_gap_second_phase = MM2INT(0.02); //!< distance between two line segments regarded as connected
int max_stitch1 = MM2INT(10.0); //!< maximal distance stitched between open polylines to form polygons
void SlicerLayer::makeBasicPolygonLoops(const Mesh* mesh, Polygons& open_polylines)
{
for(unsigned int start_segment_idx = 0; start_segment_idx < segments.size(); start_segment_idx++)
{
if (segmentList[startSegment].addedToPolygon)
continue;
Polygon poly;
poly.add(segmentList[startSegment].start);
unsigned int segmentIndex = startSegment;
bool canClose;
while(true)
if (!segments[start_segment_idx].addedToPolygon)
{
canClose = false;
segmentList[segmentIndex].addedToPolygon = true;
Point p0 = segmentList[segmentIndex].end;
poly.add(p0);
int nextIndex = -1;
const MeshFace& face = mesh->faces[segmentList[segmentIndex].faceIndex];
for(unsigned int i=0;i<3;i++)
{
decltype(face_idx_to_segment_index.begin()) it;
if (face.connected_face_index[i] > -1 && (it = face_idx_to_segment_index.find(face.connected_face_index[i])) != face_idx_to_segment_index.end())
{
int index = (*it).second;
Point p1 = segmentList[index].start;
Point diff = p0 - p1;
if (shorterThen(diff, MM2INT(0.01)))
{
if (index == static_cast<int>(startSegment))
canClose = true;
if (segmentList[index].addedToPolygon)
continue;
nextIndex = index;
}
}
}
if (nextIndex == -1)
break;
segmentIndex = nextIndex;
makeBasicPolygonLoop(mesh, open_polylines, start_segment_idx);
}
if (canClose)
polygonList.add(poly);
else
openPolygonList.add(poly);
}
//Clear the segmentList to save memory, it is no longer needed after this point.
segmentList.clear();
segments.clear();
}
void SlicerLayer::makeBasicPolygonLoop(const Mesh* mesh, Polygons& open_polylines, unsigned int start_segment_idx)
{
// TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons.
//Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
//First link up polygon ends that are within 2 microns.
for(unsigned int i=0;i<openPolygonList.size();i++)
Polygon poly;
poly.add(segments[start_segment_idx].start);
for (int segment_idx = start_segment_idx; segment_idx != -1; )
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
SlicerSegment& segment = segments[segment_idx];
poly.add(segment.end);
segment.addedToPolygon = true;
segment_idx = getNextSegmentIdx(mesh, segment, start_segment_idx);
if (segment_idx == static_cast<int>(start_segment_idx))
{ // polyon is closed
polygons.add(poly);
return;
}
}
// polygon couldn't be closed
open_polylines.add(poly);
}
int SlicerLayer::getNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment, unsigned int start_segment_idx)
{
int next_segment_idx = -1;
const MeshFace& face = mesh->faces[segment.faceIndex];
for (unsigned int face_edge_idx = 0; face_edge_idx < 3; face_edge_idx++)
{ // check segments in connected faces
decltype(face_idx_to_segment_idx.begin()) it;
if (face.connected_face_index[face_edge_idx] > -1 && (it = face_idx_to_segment_idx.find(face.connected_face_index[face_edge_idx])) != face_idx_to_segment_idx.end())
{
if (openPolygonList[j].size() < 1) continue;
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < MM2INT(0.02) * MM2INT(0.02))
int segment_idx = (*it).second;
Point p1 = segments[segment_idx].start;
Point diff = segment.end - p1;
if (shorterThen(diff, largest_neglected_gap_first_phase))
{
if (i == j)
if (segment_idx == static_cast<int>(start_segment_idx))
{
polygonList.add(openPolygonList[i]);
openPolygonList[i].clear();
break;
}else{
for(unsigned int n=0; n<openPolygonList[j].size(); n++)
openPolygonList[i].add(openPolygonList[j][n]);
return start_segment_idx;
}
if (segments[segment_idx].addedToPolygon)
{
continue;
}
next_segment_idx = segment_idx; // not immediately returned since we might still encounter the start_segment_idx
}
}
}
return next_segment_idx;
}
openPolygonList[j].clear();
void SlicerLayer::connectOpenPolylines(Polygons& open_polylines)
{
// TODO use some space partitioning data structure to make this run faster than O(n^2)
for(unsigned int open_polyline_idx = 0; open_polyline_idx < open_polylines.size(); open_polyline_idx++)
{
PolygonRef open_polyline = open_polylines[open_polyline_idx];
if (open_polyline.size() < 1) continue;
for(unsigned int open_polyline_other_idx = 0; open_polyline_other_idx < open_polylines.size(); open_polyline_other_idx++)
{
PolygonRef open_polyline_other = open_polylines[open_polyline_other_idx];
if (open_polyline_other.size() < 1) continue;
Point diff = open_polyline.back() - open_polyline_other[0];
if (shorterThen(diff, largest_neglected_gap_second_phase))
{
if (open_polyline_idx == open_polyline_other_idx)
{
polygons.add(open_polyline);
open_polyline.clear();
break;
}
else
{
for (unsigned int line_idx = 0; line_idx < open_polyline_other.size(); line_idx++)
{
open_polyline.add(open_polyline_other[line_idx]);
}
open_polyline_other.clear();
}
}
}
}
}
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::NORMAL)
void SlicerLayer::stitch(Polygons& open_polylines)
{ // TODO This is an inefficient implementation which can run in O(n^3) time.
// below code closes smallest gaps first
while(1)
{
//Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
while(1)
{
int64_t bestScore = MM2INT(10.0) * MM2INT(10.0);
unsigned int bestA = -1;
unsigned int bestB = -1;
int64_t best_dist2 = max_stitch1 * max_stitch1;
unsigned int best_polyline_1_idx = -1;
unsigned int best_polyline_2_idx = -1;
bool reversed = false;
for(unsigned int i=0;i<openPolygonList.size();i++)
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
{
if (openPolygonList[i].size() < 1) continue;
for(unsigned int j=0;j<openPolygonList.size();j++)
PolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
for(unsigned int polyline_2_idx = 0; polyline_2_idx < open_polylines.size(); polyline_2_idx++)
{
if (openPolygonList[j].size() < 1) continue;
PolygonRef polyline_2 = open_polylines[polyline_2_idx];
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
if (polyline_2.size() < 1) continue;
Point diff = polyline_1.back() - polyline_2[0];
int64_t dist2 = vSize2(diff);
if (dist2 < best_dist2)
{
bestScore = distSquared;
bestA = i;
bestB = j;
best_dist2 = dist2;
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_2_idx;
reversed = false;
}
if (i != j)
if (polyline_1_idx != polyline_2_idx)
{
Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1];
int64_t distSquared = vSize2(diff);
if (distSquared < bestScore)
Point diff = polyline_1.back() - polyline_2.back();
int64_t dist2 = vSize2(diff);
if (dist2 < best_dist2)
{
bestScore = distSquared;
bestA = i;
bestB = j;
best_dist2 = dist2;
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_2_idx;
reversed = true;
}
}
}
}
if (bestScore >= MM2INT(10.0) * MM2INT(10.0))
break;
if (best_dist2 >= max_stitch1 * max_stitch1)
break; // this code is reached if there was nothing to stitch within the distance limits
if (bestA == bestB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}else{
PolygonRef polyline_1 = open_polylines[best_polyline_1_idx];
PolygonRef polyline_2 = open_polylines[best_polyline_2_idx];
if (best_polyline_1_idx == best_polyline_2_idx)
{ // connect last piece of 'circle'
polygons.add(polyline_1);
polyline_1.clear();
}
else
{ // connect two polylines
if (reversed)
{
if (openPolygonList[bestA].polygonLength() > openPolygonList[bestB].polygonLength())
if (polyline_1.size() > polyline_2.size()) // decide which polygon to copy into the other
{
for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
}else{
for(unsigned int n=openPolygonList[bestA].size()-1; int(n)>=0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
for(int poly_idx = polyline_2.size()-1; poly_idx >= 0; poly_idx--)
polyline_1.add(polyline_2[poly_idx]);
polyline_2.clear();
}
else
{
for(int poly_idx = polyline_1.size()-1; poly_idx >= 0; poly_idx--)
polyline_2.add(polyline_1[poly_idx]);
polyline_1.clear();
}
}else{
for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
openPolygonList[bestA].add(openPolygonList[bestB][n]);
openPolygonList[bestB].clear();
// note that either way we end up with the end of former polyline_1 next to the start of former polyline_2
}
else
{
for(Point& p : polyline_2)
polyline_1.add(p);
polyline_2.clear();
}
}
}
}
if (extensive_stitching)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
// Then find the sortest path over this polygon that can be used to connect the open polygons,
// And generate a path over this shortest bit to link up the 2 open polygons.
// (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
while(1)
{
unsigned int bestA = -1;
unsigned int bestB = -1;
GapCloserResult bestResult;
bestResult.len = POINT_MAX;
bestResult.polygonIdx = -1;
bestResult.pointIdxA = -1;
bestResult.pointIdxB = -1;
for(unsigned int i=0; i<openPolygonList.size(); i++)
{
if (openPolygonList[i].size() < 1) continue;
{
GapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = i;
bestResult = res;
}
}
}
for(unsigned int j=0; j<openPolygonList.size(); j++)
void SlicerLayer::stitch_extensive(Polygons& open_polylines)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
// Then find the shortest path over this polygon that can be used to connect the open polygons,
// And generate a path over this shortest bit to link up the 2 open polygons.
// (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
while(1)
{
unsigned int best_polyline_1_idx = -1;
unsigned int best_polyline_2_idx = -1;
GapCloserResult best_result;
best_result.len = POINT_MAX;
best_result.polygonIdx = -1;
best_result.pointIdxA = -1;
best_result.pointIdxB = -1;
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
{
PolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
{
GapCloserResult res = findPolygonGapCloser(polyline_1[0], polyline_1.back());
if (res.len > 0 && res.len < best_result.len)
{
if (openPolygonList[j].size() < 1 || i == j) continue;
GapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
bestB = j;
bestResult = res;
}
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_1_idx;
best_result = res;
}
}
if (bestResult.len < POINT_MAX)
for(unsigned int polyline_2_idx = 0; polyline_2_idx < open_polylines.size(); polyline_2_idx++)
{
if (bestA == bestB)
PolygonRef polyline_2 = open_polylines[polyline_2_idx];
if (polyline_2.size() < 1 || polyline_1_idx == polyline_2_idx) continue;
GapCloserResult res = findPolygonGapCloser(polyline_1[0], polyline_2.back());
if (res.len > 0 && res.len < best_result.len)
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
polygonList.add(openPolygonList[bestA]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
PolygonRef poly = polygonList.newPoly();
for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][j]);
for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
poly.add(openPolygonList[bestA][j]);
openPolygonList[bestA].clear();
}
else
{
unsigned int n = polygonList.size();
polygonList.add(openPolygonList[bestA]);
for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
polygonList[n].add(polygonList[bestResult.polygonIdx][j]);
openPolygonList[bestA].clear();
}
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_2_idx;
best_result = res;
}
}
}
if (best_result.len < POINT_MAX)
{
if (best_polyline_1_idx == best_polyline_2_idx)
{
if (best_result.pointIdxA == best_result.pointIdxB)
{
polygons.add(open_polylines[best_polyline_1_idx]);
open_polylines[best_polyline_1_idx].clear();
}
else if (best_result.AtoB)
{
PolygonRef poly = polygons.newPoly();
for(unsigned int j = best_result.pointIdxA; j != best_result.pointIdxB; j = (j + 1) % polygons[best_result.polygonIdx].size())
poly.add(polygons[best_result.polygonIdx][j]);
for(unsigned int j = open_polylines[best_polyline_1_idx].size() - 1; int(j) >= 0; j--)
poly.add(open_polylines[best_polyline_1_idx][j]);
open_polylines[best_polyline_1_idx].clear();
}
else
{
if (bestResult.pointIdxA == bestResult.pointIdxB)
{
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else if (bestResult.AtoB)
{
Polygon poly;
for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
poly.add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
openPolygonList[bestB].add(poly[n]);
for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
else
{
for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
openPolygonList[bestB].add(polygonList[bestResult.polygonIdx][n]);
for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
openPolygonList[bestB].add(openPolygonList[bestA][n]);
openPolygonList[bestA].clear();
}
unsigned int n = polygons.size();
polygons.add(open_polylines[best_polyline_1_idx]);
for(unsigned int j = best_result.pointIdxB; j != best_result.pointIdxA; j = (j + 1) % polygons[best_result.polygonIdx].size())
polygons[n].add(polygons[best_result.polygonIdx][j]);
open_polylines[best_polyline_1_idx].clear();
}
}
else
{
break;
if (best_result.pointIdxA == best_result.pointIdxB)
{
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
open_polylines[best_polyline_1_idx].clear();
}
else if (best_result.AtoB)
{
Polygon poly;
for(unsigned int n = best_result.pointIdxA; n != best_result.pointIdxB; n = (n + 1) % polygons[best_result.polygonIdx].size())
poly.add(polygons[best_result.polygonIdx][n]);
for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
open_polylines[best_polyline_2_idx].add(poly[n]);
for(unsigned int n=0; n<open_polylines[best_polyline_1_idx].size(); n++)
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
open_polylines[best_polyline_1_idx].clear();
}
else
{
for(unsigned int n = best_result.pointIdxB; n != best_result.pointIdxA; n = (n + 1) % polygons[best_result.polygonIdx].size())
open_polylines[best_polyline_2_idx].add(polygons[best_result.polygonIdx][n]);
for(unsigned int n = open_polylines[best_polyline_1_idx].size() - 1; int(n) >= 0; n--)
open_polylines[best_polyline_2_idx].add(open_polylines[best_polyline_1_idx][n]);
open_polylines[best_polyline_1_idx].clear();
}
}
}
else
{
break;
}
}
}
GapCloserResult SlicerLayer::findPolygonGapCloser(Point ip0, Point ip1)
{
GapCloserResult ret;
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
return ret;
}
ret.polygonIdx = c1.polygonIdx;
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
ret.len = vSize(ip0 - ip1);
}else{
//Find out if we have should go from A to B or the other way around.
Point p0 = polygons[ret.polygonIdx][ret.pointIdxA];
int64_t lenA = vSize(p0 - ip0);
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygons[ret.polygonIdx].size())
{
Point p1 = polygons[ret.polygonIdx][i];
lenA += vSize(p0 - p1);
p0 = p1;
}
lenA += vSize(p0 - ip1);
p0 = polygons[ret.polygonIdx][ret.pointIdxB];
int64_t lenB = vSize(p0 - ip1);
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygons[ret.polygonIdx].size())
{
Point p1 = polygons[ret.polygonIdx][i];
lenB += vSize(p0 - p1);
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
ret.len = lenA;
}else{
ret.AtoB = false;
ret.len = lenB;
}
}
return ret;
}
ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input)
{
ClosePolygonResult ret;
for(unsigned int n=0; n<polygons.size(); n++)
{
Point p0 = polygons[n][polygons[n].size()-1];
for(unsigned int i=0; i<polygons[n].size(); i++)
{
Point p1 = polygons[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
if (lineLength > 1)
{
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
if (distOnLine >= 0 && distOnLine <= lineLength)
{
Point q = p0 + pDiff * distOnLine / lineLength;
if (shorterThen(q - input, 100))
{
ret.intersectionPoint = q;
ret.polygonIdx = n;
ret.pointIdx = i;
return ret;
}
}
}
p0 = p1;
}
}
ret.polygonIdx = -1;
return ret;
}
void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
{
Polygons open_polylines;
makeBasicPolygonLoops(mesh, open_polylines);
connectOpenPolylines(open_polylines);
// TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons.
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::NORMAL)
{ // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be open will be closed
stitch(open_polylines);
}
if (extensive_stitching)
{
stitch_extensive(open_polylines);
}
if (keep_none_closed)
{
for(unsigned int n=0; n<openPolygonList.size(); n++)
for (PolygonRef polyline : open_polylines)
{
if (openPolygonList[n].size() > 0)
polygonList.add(openPolygonList[n]);
if (polyline.size() > 0)
openPolylines.add(polyline);
}
}
for(unsigned int i=0;i<openPolygonList.size();i++)
for (PolygonRef polyline : open_polylines)
{
if (openPolygonList[i].size() > 0)
openPolylines.add(openPolygonList[i]);
if (polyline.size() > 0)
openPolylines.add(polyline);
}
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
int snapDistance = MM2INT(1.0);
for(unsigned int i=0;i<polygonList.size();i++)
{
int length = 0;
for(unsigned int n=1; n<polygonList[i].size(); n++)
{
length += vSize(polygonList[i][n] - polygonList[i][n-1]);
if (length > snapDistance)
break;
}
if (length < snapDistance)
{
polygonList.remove(i);
i--;
}
}
int snapDistance = MM2INT(1.0); // TODO: hardcoded value
auto it = std::remove_if(polygons.begin(), polygons.end(), [snapDistance](PolygonRef poly) { return poly.shorterThan(snapDistance); });
polygons.erase(it, polygons.end());
//Finally optimize all the polygons. Every point removed saves time in the long run.
polygonList.simplify();
polygons.simplify();
polygonList.removeDegenerateVerts(); // remove verts connected to overlapping line segments
polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments
int xy_offset = mesh->getSettingInMicrons("xy_offset");
if (xy_offset != 0)
{
polygonList = polygonList.offset(xy_offset);
polygons = polygons.offset(xy_offset);
}
}
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keep_none_closed, bool extensive_stitching)
: mesh(mesh)
{
assert(layer_count > 0);
@@ -368,10 +504,10 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool kee
// on the slice would create two segments
continue;
}
layers[layer_nr].face_idx_to_segment_index.insert(std::make_pair(mesh_idx, layers[layer_nr].segmentList.size()));
layers[layer_nr].face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layers[layer_nr].segments.size()));
s.faceIndex = mesh_idx;
s.addedToPolygon = false;
layers[layer_nr].segmentList.push_back(s);
layers[layer_nr].segments.push_back(s);
}
}
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
+72 -91
Ver Arquivo
@@ -39,109 +39,90 @@ public:
class SlicerLayer
{
public:
std::vector<SlicerSegment> segmentList;
std::unordered_map<int, int> face_idx_to_segment_index; // topology
std::vector<SlicerSegment> segments;
std::unordered_map<int, int> face_idx_to_segment_idx; // topology
int z;
Polygons polygonList;
Polygons polygons;
Polygons openPolylines;
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
private:
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
{
GapCloserResult ret;
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
return ret;
}
ret.polygonIdx = c1.polygonIdx;
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
ret.len = vSize(ip0 - ip1);
}else{
//Find out if we have should go from A to B or the other way around.
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
int64_t lenA = vSize(p0 - ip0);
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenA += vSize(p0 - p1);
p0 = p1;
}
lenA += vSize(p0 - ip1);
/*!
* Connect the segments into polygons for this layer of this \p mesh
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param keepNoneClosed Whether to throw away the data for segments which we couldn't stitch into a polygon
* \param extensiveStitching Whether to perform extra work to try and close polylines into polygons when there are large gaps
*/
void makePolygons(const Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
int64_t lenB = vSize(p0 - ip1);
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenB += vSize(p0 - p1);
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
ret.len = lenA;
}else{
ret.AtoB = false;
ret.len = lenB;
}
}
return ret;
}
protected:
/*!
* Connect the segments into loops which correctly form polygons (don't perform stitching here)
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
*/
void makeBasicPolygonLoops(const Mesh* mesh, Polygons& open_polylines);
ClosePolygonResult findPolygonPointClosestTo(Point input)
{
ClosePolygonResult ret;
for(unsigned int n=0; n<polygonList.size(); n++)
{
Point p0 = polygonList[n][polygonList[n].size()-1];
for(unsigned int i=0; i<polygonList[n].size(); i++)
{
Point p1 = polygonList[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
if (lineLength > 1)
{
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
if (distOnLine >= 0 && distOnLine <= lineLength)
{
Point q = p0 + pDiff * distOnLine / lineLength;
if (shorterThen(q - input, 100))
{
ret.intersectionPoint = q;
ret.polygonIdx = n;
ret.pointIdx = i;
return ret;
}
}
}
p0 = p1;
}
}
ret.polygonIdx = -1;
return ret;
}
/*!
* Connect the segments into a loop, starting from the segment with index \p start_segment_idx
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
* \param[in] start_segment_idx The index into SlicerLayer::segments for the first segment from which to start the polygon loop
*/
void makeBasicPolygonLoop(const Mesh* mesh, Polygons& open_polylines, unsigned int start_segment_idx);
/*!
* Get the next segment connected to the end of \p segment.
* Used to make closed polygon loops.
* Return ASAP if segment is (also) connected to SlicerLayer::segments[\p start_segment_idx]
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[in] segment The segment from which to start looking for the next
* \param[in] start_segment_idx The index to the segment which when conected to \p segment will immediately stop looking for further candidates.
*/
int getNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment, unsigned int start_segment_idx);
/*!
* Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons.
* First link up polygon ends that are within 2 microns.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
*/
void connectOpenPolylines(Polygons& open_polylines);
/*!
* Link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop yet
*/
void stitch(Polygons& open_polylines);
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1);
ClosePolygonResult findPolygonPointClosestTo(Point input);
/*!
* Try to close up polylines into polygons while they have large gaps in them.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop yet
*/
void stitch_extensive(Polygons& open_polylines);
};
class Slicer
{
public:
std::vector<SlicerLayer> layers;
const Mesh* mesh; //!< The sliced mesh
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
+129 -70
Ver Arquivo
@@ -3,7 +3,8 @@
#include <cmath> // sqrt
#include <utility> // pair
#include "Progress.h"
#include <deque>
#include "progress/Progress.h"
namespace cura
{
@@ -35,21 +36,22 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
return joined;
}
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
{
// initialization of supportAreasPerLayer
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
storage.support.supportLayers.emplace_back();
for(SliceMeshStorage& mesh : storage.meshes)
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, &mesh, layer_count, supportAreas, commandSocket);
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
if (mesh.getSettingBoolean("support_roof_enable"))
{
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
generateSupportRoofs(storage, supportAreas, layer_count, storage.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
}
else
{
@@ -79,66 +81,72 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
*
* for support buildplate only: purge all support not connected to buildplate
*/
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
// given settings
ESupportType support_type = object->getSettingAsSupportType("support_type");
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
if (!object->getSettingBoolean("support_enable"))
if (!mesh.getSettingBoolean("support_enable"))
return;
if (support_type == ESupportType::NONE)
return;
double supportAngle = object->getSettingInAngleRadians("support_angle");
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
int supportZDistance = object->getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = object->getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = object->getSettingInMicrons("support_top_distance");
int join_distance = object->getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = object->getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = object->getSettingInMicrons("support_area_smoothing");
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
int join_distance = mesh.getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing");
int extension_offset = object->getSettingInMicrons("support_offset");
int extension_offset = mesh.getSettingInMicrons("support_offset");
int supportTowerDiameter = object->getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = object->getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = object->getSettingInAngleRadians("support_tower_roof_angle");
int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
int z_layer_distance_tower = 1; // start tower directly below overhang point
int layerThickness = object->getSettingInMicrons("layer_height");
int extrusionWidth = object->getSettingInMicrons("support_line_width");
int supportXYDistance = object->getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
int layerThickness = storage.getSettingInMicrons("layer_height");
int extrusionWidth = storage.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
int support_xy_distance_overhang = mesh.getSettingInMicrons("support_xy_distance_overhang");
bool use_support_xy_distance_overhang = mesh.getSettingAsSupportDistPriority("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
bool conical_support = object->getSettingBoolean("support_conical_enabled");
double conical_support_angle = object->getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = object->getSettingInMicrons("support_conical_min_width");
if (conical_support_angle == 0)
{
conical_support = false;
}
// derived settings:
if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance;
int supportLayerThickness = layerThickness;
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged
int max_dist_from_lower_layer = tanAngle * supportLayerThickness; // max dist which can be bridged
int64_t conical_support_offset;
if (conical_support_angle > 0)
{
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
{ // outward ==> wider base than overhang
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
}
else
{
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
{ // inward ==> smaller base than overhang
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
}
unsigned int support_layer_count = layer_count;
@@ -160,46 +168,28 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
AreaSupport::detectOverhangPoints(storage, *object, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
std::deque<std::pair<Polygons, Polygons>> basic_and_full_overhang_above;
for (unsigned int layer_idx = support_layer_count - 1; layer_idx != support_layer_count - 1 - layerZdistanceTop ; layer_idx--)
{
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
}
bool still_in_upper_empty_layers = true;
int overhang_points_pos = overhang_points.size() - 1;
Polygons supportLayer_last;
std::vector<Polygons> towerRoofs;
for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--)
{
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
Polygons overhang;
{
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
Polygons supportLayer_supportee = object->layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
// Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
//
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
// presumably the computation above is slower than the one below
Polygons overhang_extented = basic_overhang.offset(maxDistFromLowerLayer + 100); // +100 for easier joining with support from layer above
overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
/* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^ supporter
* ^^^^^^^^^^^^^^^^^ supported
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
overhang = basic_and_full_overhang_above.back().second;
basic_and_full_overhang_above.pop_back();
}
Polygons& supportLayer_this = overhang;
@@ -239,17 +229,33 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
// inset using X/Y distance
if (supportLayer_this.size() > 0)
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
{
Polygons& basic_overhang = basic_and_full_overhang_above.front().first; // basic overhang on this layer
Polygons outlines = storage.getLayerOutlines(layer_idx, false);
if (use_support_xy_distance_overhang)
{
Polygons xy_overhang_disallowed = basic_overhang.offset(supportZDistanceTop * tanAngle);
Polygons xy_non_overhang_disallowed = outlines.difference(basic_overhang.offset(supportXYDistance)).offset(supportXYDistance);
Polygons xy_disallowed = xy_overhang_disallowed.unionPolygons(xy_non_overhang_disallowed.unionPolygons(outlines.offset(support_xy_distance_overhang)));
supportLayer_this = supportLayer_this.difference(xy_disallowed);
}
else
{
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
}
}
supportAreas[layer_idx] = supportLayer_this;
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = layer_idx;
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, support_layer_count - layer_idx, support_layer_count, commandSocket);
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
}
// do stuff for when support on buildplate only
@@ -260,6 +266,26 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
{
Polygons& supportLayer = supportAreas[layer_idx];
if (conical_support)
{ // with conical support the next layer is allowed to be larger than the previous
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
//
// conical support can make
// layer above layer below
// v v
// | : |
// | ==> : |__
// |____ :....
//
// a miter limit would result in
// | : : |
// | :.. <== : |__
// .\___ :....
//
}
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
supportAreas[layer_idx] = touching_buildplate;
@@ -267,6 +293,39 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
}
}
/* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^ supporter
* ^^^^^^^^^^^^^^^^^ supported
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx, const int64_t max_dist_from_lower_layer)
{
Polygons supportLayer_supportee = mesh.layers[layer_idx].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
//
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
// presumably the computation above is slower than the one below
Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 100); // +100 for easier joining with support from layer above
Polygons full_overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
return std::make_pair(basic_overhang, full_overhang);
}
void AreaSupport::detectOverhangPoints(
SliceDataStorage& storage,
SliceMeshStorage& mesh,
@@ -406,7 +465,7 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
{
int roof_layer_count = support_roof_height / layerThickness;
+22 -7
Ver Arquivo
@@ -15,9 +15,8 @@ public:
* Generate the support areas and support roof areas for all models.
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
private:
/*!
@@ -26,11 +25,10 @@ private:
* This function also handles small overhang areas (creates towers with larger diameter than just the overhang area) and single walls which could otherwise fall over.
*
* \param storage data storage containing the input layer outline data
* \param object The object for which to generate support areas
* \param mesh_idx The index of the object for which to generate support areas
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas);
@@ -39,11 +37,10 @@ private:
*
* \param storage Output storage: support area + support roof area output
* \param supportAreas The basic support areas for the current mesh
* \param commandSocket Socket over which to report the progress
* \param layerThickness The layer height
* \param support_roof_height The thickness of the hammock in z directiontt
*/
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
@@ -78,6 +75,24 @@ private:
int extrusionWidth
);
/*!
* Compute the basic overhang and full overhang of a layer.
* The basic overhang consists of the parts of this layer which are too far away from the layer below to be supported.
* The full overhang consists of the basic overhang extended toward the border of the layer below.
*
* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
* ^^^^^^^^^^^^^^ full overhang
*
* \param storage The slice data storage
* \param mesh The mesh for which to compute the basic overhangs
* \param layer_idx The layer for which to compute the overhang
* \param max_dist_from_lower_layer The outward distance from the layer below which can be supported by it
* \return a pair of basic overhang and full overhang
*/
static std::pair<Polygons, Polygons> computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx, const int64_t max_dist_from_lower_layer);
/*!
* Adds tower pieces to the current support layer.
* From below the roof, the towers are added to the normal support layer and handled as normal support area.
+9 -2
Ver Arquivo
@@ -24,8 +24,15 @@ void TimeEstimateCalculator::setPosition(Position newPos)
currentPosition = newPos;
}
void TimeEstimateCalculator::addTime(double time)
{
extra_time += time;
}
void TimeEstimateCalculator::reset()
{
extra_time = 0.0;
blocks.clear();
}
@@ -190,7 +197,7 @@ double TimeEstimateCalculator::calculate()
forward_pass();
recalculate_trapezoids();
double totalTime = 0;
double totalTime = extra_time;
for(unsigned int n=0; n<blocks.size(); n++)
{
Block& block = blocks[n];
@@ -287,7 +294,7 @@ void TimeEstimateCalculator::recalculate_trapezoids()
Block *current;
Block *next = nullptr;
for(unsigned int n=0; n<blocks.size(); n--)
for(unsigned int n=0; n<blocks.size(); n++)
{
current = next;
next = &blocks[n];

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais