Comparar commits

..

457 Commits

Autor SHA1 Mensagem Data
Tim Kuipers f1d59af2a9 Merge branch 'feature_wall_reinforcement' of https://github.com/Ultimaker/CuraEngine into feature_wall_reinforcement 2015-12-03 19:06:26 +01:00
Tim Kuipers 7d354ca587 merge fixes 2015-12-03 19:03:04 +01:00
Tim Kuipers 7560250348 bugfixes wall_reinforcement 2015-12-03 18:57:21 +01:00
Tim Kuipers 9b7a985b54 feature: multple reinforcement walls 2015-12-03 18:57:21 +01:00
Tim Kuipers db0c47e314 doc: wall reinforcement better documentation 2015-12-03 18:57:21 +01:00
Tim Kuipers 4aed36d7d5 bugfixes wall reinforcement: now also works in edge cases and when totally disabled. 2015-12-03 18:57:21 +01:00
Tim Kuipers 44b0522447 wall reinforcement extra walls now have wall_x config 2015-12-03 18:57:21 +01:00
Tim Kuipers 464fb29bca bugfix: wall reinforcement had wrong config 2015-12-03 18:57:21 +01:00
Tim Kuipers fbf989282b refactor: reinforcement_wall ==> wall reinforcement 2015-12-03 18:57:21 +01:00
Tim Kuipers db99b46506 bugfixes for reinforcement wall 2015-12-03 18:57:14 +01:00
Tim Kuipers c7ffd5b23d reinforcement_wall new feature 2015-12-03 18:56:05 +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 56d6d9196f bugfixes wall_reinforcement 2015-11-06 09:26:59 +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
Tim Kuipers e9c9157ac8 feature: multple reinforcement walls 2015-11-05 20:59:01 +01:00
Tim Kuipers 8ad0416428 merge fix from master 2015-11-05 19:25:47 +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 c185c846e6 doc: wall reinforcement better documentation 2015-10-22 21:04:55 +02:00
Tim Kuipers e28cef3272 bugfixes wall reinforcement: now also works in edge cases and when totally disabled. 2015-10-22 21:00:49 +02:00
Tim Kuipers 777f45ac16 wall reinforcement extra walls now have wall_x config 2015-10-22 20:26:16 +02:00
Tim Kuipers a9a3c8a4b7 bugfix: wall reinforcement had wrong config 2015-10-22 20:22:35 +02:00
Tim Kuipers ad2cf3b7a6 refactor: reinforcement_wall ==> wall reinforcement 2015-10-22 20:19:43 +02:00
Tim Kuipers a57ec01cc4 bugfixes for reinforcement wall 2015-10-22 19:45:16 +02:00
Tim Kuipers c787ffad02 reinforcement_wall new feature 2015-10-22 19:22:23 +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
90 arquivos alterados com 1930272 adições e 1754 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
+55 -7
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,18 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
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 +40,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
@@ -33,7 +52,7 @@ set(engine_SRCS
src/infill.cpp
src/inset.cpp
src/layerPart.cpp
src/main.cpp
src/LayerPlanBuffer.cpp
src/MergeInfillLines.cpp
src/mesh.cpp
src/MeshGroup.cpp
@@ -60,15 +79,44 @@ set(engine_SRCS
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
LinearAlg2DTest
)
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()
endif()
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
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)"
+2
Ver Arquivo
@@ -62,6 +62,8 @@ message Polygon {
SkirtType = 5;
InfillType = 6;
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
}
Type type = 1;
bytes points = 2;
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
+22
Ver Arquivo
@@ -0,0 +1,22 @@
#ifndef FAN_SPEED_LAYER_TIME_H
#define FAN_SPEED_LAYER_TIME_H
#include "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
+351 -267
Ver Arquivo
@@ -16,15 +16,30 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
gcode.preSetup(storage.meshgroup);
gcode.resetTotalPrintTimeAndFilament();
if (meshgroup_number == 1)
{
gcode.resetTotalPrintTimeAndFilament();
}
if (command_socket)
command_socket->beginGCode();
setConfigFanSpeedLayerTime();
setConfigCoasting(storage);
setConfigRetraction(storage);
initConfigs(storage);
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{ // skirt
storage.skirt_config[extruder].setLayerHeight(getSettingInMicrons("layer_height_0"));
}
layer_plan_buffer.setPreheatConfig(*storage.meshgroup);
if (meshgroup_number == 1)
{
processStartingCode(storage);
@@ -35,8 +50,13 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
}
meshgroup_number++;
unsigned int total_layers = storage.meshes[0].layers.size();
//gcode.writeComment("Layer count: %d", totalLayers);
size_t total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
total_layers = std::max(total_layers, mesh.layers.size());
}
gcode.writeLayerCountComment(total_layers);
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
if (has_raft)
@@ -51,31 +71,30 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
{
processLayer(storage, layer_nr, total_layers, has_raft);
}
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
gcode.writeFanCommand(0);
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
max_object_height = std::max(max_object_height, storage.model_max.z);
if (command_socket)
{
finalize();
command_socket->sendGCodeLayer();
command_socket->endSendSlicedObject();
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
std::ostringstream prefix;
prefix << ";FLAVOR:UltiGCode\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
command_socket->sendGCodePrefix(prefix.str());
}
}
layer_plan_buffer.flush();
}
void FffGcodeWriter::setConfigFanSpeedLayerTime()
{
fan_speed_layer_time_settings.cool_min_layer_time = getSettingInSeconds("cool_min_layer_time");
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = getSettingInSeconds("cool_min_layer_time_fan_speed_max");
fan_speed_layer_time_settings.cool_fan_speed_min = getSettingInPercentage("cool_fan_speed_min");
fan_speed_layer_time_settings.cool_fan_speed_max = getSettingInPercentage("cool_fan_speed_max");
fan_speed_layer_time_settings.cool_min_speed = getSettingInMillimetersPerSecond("cool_min_speed");
fan_speed_layer_time_settings.cool_fan_full_layer = getSettingAsCount("cool_fan_full_layer");
}
void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
{
@@ -87,127 +106,128 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
coasting_config.coasting_volume_move = train->getSettingInCubicMillimeters("coasting_volume_move");
coasting_config.coasting_min_volume_move = train->getSettingInCubicMillimeters("coasting_min_volume_move");
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move");
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move") / 100.0;
coasting_config.coasting_volume_retract = train->getSettingInCubicMillimeters("coasting_volume_retract");
coasting_config.coasting_min_volume_retract = train->getSettingInCubicMillimeters("coasting_min_volume_retract");
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract");
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
}
}
void FffGcodeWriter::setConfigRetraction(SliceDataStorage& storage)
{
storage.retraction_config.amount = (storage.getSettingBoolean("retraction_enable"))? INT2MM(getSettingInMicrons("retraction_amount")) : 0;
storage.retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
storage.retraction_config.distance = (storage.getSettingBoolean("retraction_enable"))? INT2MM(getSettingInMicrons("retraction_amount")) : 0;
storage.retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
storage.retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
storage.retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
storage.retraction_config.zHop = getSettingInMicrons("retraction_hop");
storage.retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
storage.retraction_config.retraction_extrusion_window = getSettingInMicrons("retraction_extrusion_window");
storage.retraction_config.retraction_count_max = getSettingInMicrons("retraction_count_max");
storage.retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
storage.retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
int extruder_count = storage.meshgroup->getExtruderCount();
for (int extruder = 0; extruder < extruder_count; extruder++)
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
storage.retraction_config_per_extruder[extruder].amount = (train->getSettingBoolean("retraction_enable"))? INT2MM(train->getSettingInMicrons("retraction_amount")) : 0;
storage.retraction_config_per_extruder[extruder].primeAmount = INT2MM(train->getSettingInMicrons("retraction_extra_prime_amount"));
storage.retraction_config_per_extruder[extruder].speed = train->getSettingInMillimetersPerSecond("retraction_retract_speed");
storage.retraction_config_per_extruder[extruder].primeSpeed = train->getSettingInMillimetersPerSecond("retraction_prime_speed");
storage.retraction_config_per_extruder[extruder].zHop = train->getSettingInMicrons("retraction_hop");
storage.retraction_config_per_extruder[extruder].retraction_min_travel_distance = train->getSettingInMicrons("retraction_min_travel");
storage.retraction_config_per_extruder[extruder].retraction_extrusion_window = train->getSettingInMicrons("retraction_extrusion_window");
storage.retraction_config_per_extruder[extruder].retraction_count_max = train->getSettingInMicrons("retraction_count_max");
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder];
retraction_config.distance = (train->getSettingBoolean("retraction_enable"))? INT2MM(train->getSettingInMicrons("retraction_amount")) : 0;
retraction_config.prime_volume = train->getSettingInCubicMillimeters("retraction_extra_prime_amount");
retraction_config.speed = train->getSettingInMillimetersPerSecond("retraction_retract_speed");
retraction_config.primeSpeed = train->getSettingInMillimetersPerSecond("retraction_prime_speed");
retraction_config.zHop = train->getSettingInMicrons("retraction_hop");
retraction_config.retraction_min_travel_distance = train->getSettingInMicrons("retraction_min_travel");
retraction_config.retraction_extrusion_window = INT2MM(train->getSettingInMicrons("retraction_extrusion_window"));
retraction_config.retraction_count_max = train->getSettingAsCount("retraction_count_max");
}
for(SliceMeshStorage& mesh : storage.meshes)
{
mesh.retraction_config.amount = (mesh.getSettingBoolean("retraction_enable"))? INT2MM(mesh.getSettingInMicrons("retraction_amount")) : 0;
mesh.retraction_config.primeAmount = INT2MM(mesh.getSettingInMicrons("retraction_extra_prime_amount"));
mesh.retraction_config.distance = (mesh.getSettingBoolean("retraction_enable"))? INT2MM(mesh.getSettingInMicrons("retraction_amount")) : 0;
mesh.retraction_config.prime_volume = mesh.getSettingInCubicMillimeters("retraction_extra_prime_amount");
mesh.retraction_config.speed = mesh.getSettingInMillimetersPerSecond("retraction_retract_speed");
mesh.retraction_config.primeSpeed = mesh.getSettingInMillimetersPerSecond("retraction_prime_speed");
mesh.retraction_config.zHop = mesh.getSettingInMicrons("retraction_hop");
mesh.retraction_config.retraction_min_travel_distance = mesh.getSettingInMicrons("retraction_min_travel");
mesh.retraction_config.retraction_extrusion_window = mesh.getSettingInMicrons("retraction_extrusion_window");
mesh.retraction_config.retraction_count_max = mesh.getSettingInMicrons("retraction_count_max");
mesh.retraction_config.retraction_extrusion_window = INT2MM(mesh.getSettingInMicrons("retraction_extrusion_window"));
mesh.retraction_config.retraction_count_max = mesh.getSettingAsCount("retraction_count_max");
}
}
void FffGcodeWriter::setConfigSkirt(SliceDataStorage& storage, int layer_thickness)
void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
{
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
storage.skirt_config[extruder].setSpeed(train->getSettingInMillimetersPerSecond("skirt_speed"));
storage.skirt_config[extruder].setLineWidth(train->getSettingInMicrons("skirt_line_width"));
storage.skirt_config[extruder].setFlow(train->getSettingInPercentage("material_flow"));
storage.skirt_config[extruder].setLayerHeight(layer_thickness);
}
}
void FffGcodeWriter::setConfigSupport(SliceDataStorage& storage, int layer_thickness)
{
storage.support_config.setLineWidth(getSettingInMicrons("support_line_width"));
storage.support_config.setSpeed(getSettingInMillimetersPerSecond("speed_support_lines"));
storage.support_config.setFlow(storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"))->getSettingInPercentage("material_flow"));
storage.support_config.setLayerHeight(layer_thickness);
storage.travel_config.init(getSettingInMillimetersPerSecond("speed_travel"), 0, 0);
storage.support_roof_config.setLineWidth(getSettingInMicrons("support_roof_line_width"));
storage.support_roof_config.setSpeed(getSettingInMillimetersPerSecond("speed_support_roof"));
storage.support_roof_config.setFlow(storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_roof_extruder_nr"))->getSettingInPercentage("material_flow"));
storage.support_roof_config.setLayerHeight(layer_thickness);
}
void FffGcodeWriter::setConfigInsets(SliceMeshStorage& mesh, int layer_thickness)
{
mesh.inset0_config.setLineWidth(mesh.getSettingInMicrons("wall_line_width_0"));
mesh.inset0_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_wall_0"));
mesh.inset0_config.setFlow(mesh.getSettingInPercentage("material_flow"));
mesh.inset0_config.setLayerHeight(layer_thickness);
mesh.insetX_config.setLineWidth(mesh.getSettingInMicrons("wall_line_width_x"));
mesh.insetX_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_wall_x"));
mesh.insetX_config.setFlow(mesh.getSettingInPercentage("material_flow"));
mesh.insetX_config.setLayerHeight(layer_thickness);
}
void FffGcodeWriter::setConfigSkin(SliceMeshStorage& mesh, int layer_thickness)
{
mesh.skin_config.setLineWidth(mesh.getSettingInMicrons("skin_line_width"));
mesh.skin_config.setSpeed(mesh.getSettingInMillimetersPerSecond("speed_topbottom"));
mesh.skin_config.setFlow(mesh.getSettingInPercentage("material_flow"));
mesh.skin_config.setLayerHeight(layer_thickness);
}
void FffGcodeWriter::setConfigInfill(SliceMeshStorage& mesh, int layer_thickness)
{
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setLineWidth(mesh.getSettingInMicrons("infill_line_width") * (idx + 1));
mesh.infill_config[idx].setSpeed(mesh.getSettingInMillimetersPerSecond("speed_infill"));
mesh.infill_config[idx].setFlow(mesh.getSettingInPercentage("material_flow"));
mesh.infill_config[idx].setLayerHeight(layer_thickness);
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{ // skirt
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
storage.skirt_config[extruder].init(train->getSettingInMillimetersPerSecond("skirt_speed"), train->getSettingInMicrons("skirt_line_width"), train->getSettingInPercentage("material_flow"));
}
}
{ // support
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_infill"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
}
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
}
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
}
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
}
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
if (!command_socket)
{
if (!command_socket)
std::ostringstream prefix;
prefix << "FLAVOR:" << toString(gcode.getFlavor());
gcode.writeComment(prefix.str().c_str());
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
gcode.writeComment("TIME:666");
gcode.writeComment("MATERIAL:666");
gcode.writeComment("MATERIAL2:-1");
}
}
else
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
for(SliceMeshStorage& mesh : storage.meshes)
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"));
for(SliceMeshStorage& mesh : storage.meshes)
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"), true);
if (getSettingBoolean("material_bed_temp_prepend"))
{
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"))
{
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
{
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"));
}
}
if (getSettingBoolean("material_print_temp_wait"))
{
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingInDegreeCelsius("material_print_temperature") > 0)
{
gcode.writeTemperatureCommand(mesh.getSettingAsIndex("extruder_nr"), mesh.getSettingInDegreeCelsius("material_print_temperature"), true);
}
}
}
}
}
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
@@ -231,115 +251,126 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
}
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int totalLayers)
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
{
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
bool retraction_combing = true;
int n_raft_surface_layers = train->getSettingAsCount("raft_surface_layers");
int z = 0;
{ // set configs
storage.raft_base_config.init(train->getSettingInMillimetersPerSecond("raft_base_speed"), train->getSettingInMicrons("raft_base_line_width"), train->getSettingInPercentage("material_flow"));
storage.raft_base_config.setLayerHeight(train->getSettingInMicrons("raft_base_thickness"));
storage.raft_interface_config.init(train->getSettingInMillimetersPerSecond("raft_interface_speed"), train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInPercentage("material_flow"));
storage.raft_interface_config.setLayerHeight(train->getSettingInMicrons("raft_interface_thickness"));
bool retraction_combing = false; // the raft isn't added to the parts to avoid
storage.raft_surface_config.init(train->getSettingInMillimetersPerSecond("raft_surface_speed"), train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInPercentage("material_flow"));
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
}
{ // raft base layer
gcode.writeLayerComment(-3);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
int layer_nr = -n_raft_surface_layers - 2;
int layer_height = getSettingInMicrons("raft_base_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
gcode_layer.setExtruder(extruder_nr);
gcode.setZ(getSettingInMicrons("raft_base_thickness"));
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &raft_base_config);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_base_line_width"), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
gcode_layer.addLinesByOptimizer(raftLines, &raft_base_config);
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_base_config.getLineWidth());
gcode.writeFanCommand(train->getSettingInPercentage("raft_base_fan_speed"));
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_base_thickness"));
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_base_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
}
{ // raft interface layer
gcode.writeLayerComment(-2);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness"));
int layer_nr = -n_raft_surface_layers - 1;
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
gcode_layer.addLinesByOptimizer(raftLines, &raft_interface_config);
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_interface_config.getLineWidth());
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_interface_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
}
int layer_height = train->getSettingInMicrons("raft_surface_thickness");
for (int raftSurfaceLayer=1; raftSurfaceLayer<=train->getSettingAsCount("raft_surface_layers"); raftSurfaceLayer++)
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
{ // raft surface layers
gcode.writeLayerComment(-1);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer);
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
Polygons raft_lines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
gcode_layer.addLinesByOptimizer(raft_lines, &raft_surface_config);
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config);
sendPolygons(SupportType, layer_nr, raft_lines, storage.raft_surface_config.getLineWidth());
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
gcode_layer.setFanSpeed(train->getSettingInPercentage("raft_surface_fan_speed"));
gcode_layer.processFanSpeedAndMinimalLayerTime();
}
}
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, command_socket);
int layer_thickness = getSettingInMicrons("layer_height");
if (layer_nr == 0 && !has_raft)
if (layer_nr == 0)
{
layer_thickness = getSettingInMicrons("layer_height_0");
}
setConfigSkirt(storage, layer_thickness);
setConfigSupport(storage, layer_thickness);
storage.primeTower.setConfigs(storage.meshgroup, storage.retraction_config_per_extruder, layer_thickness);
for(SliceMeshStorage& mesh : storage.meshes)
int max_nozzle_size = 0;
std::vector<bool> extruders_used = storage.getExtrudersUsed(layer_nr);
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
{
setConfigInsets(mesh, layer_thickness);
setConfigSkin(mesh, layer_thickness);
setConfigInfill(mesh, layer_thickness);
if (extruders_used[extr_nr])
{
max_nozzle_size = std::max(max_nozzle_size, storage.meshgroup->getExtruderTrain(extr_nr)->getSettingInMicrons("machine_nozzle_size"));
}
}
processInitialLayersSpeedup(storage, layer_nr);
gcode.writeLayerComment(layer_nr);
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
int z = storage.meshes[0].layers[layer_nr].printZ;
gcode.setZ(z);
gcode.resetStartPosition();
int64_t comb_offset_from_outlines = max_nozzle_size * 2;// TODO: only used when there is no second wall.
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
if (layer_nr == 0)
{
@@ -357,22 +388,22 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
//Figure out in which order to print the meshes, do this by looking at the current extruder and preferer the meshes that use that extruder.
std::vector<unsigned int> mesh_order = calculateMeshOrder(storage, gcode_layer.getExtruder());
gcode_layer.setCombing(true);
gcode_layer.setIsInside(true);
for(unsigned int mesh_idx : mesh_order)
{
SliceMeshStorage* mesh = &storage.meshes[mesh_idx];
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
addMeshLayerToGCode_meshSurfaceMode(storage, mesh, gcode_layer, layer_nr);
}
else
{
gcode_layer.setCombing(true); // needed when the last mesh was spiralized
gcode_layer.setIsInside(true); // needed when the last mesh was spiralized
addMeshLayerToGCode(storage, mesh, gcode_layer, layer_nr);
}
}
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
addSupportToGCode(storage, gcode_layer, layer_nr, extruder_nr_before, false);
@@ -381,41 +412,20 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
int prev_extruder = gcode_layer.getExtruder(); // most likely the same extruder as we are extruding with now
addPrimeTower(storage, gcode_layer, layer_nr, prev_extruder);
}
processFanSpeedAndMinimalLayerTime(storage, gcode_layer, layer_nr);
gcode_layer.writeGCode(getSettingBoolean("cool_lift_head"), layer_nr > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
}
void FffGcodeWriter::processInitialLayersSpeedup(SliceDataStorage& storage, unsigned int layer_nr)
{
double initial_speedup_layers = getSettingAsCount("speed_slowdown_layers");
if (static_cast<int>(layer_nr) < initial_speedup_layers)
{
double initial_layer_speed = getSettingInMillimetersPerSecond("speed_layer_0");
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
}
}
}
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
gcode_layer.processFanSpeedAndMinimalLayerTime();
}
void FffGcodeWriter::processSkirt(SliceDataStorage& storage, GCodePlanner& gcode_layer, unsigned int extruder_nr)
{
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
Polygons& skirt = storage.skirt[extruder_nr];
if (skirt.size() > 0)
{
gcode_layer.addTravel(skirt[skirt.size()-1].closestPointTo(gcode.getPositionXY()));
gcode_layer.addTravel(skirt[skirt.size()-1].closestPointTo(gcode_layer.getLastPosition()));
}
gcode_layer.addPolygonsByOptimizer(skirt, &storage.skirt_config[extruder_nr]);
@@ -425,7 +435,7 @@ void FffGcodeWriter::processOozeShield(SliceDataStorage& storage, GCodePlanner&
{
if (storage.oozeShield.size() > 0)
{
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
gcode_layer.addPolygonsByOptimizer(storage.oozeShield[layer_nr], &storage.skirt_config[0]); // TODO: skirt config idx should correspond to ooze shield extruder nr
}
}
@@ -448,7 +458,7 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
return;
}
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
}
@@ -543,7 +553,7 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
EZSeamType z_seam_type = mesh->getSettingAsZSeamType("z_seam_type");
PathOrderOptimizer part_order_optimizer(gcode.getStartPositionXY(), z_seam_type);
PathOrderOptimizer part_order_optimizer(last_position_planned, z_seam_type);
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
part_order_optimizer.addPolygon(layer->parts[partNr].insets[0][0]);
@@ -556,33 +566,54 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
{
SliceLayerPart& part = layer->parts[order_idx];
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
int infill_angle = 45;
if (layer_nr & 1)
if ((infill_pattern==EFillMethod::LINES || infill_pattern==EFillMethod::ZIG_ZAG) && layer_nr & 1)
{
infill_angle += 90;
int extrusion_width = mesh->infill_config[0].getLineWidth();
}
int infill_line_width = mesh->infill_config[0].getLineWidth();
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
int wall_reinforcement_line_distance = mesh->getSettingInMicrons("wall_reinforcement_line_distance");
int wall_reinforcement_line_width = mesh->wall_reinforcement_config.getLineWidth();
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
for (unsigned int wall_idx = part.reinforcement_walls.size() - 1; int(wall_idx) >= 0; wall_idx--)
{
ReinforcementWall& reinforcement_wall = part.reinforcement_walls[wall_idx];
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, true);
}
}
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
for (ReinforcementWall& reinforcement_wall : part.reinforcement_walls)
{
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, false);
}
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
}
EFillMethod skin_pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
int skin_angle = 45;
if ((skin_pattern == EFillMethod::LINES || skin_pattern == EFillMethod::ZIG_ZAG) && layer_nr & 1)
{
skin_angle += 90; // should coincide with infill_angle (if both skin and infill are lines) so that the first top layer is orthogonal to the last infill layer
}
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
infill_angle -= 45;
skin_angle -= 45;
int64_t skin_overlap = 0;
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_angle, extrusion_width);
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, skin_angle, mesh->skin_config.getLineWidth());
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
@@ -643,6 +674,54 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
}
void FffGcodeWriter::processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out)
{
if (wall_reinforcement_line_distance == 0 || (reinforcement_wall.wall_reinforcement_area.size() == 0 && reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0) )
{
return;
}
if (inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
processWallReinforcement_infill(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width);
if (!inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
}
void FffGcodeWriter::processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out)
{
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
for(int inset_number=reinforcement_wall.wall_reinforcement_axtra_walls.size()-1; inset_number>-1; inset_number--)
{
gcode_layer.addPolygonsByOptimizer(reinforcement_wall.wall_reinforcement_axtra_walls[inset_number], &mesh->insetX_config);
}
}
}
void FffGcodeWriter::processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width)
{
if (reinforcement_wall.wall_reinforcement_area.size() == 0)
{
return;
}
Polygons infill_polygons;
Polygons infill_lines;
EFillMethod pattern = mesh->getSettingAsFillMethod("wall_reinforcement_pattern");
Infill infill_comp(pattern, reinforcement_wall.wall_reinforcement_area, 0, false, wall_reinforcement_line_width, wall_reinforcement_line_distance, infill_overlap, infill_angle, false, false);
infill_comp.generate(infill_polygons, infill_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->wall_reinforcement_config);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->wall_reinforcement_config);
sendPolygons(SupportInfillType, layer_nr, infill_lines, wall_reinforcement_line_width);
}
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
{
bool compensate_overlap = mesh->getSettingBoolean("travel_compensate_overlapping_walls_enabled");
@@ -681,10 +760,11 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
{
Polygons skin_polygons;
Polygons skin_lines;
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
{
Polygons skin_polygons;
Polygons skin_lines;
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
int bridge = -1;
if (layer_nr > 0)
@@ -703,18 +783,20 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
{
for (Polygons& skin_perimeter : skin_part.insets)
{
sendPolygons(SkinType, layer_nr, skin_perimeter, mesh->skin_config.getLineWidth());
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
}
if (skin_part.insets.size() > 0)
{
inner_skin_outline = &skin_part.insets.back();
offset_from_inner_skin_outline = -extrusion_width/2;
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
}
}
}
if (inner_skin_outline == nullptr)
{
inner_skin_outline = &skin_part.outline;
@@ -725,12 +807,16 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config);
sendPolygons(SkinType, layer_nr, skin_polygons, mesh->skin_config.getLineWidth());
sendPolygons(SkinType, layer_nr, skin_lines, mesh->skin_config.getLineWidth());
}
// handle gaps between perimeters etc.
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
generateLineInfill(part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
Polygons perimeter_gap_lines;
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
}
}
@@ -740,39 +826,39 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
return;
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
|| support_roof_extruder_nr == extruder_nr_before;
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
if (print_support_before_rest != before_rest)
return;
gcode_layer.setCombing(false);
gcode_layer.setIsInside(false);
int current_extruder_nr = gcode_layer.getExtruder();
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
{
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
{
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
}
else
{
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
}
}
else
{
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
}
}
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
{
if (!storage.support.generated
|| layer_nr > storage.support.layer_nr_max_filled_layer
@@ -786,17 +872,17 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
double infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap");
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
std::vector<PolygonsPart> support_islands = support.splitIntoParts();
PathOrderOptimizer island_order_optimizer(gcode.getPositionXY());
PathOrderOptimizer island_order_optimizer(gcode_layer.getLastPosition());
for(unsigned int n=0; n<support_islands.size(); n++)
{
island_order_optimizer.addPolygon(support_islands[n][0]);
@@ -814,9 +900,14 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
infill_comp.generate(support_polygons, support_lines, nullptr);
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addPolygonsByOptimizer(island, &storage.support_config);
sendPolygons(SupportType, layer_nr, island, storage.support_config.getLineWidth());
}
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
}
}
@@ -836,7 +927,11 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
setExtruder_addPrime(storage, gcode_layer, layer_nr, roof_extruder_nr);
double fillAngle;
if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height"))
if (pattern == EFillMethod::CONCENTRIC)
{
fillAngle = 0;
}
else if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height") || pattern == EFillMethod::TRIANGLES)
{
fillAngle = 90; // perpendicular to support lines
}
@@ -852,8 +947,10 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config);
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
}
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
@@ -890,48 +987,35 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
}
void FffGcodeWriter::processFanSpeedAndMinimalLayerTime(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr)
{
double travelTime;
double extrudeTime;
gcodeLayer.getNaiveTimeEstimates(travelTime, extrudeTime);
gcodeLayer.forceMinimalLayerTime(getSettingInSeconds("cool_min_layer_time"), getSettingInMillimetersPerSecond("cool_min_speed"), travelTime, extrudeTime);
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
double fanSpeed = getSettingInPercentage("cool_fan_speed_min");
double totalLayerTime = travelTime + extrudeTime;
if (totalLayerTime < getSettingInSeconds("cool_min_layer_time"))
{
fanSpeed = getSettingInPercentage("cool_fan_speed_max");
}
else if (totalLayerTime < getSettingInSeconds("cool_min_layer_time_fan_speed_max"))
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
double minTime = (getSettingInSeconds("cool_min_layer_time"));
double maxTime = (getSettingInSeconds("cool_min_layer_time_fan_speed_max"));
double fanSpeedMin = getSettingInPercentage("cool_fan_speed_min");
double fanSpeedMax = getSettingInPercentage("cool_fan_speed_max");
fanSpeed = fanSpeedMax - (fanSpeedMax-fanSpeedMin) * (totalLayerTime - minTime) / (maxTime - minTime);
}
if (static_cast<int>(layer_nr) < getSettingAsCount("cool_fan_full_layer"))
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
fanSpeed = fanSpeed * layer_nr / getSettingAsCount("cool_fan_full_layer");
}
gcode.writeFanCommand(fanSpeed);
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
}
void FffGcodeWriter::finalize()
{
gcode.finalize(max_object_height, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
if (command_socket)
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
}
command_socket->sendGCodePrefix(prefix.str());
}
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
gcode.writeComment("End of Gcode");
/*
the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB.
gcode.writeCode("M25 ;Stop reading from this point on.");
gcode.writeComment("Cura profile string:");
gcode.writeComment(FffProcessor::getInstance()->getAllLocalSettingsString() + FffProcessor::getInstance()->getProfileString());
*/
}
+68 -30
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,12 @@
#include "gcodePlanner.h"
#include "gcodeExport.h"
#include "commandSocket.h"
#include "utils/polygonUtils.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "LayerPlanBuffer.h"
namespace cura
{
@@ -26,12 +32,15 @@ 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
LayerPlanBuffer layer_plan_buffer;
GCodeExport gcode;
CommandSocket* command_socket;
std::ofstream output_file;
@@ -42,9 +51,17 @@ private:
* 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];
FanSpeedLayerTimeSettings fan_speed_layer_time_settings;
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
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, layer_plan_buffer(this, command_socket, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // TODO: make configurable
{
meshgroup_number = 1;
max_object_height = 0;
@@ -95,20 +112,19 @@ public:
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
private:
void setConfigFanSpeedLayerTime();
void setConfigCoasting(SliceDataStorage& storage);
//Setup the retraction parameters.
void setConfigRetraction(SliceDataStorage& storage);
void setConfigSkirt(SliceDataStorage& storage, int layer_thickness);
/*!
* initialize GcodePathConfig config parameters which don't change over all layers
*/
void initConfigs(SliceDataStorage& storage);
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);
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
/*!
* Set temperatures and perform initial priming.
@@ -125,25 +141,18 @@ private:
/*!
* Add raft gcode.
* \param storage Input: where the slice data is stored.
* \param totalLayers The total number of layers.
* \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.
* \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);
/*!
* 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);
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
/*!
* Add the skirt to the gcode.
@@ -233,6 +242,43 @@ private:
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Add wall reinforcement 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 reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_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 fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
* \param inside_out Whether to print from inside outward or other way around
*/
void processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the inner extra walls of the wall reinforcement 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 reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the infill of the wall reinforcement 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 reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_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 fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
@@ -271,7 +317,7 @@ private:
* \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.
@@ -301,14 +347,6 @@ private:
*/
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.
*/
+154 -162
Ver Arquivo
@@ -1,9 +1,6 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include <random> // for bulging effect?
#include <functional> // for bugling?
#include <cmath> // for bulging?
#include "slicer.h"
#include "utils/gettime.h"
@@ -49,13 +46,28 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
log("Slicing model...\n");
int initial_layer_thickness = meshgroup->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.",initial_layer_thickness);
return false;
}
int layer_thickness = meshgroup->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.",layer_thickness);
return false;
}
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{
initial_layer_thickness = layer_thickness;
}
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.
{
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
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++)
@@ -79,9 +91,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
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);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
@@ -99,20 +108,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
meshStorage.getSettingInMicrons("layer_height_0")
- initial_slice_z;
if (has_raft)
{
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;
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap");
}
@@ -123,7 +128,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
if (commandSocket)
{
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0 && !has_raft? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
}
}
@@ -136,8 +141,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
// const
unsigned int total_layers = storage.meshes.at(0).layers.size();
size_t total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
}
//layerparts2HTML(storage, "output/output.html");
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
@@ -157,6 +166,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
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++)
@@ -165,6 +175,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
int mesh_max_bottom_layer_count = 0;
@@ -179,15 +190,20 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
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.
{
processSkins(storage, layer_number);
processSkinsAndInfill(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(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
for(SliceMeshStorage& mesh : storage.meshes)
combineInfillLayers(layer_number, mesh, mesh.getSettingAsCount("infill_sparse_combine"));
processWallReinforcement(storage, layer_number);
}
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
for(SliceMeshStorage& mesh : storage.meshes)
{
combineInfillLayers(mesh,combined_infill_layers);
}
storage.primeTower.computePrimeTowerMax(storage);
@@ -204,7 +220,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzySkin(mesh);
processFuzzyWalls(mesh);
}
else if (mesh.getSettingAsCount("wall_line_count") > 0)
{ // only send polygon data
for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
{
SliceLayer* layer = &mesh.layers[layer_nr];
for(SliceLayerPart& part : layer->parts)
{
sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
}
@@ -229,20 +256,12 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
{
if (layer->parts[partNr].insets.size() > 0)
{
sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0);
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin
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 (PolygonRef polyline : layer->openPolyLines)
@@ -260,10 +279,75 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers)
void FffPolygonGenerator::processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
{ // generate infill area
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
continue;
}
if (mesh.getSettingInMicrons("wall_reinforcement_thickness") == 0.0 && mesh.getSettingAsCount("wall_reinforcement_line_count") == 0)
{
return;
}
int inset_count = mesh.getSettingAsCount("wall_reinforcement_line_count");
int wall_line_width = mesh.getSettingInMicrons("wall_line_width_x");
SliceLayer* layer = &mesh.layers[layer_nr];
for (SliceLayerPart& part : layer->parts)
{
if (part.infill_area.size() == 0)
{
continue;
}
int wall_reinforcement_count = mesh.getSettingAsCount("wall_reinforcement_count");
part.reinforcement_walls.reserve(wall_reinforcement_count);
for (unsigned int wall_idx = 0; int(wall_idx) < wall_reinforcement_count; wall_idx++)
{
part.reinforcement_walls.emplace_back();
ReinforcementWall& reinforcement_wall = part.reinforcement_walls.back();
Polygons outer_wall_reinforcement_edge = part.infill_area[0].offset(-mesh.getSettingInMicrons("wall_reinforcement_thickness"));
reinforcement_wall.wall_reinforcement_area = part.infill_area[0].difference(outer_wall_reinforcement_edge);
if (mesh.getSettingAsCount("wall_reinforcement_line_count") > 0)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(outer_wall_reinforcement_edge.offset(-wall_line_width/2));
}
else
{
part.infill_area[0] = outer_wall_reinforcement_edge.offset(-wall_line_width/2);
}
// generate reinforcement wall extra walls
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0)
{
continue;
}
generateWallReinforcementWallExtraWalls(&part, reinforcement_wall, wall_line_width, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
part.infill_area[0] = reinforcement_wall.wall_reinforcement_axtra_walls.back().offset(-wall_line_width/2); // update the infill area to one reinforcement wall insetted (updated each time a reinforcement wall is generated)
}
if (part.insets.size() > 0)
{
for(Polygons& polys : reinforcement_wall.wall_reinforcement_axtra_walls)
sendPolygons(SupportType, layer_nr, polys, wall_line_width);
}
}
}
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, 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 +381,56 @@ 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(SliceDataStorage& storage, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
int extrusionWidth = mesh.getSettingInMicrons("wall_line_width_x");
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = 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"));
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"), 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;
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
{
infill_skin_overlap = extrusionWidth / 2;
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
if (mesh.getSettingString("fill_perimeter_gaps") == "Skin")
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
}
else if (mesh.getSettingString("fill_perimeter_gaps") == "Everywhere")
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, 0, 0);
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
}
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
bool frontend_can_show_polygon_as_filled_polygon = false;
if (frontend_can_show_polygon_as_filled_polygon)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, extrusionWidth);
// 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, innermost_wall_extrusion_width);
}
}
}
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int totalLayers)
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
@@ -349,28 +439,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 +477,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));
}
@@ -420,14 +510,19 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
}
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;
@@ -472,110 +567,7 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
}
}
skin = results;
}
}
}
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;
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
+21 -16
Ver Arquivo
@@ -4,6 +4,7 @@
#include "MeshGroup.h"
#include "utils/polygonUtils.h"
#include "utils/NoCopy.h"
#include "utils/gettime.h"
#include "settings.h"
#include "sliceDataStorage.h"
@@ -12,8 +13,6 @@
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,7 +22,7 @@ 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;
@@ -91,9 +90,9 @@ private:
* Remove all bottom layers which are empty.
* \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, int layer_height, unsigned int total_layers);
/*!
* Generate the inset polygons which form the walls.
@@ -102,27 +101,36 @@ private:
*/
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Generate the wall reinforcement extra wall polygons and its infill area which form the reinforcement.
*
* Also redefines the infill area;
*
* \param storage 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 processWallReinforcement(SliceDataStorage& storage, 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 layer_nr The layer for which to generate the skin areas.
*/
void processSkins(SliceDataStorage& storage, unsigned int layer_nr);
void processSkinsAndInfill(SliceDataStorage& storage, 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 +140,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
+9
Ver Arquivo
@@ -64,6 +64,15 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
TimeKeeper time_keeper_total;
if (meshgroup->meshes.empty())
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
return true;
}
if (meshgroup->getSettingBoolean("wireframe_enabled"))
{
log("starting Neith Weaver...\n");
+1 -1
Ver Arquivo
@@ -37,7 +37,7 @@ public:
private:
FffPolygonGenerator polygon_generator;
FffGcodeWriter gcode_writer;
CommandSocket* command_socket;
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
bool first_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
+227
Ver Arquivo
@@ -0,0 +1,227 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
namespace cura {
void LayerPlanBuffer::flush()
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the very last layer
}
for (GCodePlanner& layer_plan : buffer)
{
layer_plan.writeGCode(gcode, getSettingBoolean("cool_lift_head"), layer_plan.getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
}
}
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
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
{ // set temperature of the first nozzle, turn other nozzles down
if (extruder_idx == extruder)
{
// extruder_plan.insertCommand(0, extruder, required_temp, true);
// the first used extruder should already be set to the required temp in the start gcode
}
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
+134
Ver Arquivo
@@ -0,0 +1,134 @@
#ifndef LAYER_PLAN_BUFFER_H
#define LAYER_PLAN_BUFFER_H
#include <list>
#include "settings.h"
#include "commandSocket.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "Preheat.h"
namespace cura
{
class LayerPlanBuffer : SettingsMessenger
{
CommandSocket* command_socket;
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, CommandSocket* command_socket, GCodeExport& gcode)
: SettingsMessenger(settings)
, command_socket(command_socket)
, 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, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
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
+6 -5
Ver Arquivo
@@ -20,10 +20,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,12 +37,15 @@ 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;
+4 -2
Ver Arquivo
@@ -13,6 +13,8 @@ class MergeInfillLines
// void merge(Point& from, Point& p0, Point& p1);
GCodeExport& gcode; //!< Where to write the combined line to
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
@@ -41,8 +43,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, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), 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.
+4 -7
Ver Arquivo
@@ -140,15 +140,12 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x
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;
}
+14 -4
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,7 +15,7 @@ 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;
@@ -28,9 +29,9 @@ public:
return extruder_count;
}
MeshGroup(SettingsBaseVirtual* settings_base)
MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
, extruder_count(-1)
{}
~MeshGroup()
@@ -44,7 +45,10 @@ public:
}
}
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
/*!
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
*/
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[extruder_nr])
{
@@ -53,6 +57,12 @@ public:
return extruders[extruder_nr];
}
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
std::vector<Mesh> meshes;
Point3 min() //! minimal corner of bounding box
+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
+33 -21
Ver Arquivo
@@ -16,20 +16,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], "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 +50,18 @@ 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")]
, 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 +81,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;
}
@@ -104,7 +113,7 @@ 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
@@ -113,7 +122,7 @@ void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLaye
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)
@@ -177,7 +186,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)
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, CommandSocket* command_socket)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
@@ -199,10 +208,10 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
{
wipe = false;
}
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
}
void PrimeTower::addToGcode3(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)
void PrimeTower::addToGcode3(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, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
@@ -221,14 +230,17 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
gcodeLayer.addLinesByOptimizer(pattern, &config);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (command_socket)
command_socket->sendPolygons(SupportType, 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));
}
}
void PrimeTower::addToGcode_OLD(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)
void PrimeTower::addToGcode_OLD(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, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
+8 -7
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,18 +45,18 @@ 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);
PrimeTower();
void 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);
void addToGcode_OLD(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);
void addToGcode3(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);
void 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, CommandSocket* command_socket);
void addToGcode_OLD(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, CommandSocket* command_socket);
void addToGcode3(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, CommandSocket* command_socket);
};
+2 -2
Ver Arquivo
@@ -4,8 +4,8 @@
namespace cura
{
enum class EPrintFeature : unsigned int
{
enum class EPrintFeature : unsigned int // unused!!
{ // TODO: use in gcodePathConfigs ?
OUTER_WALL,
INNER_WALLS,
INFILL,
+19 -8
Ver Arquivo
@@ -28,7 +28,6 @@ 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++)
@@ -56,7 +55,14 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, 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)
@@ -136,16 +142,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);
}
}
}
+2 -1
Ver Arquivo
@@ -8,6 +8,7 @@
#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:
+47 -20
Ver Arquivo
@@ -22,12 +22,20 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
processStartingCode(commandSocket);
int maxObjectHeight = wireFrame.layers.back().z1;
int maxObjectHeight;
if (wireFrame.layers.empty())
{
maxObjectHeight = 0;
}
else
{
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt(commandSocket);
unsigned int totalLayers = wireFrame.layers.size();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment("SKIRT");
@@ -71,7 +79,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
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, commandSocket); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_nr];
@@ -157,7 +165,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
gcode.writeFanCommand(0);
finalize(maxObjectHeight);
finalize();
if (commandSocket)
{
@@ -231,11 +239,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 = INT2MM(getSettingInMicrons("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 +484,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,13 +540,14 @@ 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 = INT2MM(getSettingInMicrons("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 = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
@@ -549,13 +561,24 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
}
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);
}
}
}
}
@@ -574,8 +597,12 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
{
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();
@@ -593,9 +620,9 @@ void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
}
void Wireframe2gcode::finalize(int maxObjectHeight)
void Wireframe2gcode::finalize()
{
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
}
+5 -3
Ver Arquivo
@@ -4,6 +4,8 @@
#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"
@@ -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;
@@ -73,7 +75,7 @@ public:
private:
WireFrame wireFrame;
WireFrame& wireFrame;
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
@@ -88,7 +90,7 @@ private:
/*!
* 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
+74 -68
Ver Arquivo
@@ -5,34 +5,11 @@
#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()
{
@@ -44,7 +21,7 @@ Polygons* Comb::getBoundaryOutside()
return boundary_outside;
}
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
@@ -52,7 +29,7 @@ Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_bounda
, offset_from_outlines_outside(travel_avoid_distance)
, 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)
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
{
@@ -64,7 +41,7 @@ Comb::~Comb()
delete boundary_outside;
}
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))
{
@@ -78,7 +55,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
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 +71,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,13 +94,15 @@ 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;
Point inside_middle_from;
Point inside_middle_to;
if (startInside && endInside)
{
@@ -131,27 +110,36 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
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;
inside_middle_from = middle_from_cp.location;
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else
else if(!startInside && !endInside)
{
if (!startInside && !endInside)
{
middle_from = startPoint;
middle_to = endPoint;
}
else if (!startInside && endInside)
{
middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
}
else if (startInside && !endInside)
{
middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
}
middle_from = startPoint;
inside_middle_from = startPoint;
middle_to = endPoint;
inside_middle_to = endPoint;
}
else if(!startInside && endInside)
{
middle_from = startPoint;
inside_middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else if(startInside && !endInside)
{
middle_to = endPoint;
inside_middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
inside_middle_from = middle_from_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
if (startInside)
@@ -159,7 +147,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
// 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, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
// throught air from boundary to boundary
@@ -178,14 +166,14 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
}
combPaths.emplace_back();
combPaths.back().throughAir = true;
if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) )
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
{ // via outside is a detour
combPaths.back().push_back(middle_from);
combPaths.back().push_back(middle_to);
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
else
{
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
}
else
@@ -193,8 +181,8 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
combPaths.emplace_back();
combPaths.back().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(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
if (endInside)
@@ -202,7 +190,7 @@ 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, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
return true;
@@ -219,18 +207,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 +278,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();
@@ -308,7 +310,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 +325,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 +350,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]))
{
+8 -26
Ver Arquivo
@@ -113,7 +113,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 +178,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,26 +205,20 @@ 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.
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.
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.
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)
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.
@@ -236,16 +230,14 @@ 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 +249,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
+102 -13
Ver Arquivo
@@ -6,18 +6,26 @@
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#endif
#include <string> // stoi
#ifdef _WIN32
#include <windows.h>
#endif
#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
#ifdef ARCUS
class CommandSocket::Private
{
public:
@@ -26,6 +34,8 @@ public:
, object_count(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
cura::proto::Layer* getLayerById(int id);
@@ -43,6 +53,11 @@ public:
// Number of sliced objects for this sliced object list
int sliced_objects;
// 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;
// Ids of the sliced objects
std::vector<int64_t> object_ids;
@@ -53,15 +68,21 @@ public:
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
};
#endif
CommandSocket::CommandSocket()
#ifdef ARCUS
: d(new Private)
#endif
{
#ifdef ARCUS
FffProcessor::getInstance()->setCommandSocket(this);
#endif
}
void CommandSocket::connect(const std::string& ip, int port)
{
#ifdef ARCUS
d->socket = new Arcus::Socket();
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
@@ -74,18 +95,27 @@ void CommandSocket::connect(const std::string& ip, int port)
d->socket->connect(ip, 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(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
{
//If there is an object to slice, do so.
if(d->objects_to_slice.size())
{
FffProcessor::getInstance()->resetFileNumber();
for(auto object : d->objects_to_slice)
{
FffProcessor::getInstance()->processMeshGroup(object.get());
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
d->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
sendGCodeLayer();
sendPrintTime();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//d->processor->processModel(d->object_to_slice.get());
//d->object_to_slice.reset();
@@ -128,19 +158,44 @@ void CommandSocket::connect(const std::string& ip, int port)
d->socket->clearError();
}
}
#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();
MeshGroup* meshgroup = d->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
meshgroup->createExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
}
for(auto object : list->objects())
{
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();
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();
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
@@ -155,8 +210,16 @@ 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]);
}
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());
@@ -166,13 +229,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
mesh.finish();
}
for(auto setting : list->settings())
{
object_to_slice->setSetting(setting.name(), setting.value());
}
d->object_count++;
object_to_slice->finalize();
meshgroup->finalize();
}
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
@@ -182,9 +240,11 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
#ifdef ARCUS
if(!d->current_sliced_object)
{
return;
@@ -193,10 +253,12 @@ void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
cura::proto::Layer* layer = d->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)
{
#ifdef ARCUS
if(!d->current_sliced_object)
return;
@@ -214,15 +276,18 @@ void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polyg
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);
message->set_amount(amount);
d->socket->sendMessage(message);
#endif
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
@@ -232,10 +297,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);
#endif
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -249,6 +316,7 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
void CommandSocket::beginSendSlicedObject()
{
#ifdef ARCUS
if(!d->sliced_object_list)
{
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
@@ -256,45 +324,64 @@ void CommandSocket::beginSendSlicedObject()
d->current_sliced_object = d->sliced_object_list->add_objects();
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
#endif
}
void CommandSocket::endSendSlicedObject()
{
#ifdef ARCUS
d->sliced_objects++;
d->current_layer_offset = d->current_layer_count;
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
std::cout << "current layer count" << d->current_layer_count << std::endl;
std::cout << "current layer offset" << d->current_layer_offset << std::endl;
if(d->sliced_objects >= d->object_count)
{
d->socket->sendMessage(d->sliced_object_list);
d->sliced_objects = 0;
d->current_layer_count = 0;
d->current_layer_offset = 0;
d->sliced_object_list.reset();
d->current_sliced_object = nullptr;
}
#endif
}
void CommandSocket::beginGCode()
{
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
#endif
}
void CommandSocket::sendGCodeLayer()
{
#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);
d->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);
#endif
}
#ifdef ARCUS
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
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; });
cura::proto::Layer* layer = nullptr;
@@ -306,9 +393,11 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
layer = current_sliced_object->add_layers();
layer->set_id(id);
current_layer_count++;
}
return layer;
}
#endif
}//namespace cura
+9 -3
Ver Arquivo
@@ -8,10 +8,12 @@
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura {
namespace cura
{
class CommandSocket
{
@@ -24,7 +26,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,6 +39,7 @@ public:
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
#endif
/*!
* Does nothing at the moment
@@ -74,9 +78,11 @@ public:
void sendGCodeLayer();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> d;
#endif
};
}//namespace cura
+301 -205
Ver Arquivo
@@ -1,6 +1,7 @@
/** 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"
@@ -8,19 +9,19 @@
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))
, commandSocket(nullptr)
, 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);
}
@@ -28,6 +29,11 @@ GCodeExport::~GCodeExport()
{
}
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
commandSocket = commandSocket_;
layer_nr = layer_nr_;
}
void GCodeExport::setOutputStream(std::ostream* stream)
{
output_stream = stream;
@@ -63,6 +69,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 +104,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,20 +116,21 @@ 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;
}
}
@@ -133,14 +138,10 @@ double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
double GCodeExport::getTotalFilamentUsed(int e)
{
if (e == current_extruder)
return extruder_attr[e].totalFilament + getExtrusionAmountMM3(e);
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
return extruder_attr[e].totalFilament;
}
double GCodeExport::getTotalPrintTime(EPrintFeature print_feature)
{
return total_print_time_per_feature[(unsigned int)print_feature];
}
double GCodeExport::getTotalPrintTime()
{
return totalPrintTime;
@@ -149,30 +150,34 @@ 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 << "\n";
}
void GCodeExport::writeTypeComment(const char* type)
@@ -184,6 +189,11 @@ void GCodeExport::writeLayerComment(int layer_nr)
*output_stream << ";LAYER:" << layer_nr << "\n";
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << "\n";
@@ -191,20 +201,24 @@ void GCodeExport::writeLine(const char* line)
void GCodeExport::resetExtrusionValue()
{
if (extrusion_amount != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
if (current_e_value != 0.0 && 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;
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;
estimateCalculator.addTime(timeAmount);
}
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
@@ -217,223 +231,306 @@ 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 = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
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 << "\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";
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\r\n";
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 << "\r\n";
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), 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 = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / extruder_attr[current_extruder].filament_area;
}
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) << "\n";
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 += (is_volumatric) ? prime_volume : prime_volume / extruder_attr[current_extruder].filament_area;
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
if (firmware_retract)
{ // note that BFB is handled differently
*output_stream << "G11\n";
//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 << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), 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 << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), 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)
{
current_e_value += prime_volume;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), 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)
*output_stream << "G0";
if (commandSocket)
{
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";
}
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)
*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";
// 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->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
}
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 << "\n";
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), current_e_value), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
{
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;
if (config->primeAmount > 0)
{
extrusion_amount += config->primeAmount;
return;
}
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
{
return;
}
if (config->distance <= 0)
{
return;
}
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].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 - 1
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extruder_attr[current_extruder].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)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
}
retractionPrimeSpeed = config->primeSpeed;
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
double retraction_e_amount = config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
if (firmware_retract)
{
*output_stream << "G10\n";
//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), current_e_value - retraction_e_amount), 25); // TODO: hardcoded values!
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), current_e_value), currentSpeed);
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
extruder_attr[current_extruder].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) << "\n";
}
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)
if (!extruder_attr[current_extruder].retraction_e_amount_current)
*output_stream << "M103\r\n";
isRetracted = true;
extruder_attr[current_extruder].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;
}
resetExtrusionValue();
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
double retraction_e_amount = extruder_attr[current_extruder].extruder_switch_retraction_distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0);
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
{
*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;
return;
}
isRetracted = true;
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (firmware_retract)
{
if (extruder_attr[current_extruder].retraction_e_amount_current)
{
return;
}
*output_stream << "G10 S1\n";
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << "\n";
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
}
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(); // should be called on the old extruder
int old_extruder = current_extruder;
current_extruder = new_extruder;
if (flavor == EGCodeFlavor::MACH3)
resetExtrusionValue();
isRetracted = true;
{
resetExtrusionValue(); // also zero the E value on the new extruder
}
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
{
*output_stream << "M135 T" << current_extruder << "\n";
}
else
{
*output_stream << "T" << current_extruder << "\n";
}
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.
@@ -494,11 +591,9 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
*output_stream << temperature << "\n";
}
void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* endCode)
void GCodeExport::finalize(double moveSpeed, 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)));
@@ -509,3 +604,4 @@ void GCodeExport::finalize(int maxObjectHeight, double moveSpeed, const char* en
}
}//namespace cura
+96 -55
Ver Arquivo
@@ -8,9 +8,10 @@
#include "settings.h"
#include "utils/intpoint.h"
#include "utils/NoCopy.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "PrintFeature.h"
#include "commandSocket.h"
namespace cura {
@@ -29,13 +30,13 @@ struct CoastingConfig
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;
double retraction_extrusion_window; //!< in mm
int retraction_count_max;
};
@@ -43,62 +44,82 @@ public:
class GCodePathConfig
{
private:
double speed; //!< movement speed
double speed_base; //!< movement speed (mm/s) specific to this print feature
double speed_current; //!< 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
public:
const char* name;
const char* name; //!< name of the feature type
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) {}
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
void setSpeed(double speed)
{
this->speed = speed;
}
void setLineWidth(int line_width)
/*!
* Initialize some of the member variables.
*
* Warning! setLayerHeight still has to be called before this object can be used.
*/
void init(double speed, int line_width, double flow)
{
speed_base = speed;
this->speed_current = speed;
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_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
* 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;
return speed_current;
}
int getLineWidth()
{
return line_width;
}
bool isTravelPath()
{
return line_width == 0;
}
private:
void calculateExtrusion()
{
@@ -108,7 +129,7 @@ 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
@@ -119,55 +140,68 @@ private:
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
double extruderSwitchRetraction;
int extruderSwitchRetractionSpeed;
int extruderSwitchPrimeSpeed;
double extruder_switch_retraction_distance; //<! extruder switch retraction distance in mm
int extruderSwitchRetractionSpeed; //!< extruder switch retraction speed in mm/s
int extruderSwitchPrimeSpeed; //!< prime speed of extruder switch in mm/s
double totalFilament; //!< total filament used per extruder in mm^3
int currentTemperature;
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)
, extruder_switch_retraction_distance(0.0)
, extruderSwitchRetractionSpeed(0)
, extruderSwitchPrimeSpeed(0)
, totalFilament(0)
, currentTemperature(0)
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
, last_retraction_prime_speed(1.0)
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
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
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
Point3 currentPosition;
Point3 startPosition;
double currentSpeed;
int zPos;
bool isRetracted;
bool isZHopped;
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];
TimeEstimateCalculator estimateCalculator;
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
CommandSocket* commandSocket; //!< for sending travel data
unsigned int layer_nr; //!< for sending travel data
public:
GCodeExport();
~GCodeExport();
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
Point getExtruderOffset(int id);
@@ -179,38 +213,41 @@ 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 getCurrentExtrudedVolume();
double getTotalFilamentUsed(int e);
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 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,6 +257,10 @@ 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);
@@ -246,7 +287,7 @@ public:
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].extruder_switch_retraction_distance = 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");
}
@@ -254,7 +295,7 @@ public:
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);
void finalize(double moveSpeed, const char* endCode);
};
+456 -206
Ver Arquivo
@@ -1,24 +1,39 @@
#include <cstring>
#include "gcodePlanner.h"
#include "pathOrderOptimizer.h"
#include "sliceDataStorage.h"
#include <cstring>
#include "debug.h" // debugging
#include "utils/polygonUtils.h"
#include "MergeInfillLines.h"
namespace cura {
TimeMaterialEstimates 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);
}
TimeMaterialEstimates& 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;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done && paths[paths.size()-1].flow == flow)
return &paths[paths.size()-1];
paths.push_back(GCodePath());
GCodePath* ret = &paths[paths.size()-1];
ret->retract = false;
ret->config = config;
ret->extruder = currentExtruder;
ret->done = false;
ret->flow = flow;
if (config != &travelConfig)
if (config != &storage.travel_config)
{
last_retraction_config = config->retraction_config;
}
@@ -27,28 +42,35 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float
void GCodePlanner::forceNewPathStart()
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0)
paths[paths.size()-1].done = true;
}
GCodePlanner::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)
: gcode(gcode), storage(storage)
, travelConfig(retraction_config_travel, "MOVE")
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, commandSocket(commandSocket)
, layer_nr(layer_nr)
, z(z)
, layer_thickness(layer_thickness)
, start_position(last_position)
, lastPosition(last_position)
, comb_boundary_inside(computeCombBoundaryInside())
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
{
lastPosition = gcode.getPositionXY();
travelConfig.setSpeed(travelSpeed);
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
extruder_plans.emplace_back(current_extruder);
comb = nullptr;
was_inside = true; // means it will try to get inside the comb boundary first
is_inside = true; // means it will try to get inside the comb boundary
last_retraction_config = &storage.retraction_config; // start with general config
setExtrudeSpeedFactor(1.0);
setTravelSpeedFactor(1.0);
extraTime = 0.0;
totalPrintTime = 0.0;
currentExtruder = gcode.getExtruderNr();
if (retraction_combing)
{
was_combing = true; // means it will try to get inside the comb boundary first
is_going_to_comb = true; // means it will try to get inside the comb boundary
comb = new Comb(storage, layer_nr, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
comb = new Comb(storage, layer_nr, comb_boundary_inside, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
}
else
comb = nullptr;
@@ -60,43 +82,90 @@ GCodePlanner::~GCodePlanner()
delete comb;
}
void GCodePlanner::setCombing(bool going_to_comb)
Polygons GCodePlanner::computeCombBoundaryInside()
{
is_going_to_comb = going_to_comb;
if (layer_nr < 0)
{ // when a raft is present
return storage.raftOutline.offset(MM2INT(0.1));
}
else
{
Polygons layer_walls;
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(layer_walls);
}
return layer_walls;
}
}
void GCodePlanner::setIsInside(bool _is_inside)
{
is_inside = _is_inside;
}
bool GCodePlanner::setExtruder(int extruder)
{
if (extruder == currentExtruder)
if (extruder == extruder_plans.back().extruder)
{
return false;
}
SettingsBase* train = storage.meshgroup->getExtruderTrain(currentExtruder);
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
addTravel((end_pos_absolute)? end_pos : lastPosition + end_pos);
currentExtruder = extruder; // the extruder switch
forceNewPathStart();
train = storage.meshgroup->getExtruderTrain(currentExtruder);
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
lastPosition = (start_pos_absolute)? start_pos : lastPosition + start_pos;
{ // handle end position of the prev extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
if (!end_pos_absolute)
{
end_pos += lastPosition;
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
end_pos += extruder_offset; // absolute end pos is given as a head position
}
addTravel(end_pos); // + extruder_offset cause it
}
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
extruder_plans.back().extruder = extruder;
}
else
{
extruder_plans.emplace_back(extruder);
}
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
{ // handle starting pos of the new extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
if (!start_pos_absolute)
{
start_pos += lastPosition;
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
start_pos += extruder_offset; // absolute start pos is given as a head position
}
lastPosition = start_pos;
}
return true;
}
void GCodePlanner::moveInsideCombBoundary(int distance)
{
if (!comb) return;
Point p = lastPosition;
if (comb->moveInsideBoundary(&p, distance))
int max_dist = MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
// this function is to be used to move from the boudary of a part to inside the part
Point p = lastPosition; // copy, since we are going to move p
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist) != NO_INDEX)
{
//Move inside again, so we move out of tight 90deg corners
comb->moveInsideBoundary(&p, distance);
if (comb->inside(p))
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
if (comb_boundary_inside.inside(p))
{
addTravel_simple(p);
//Make sure the that any retraction happens after this move, not before it by starting a new move path.
@@ -108,11 +177,14 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
void GCodePlanner::addTravel(Point p)
{
GCodePath* path = nullptr;
if (comb != nullptr && lastPosition != Point(0,0))
bool combed = false;
if (comb != nullptr && lastPosition != no_point)
{
CombPaths combPaths;
if (comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb))
combed = comb->calc(lastPosition, p, combPaths, was_inside, is_inside, last_retraction_config->retraction_min_travel_distance);
if (combed)
{
bool retract = combPaths.size() > 1;
{ // check whether we want to retract
@@ -129,7 +201,7 @@ void GCodePlanner::addTravel(Point p)
if (retract && last_retraction_config->zHop > 0)
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
path = getLatestPathWithConfig(&travelConfig);
path = getLatestPathWithConfig(&storage.travel_config);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
@@ -143,7 +215,7 @@ void GCodePlanner::addTravel(Point p)
{
continue;
}
path = getLatestPathWithConfig(&travelConfig);
path = getLatestPathWithConfig(&storage.travel_config);
path->retract = retract;
for (Point& combPoint : combPath)
{
@@ -153,25 +225,32 @@ void GCodePlanner::addTravel(Point p)
}
}
}
else
{
path = getLatestPathWithConfig(&travelConfig);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
}
}
was_combing = is_going_to_comb;
}
if (!combed) {
// no combing? always retract!
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
if (was_inside)
{
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
}
path = getLatestPathWithConfig(&storage.travel_config);
path->retract = true;
}
}
addTravel_simple(p, path);
was_inside = is_inside;
}
void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
path = getLatestPathWithConfig(&travelConfig);
path = getLatestPathWithConfig(&storage.travel_config);
}
path->points.push_back(p);
lastPosition = p;
@@ -250,169 +329,280 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, do
if (minExtrudeTime < 1)
minExtrudeTime = 1;
double factor = extrudeTime / minExtrudeTime;
for(unsigned int n=0; n<paths.size(); n++)
for(ExtruderPlan& extr_plan : extruder_plans)
{
GCodePath* path = &paths[n];
if (path->getExtrusionMM3perMM() == 0)
continue;
double speed = path->config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path->config->getSpeed();
for (GCodePath& path : extr_plan.paths)
{
if (path.isTravelPath())
continue;
double speed = path.config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path.config->getSpeed();
}
}
//Only slow down with the minimal time if that will be slower then a factor already set. First layer slowdown also sets the speed factor.
if (factor < getExtrudeSpeedFactor())
//Only slow down for the minimal time if that will be slower.
assert(getExtrudeSpeedFactor() == 1.0); // The extrude speed factor is assumed not to be changed yet
if (factor < 1.0)
{
setExtrudeSpeedFactor(factor);
else
factor = getExtrudeSpeedFactor();
if (minTime - (extrudeTime / factor) - travelTime > 0.1)
{
this->extraTime = minTime - (extrudeTime / factor) - travelTime;
}
this->totalPrintTime = (extrudeTime / factor) + travelTime;
else
{
factor = 1.0;
}
double inv_factor = 1.0 / factor; // cause multiplication is faster than division
// Adjust stored naive time estimates
for(ExtruderPlan& extr_plan : extruder_plans)
{
extr_plan.estimates.extrude_time *= inv_factor;
for (GCodePath& path : extr_plan.paths)
{
path.estimates.extrude_time *= inv_factor;
}
}
if (minTime - (extrudeTime * inv_factor) - travelTime > 0.1)
{
this->extraTime = minTime - (extrudeTime * inv_factor) - travelTime;
}
this->totalPrintTime = (extrudeTime * inv_factor) + travelTime;
}else{
this->totalPrintTime = totalTime;
}
}
void GCodePlanner::getNaiveTimeEstimates(double& travelTime, double& extrudeTime)
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
{
travelTime = 0.0;
extrudeTime = 0.0;
Point p0 = gcode.getPositionXY();
for(unsigned int n=0; n<paths.size(); n++)
TimeMaterialEstimates ret;
Point p0 = start_position;
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
for(ExtruderPlan& extr_plan : extruder_plans)
{
GCodePath* path = &paths[n];
for(unsigned int i=0; i<path->points.size(); i++)
for (GCodePath& path : extr_plan.paths)
{
double thisTime = vSizeMM(p0 - path->points[i]) / path->config->getSpeed();
if (path->getExtrusionMM3perMM() != 0)
extrudeTime += thisTime;
else
travelTime += thisTime;
p0 = path->points[i];
}
}
}
void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
{
GCodePathConfig* last_extrusion_config = nullptr;
int extruder = gcode.getExtruderNr();
for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++)
{
GCodePath& path = paths[path_idx];
if (extruder != path.extruder)
{
extruder = path.extruder;
gcode.switchExtruder(extruder);
}else if (path.retract)
{
writeRetraction(path_idx);
}
if (path.config != &travelConfig && last_extrusion_config != path.config)
{
gcode.writeTypeComment(path.config->name);
last_extrusion_config = path.config;
}
double speed = path.config->getSpeed();
if (path.getExtrusionMM3perMM() != 0)// Only apply the extrudeSpeed to extrusion moves
speed *= getExtrudeSpeedFactor();
else
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
if (MergeInfillLines(gcode, paths, travelConfig, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
{ // !! has effect on path_idx !!
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
continue;
}
if (path.config == &travelConfig)
{ // early comp for travel paths, which are handled more simply
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
bool is_extrusion_path = false;
double* path_time_estimate;
double& material_estimate = path.estimates.material;
if (!path.isTravelPath())
{
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
is_extrusion_path = true;
path_time_estimate = &path.estimates.extrude_time;
}
continue;
}
bool spiralize = path.config->spiralize;
if (spiralize)
{
//Check if we are the last spiralize path in the list, if not, do not spiralize.
for(unsigned int m=path_idx+1; m<paths.size(); m++)
else
{
if (paths[m].config->spiralize)
spiralize = false;
}
}
if (!spiralize) // normal (extrusion) move (with coasting
{
CoastingConfig& coasting_config = storage.coasting_config[extruder];
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
coasting = writePathWithCoasting(path_idx, layerThickness
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
}
if (! coasting) // not same as 'else', cause we might have changed coasting in the line above...
{ // normal path to gcode algorithm
if ( // change |||||| to /\/\/\/\/ ...
false &&
path_idx + 2 < paths.size() // has a next move
&& paths[path_idx+1].points.size() == 1 // is single extruded line
&& paths[path_idx+1].config != &travelConfig // next move is extrusion
&& paths[path_idx+2].config == &travelConfig // next next move is travel
&& shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by
&& shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
)
if (path.retract)
{
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
path_idx += 2;
path_time_estimate = &path.estimates.retracted_travel_time;
}
else
{
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
path_time_estimate = &path.estimates.unretracted_travel_time;
}
if (path.retract != was_retracted)
{ // handle retraction times
double retract_unretract_time;
RetractionConfig& retraction_config = *path.config->retraction_config;
if (path.retract)
{
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
retract_unretract_time = retraction_config.distance / retraction_config.speed;
}
else
{
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
}
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
}
}
for(Point& p1 : path.points)
{
double length = vSizeMM(p0 - p1);
if (is_extrusion_path)
{
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
}
double thisTime = length / path.config->getSpeed();
*path_time_estimate += thisTime;
p0 = p1;
}
extr_plan.estimates += path.estimates;
}
ret += extr_plan.estimates;
}
return ret;
}
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
{
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
fan_speed = fsml.cool_fan_speed_min;
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
if (totalLayerTime < fsml.cool_min_layer_time)
{
fan_speed = fsml.cool_fan_speed_max;
}
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
fan_speed = fsml.cool_fan_speed_max - (fsml.cool_fan_speed_max-fsml.cool_fan_speed_min) * (totalLayerTime - fsml.cool_min_layer_time) / (fsml.cool_min_layer_time_fan_speed_max - fsml.cool_min_layer_time);
}
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
fan_speed = fan_speed * layer_nr / fsml.cool_fan_full_layer;
}
}
void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness)
{
completeConfigs();
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
gcode.writeLayerComment(layer_nr);
gcode.setZ(z);
gcode.writeFanCommand(fan_speed);
GCodePathConfig* last_extrusion_config = nullptr;
int extruder = gcode.getExtruderNr();
for(unsigned int extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++)
{
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
if (extruder != extruder_plan.extruder)
{
extruder = extruder_plan.extruder;
gcode.switchExtruder(extruder);
}
std::vector<GCodePath>& paths = extruder_plan.paths;
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
return a.path_idx < b.path_idx;
} );
for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& path = paths[path_idx];
if (path.retract)
{
writeRetraction(gcode, extruder_plan_idx, path_idx);
}
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
{
gcode.writeTypeComment(path.config->name);
last_extrusion_config = path.config;
}
double speed = path.config->getSpeed();
if (path.isTravelPath())// Only apply the extrudeSpeed to extrusion moves
speed *= getTravelSpeedFactor();
else
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
{ // !! has effect on path_idx !!
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
continue;
}
if (path.config == &storage.travel_config)
{ // early comp for travel paths, which are handled more simply
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
continue;
}
bool spiralize = path.config->spiralize;
if (spiralize)
{
//Check if we are the last spiralize path in the list, if not, do not spiralize.
for(unsigned int m=path_idx+1; m<paths.size(); m++)
{
if (paths[m].config->spiralize)
spiralize = false;
}
}
if (!spiralize) // normal (extrusion) move (with coasting
{
CoastingConfig& coasting_config = storage.coasting_config[extruder];
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
}
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
{ // normal path to gcode algorithm
if ( // change |||||| to /\/\/\/\/ ...
false &&
path_idx + 2 < paths.size() // has a next move
&& paths[path_idx+1].points.size() == 1 // is single extruded line
&& paths[path_idx+1].config != &storage.travel_config // next move is extrusion
&& paths[path_idx+2].config == &storage.travel_config // next next move is travel
&& shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by
&& shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
)
{
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
path_idx += 2;
}
else
{
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
}
}
}
else
{ // SPIRALIZE
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
float totalLength = 0.0;
int z = gcode.getPositionZ();
Point p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path.points.size(); i++)
{
Point p1 = path.points[i];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
}
else
{ // SPIRALIZE
//If we need to spiralize then raise the head slowly by 1 layer as this path progresses.
float totalLength = 0.0;
int z = gcode.getPositionZ();
Point p0 = gcode.getPositionXY();
for(unsigned int i=0; i<path.points.size(); i++)
{
Point p1 = path.points[i];
totalLength += vSizeMM(p0 - p1);
p0 = p1;
}
float length = 0.0;
p0 = gcode.getPositionXY();
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
Point p1 = path.points[point_idx];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
float length = 0.0;
p0 = gcode.getPositionXY();
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
Point p1 = path.points[point_idx];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
}
extruder_plan.handleAllRemainingInserts(gcode);
}
gcode.updateTotalPrintTime();
if (liftHeadIfNeeded && extraTime > 0.0)
{
@@ -420,36 +610,85 @@ void GCodePlanner::writeGCode(bool liftHeadIfNeeded, int layerThickness)
if (last_extrusion_config)
{
bool extruder_switch_retract = false;// TODO: check whether we should do a retractoin_extruderSwitch; is the next path with a different extruder?
writeRetraction(extruder_switch_retract, last_extrusion_config->retraction_config);
writeRetraction(gcode, extruder_switch_retract, last_extrusion_config->retraction_config);
}
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), travelConfig.getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), travelConfig.getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY(), storage.travel_config.getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config.getSpeed(), 0); // TODO: is this safe?! wouldn't the head move into the sides then?!
gcode.writeDelay(extraTime);
}
}
void GCodePlanner::writeRetraction(unsigned int path_idx_travel_after)
void GCodePlanner::completeConfigs()
{
if (makeRetractSwitchRetract(path_idx_travel_after))
storage.support_config.setLayerHeight(layer_thickness);
storage.support_roof_config.setLayerHeight(layer_thickness);
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setLayerHeight(layer_thickness);
mesh.insetX_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
mesh.skin_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setLayerHeight(layer_thickness);
}
}
storage.primeTower.setConfigs(storage.meshgroup, layer_thickness);
processInitialLayersSpeedup();
}
void GCodePlanner::processInitialLayersSpeedup()
{
double initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
if (static_cast<int>(layer_nr) < initial_speedup_layers)
{
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
}
}
}
}
void GCodePlanner::writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after)
{
if (makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx_travel_after))
{
gcode.writeRetraction_extruderSwitch();
}
else
{
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
RetractionConfig* extrusion_retraction_config = nullptr;
for(int extrusion_path_idx = int(path_idx_travel_after) - 1; extrusion_path_idx >= 0; extrusion_path_idx--)
{ // backtrack to find the last extrusion path
if (paths[extrusion_path_idx].config != &travelConfig)
if (paths[extrusion_path_idx].config != &storage.travel_config)
{
extrusion_retraction_config = paths[extrusion_path_idx].config->retraction_config;
break;
}
}
writeRetraction(false, extrusion_retraction_config);
writeRetraction(gcode, false, extrusion_retraction_config);
}
}
void GCodePlanner::writeRetraction(bool extruder_switch_retract, RetractionConfig* retraction_config)
void GCodePlanner::writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config)
{
if (extruder_switch_retract)
{
@@ -463,35 +702,46 @@ void GCodePlanner::writeRetraction(bool extruder_switch_retract, RetractionConfi
}
else
{
gcode.writeRetraction(travelConfig.retraction_config);
gcode.writeRetraction(storage.travel_config.retraction_config);
}
}
}
bool GCodePlanner::makeRetractSwitchRetract(unsigned int path_idx)
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
{
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
for (unsigned int path_idx2 = path_idx + 1; path_idx2 < paths.size(); path_idx2++)
{
if (paths[path_idx2].getExtrusionMM3perMM() > 0)
{
if (paths[path_idx2].extruder != gcode.getExtruderNr())
{
return true;
}
{
return false;
}
}
return false;
if (extruder_plans.size() <= extruder_plan_idx+1)
{
return false; // TODO: check first extruder of the next layer! (generally only on the last layer of the second extruder)
}
if (extruder_plans[extruder_plan_idx + 1].extruder != extruder_plans[extruder_plan_idx].extruder)
{
return true;
}
else
{
return false;
}
}
bool GCodePlanner::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)
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, 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)
{
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
GCodePath& path = paths[path_idx];
if (path_idx + 1 >= paths.size()
||
! (path.getExtrusionMM3perMM() > 0.0 && paths[path_idx + 1].config->getExtrusionMM3perMM() == 0.0)
! (!path.isTravelPath() && paths[path_idx + 1].config->isTravelPath())
||
path.points.size() < 2
)
@@ -503,15 +753,15 @@ bool GCodePlanner::writePathWithCoasting(unsigned int path_idx, int64_t layerThi
if (path_next.retract)
{
if (coasting_volume_retract <= 0) { return false; }
return writePathWithCoasting(path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(path_idx));
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx));
}
else
{
if (coasting_volume_move <= 0) { return false; }
return writePathWithCoasting(path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
}
}
bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
{
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
@@ -599,7 +849,7 @@ bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next,
if (path_next.retract)
{
writeRetraction(extruder_switch_retract, path.config->retraction_config);
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
}
for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++)
@@ -607,7 +857,7 @@ bool GCodePlanner::writePathWithCoasting(GCodePath& path, GCodePath& path_next,
gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0);
}
gcode.setLastCoastedAmountMM3(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
gcode.addLastCoastedVolume(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
return true;
}
+315 -33
Ver Arquivo
@@ -8,6 +8,8 @@
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
namespace cura
@@ -15,46 +17,251 @@ 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; //!< in seconds
double unretracted_travel_time; //!< in seconds
double retracted_travel_time; //!< in seconds
double material; //!< in mm^3
public:
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)
{
}
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;
}
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);
}
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);
double getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
double getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
double getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
double getExtrudeTime() const
{
return extrude_time;
}
double getMaterial() const
{
return material;
}
};
class GCodePath
{
public:
GCodePathConfig* config; //!< The configuration settings of the path.
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.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
bool isTravelPath()
{
return config->isTravelPath();
}
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
};
class ExtruderPlan
{
public:
std::vector<GCodePath> paths;
std::list<NozzleTempInsert> inserts;
int extruder; //!< The extruder used for this paths in the current plan.
double required_temp;
TimeMaterialEstimates estimates;
ExtruderPlan(int extruder)
: extruder(extruder)
, required_temp(-1)
{
}
/*!
* Add a new Insert, constructed with the given arguments
*/
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
*/
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
*/
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.
*/
class GCodePlanner
class GCodePlanner : public NoCopy
{
friend class LayerPlanBuffer;
private:
GCodeExport& gcode;
SliceDataStorage& storage;
Point lastPosition;
std::vector<GCodePath> paths;
CommandSocket* commandSocket;
bool was_combing;
bool is_going_to_comb;
int layer_nr;
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;
@@ -82,26 +289,53 @@ 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
*/
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(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, 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.
* \return the comb_boundary_inside
*/
Polygons computeCombBoundaryInside();
public:
int getLayerNr()
{
return layer_nr;
}
Point getLastPosition()
{
return lastPosition;
}
/*!
* 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 +351,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.
@@ -159,15 +389,53 @@ public:
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, 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);
/*!
* Write the planned paths to gcode
*
* \param gcode The gcode to write the planned paths to
*/
void writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness);
/*!
* 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_move The volume otherwise leaked during a normal move.
@@ -178,7 +446,7 @@ public:
* \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.
*/
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);
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, 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.
@@ -187,6 +455,7 @@ public:
*
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
*
* \param gcode The gcode to write the planned paths to
* \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.
@@ -196,23 +465,36 @@ public:
* \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);
bool writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
/*!
* 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
+2 -69
Ver Arquivo
@@ -138,24 +138,6 @@ void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, i
}
}
/*!
* 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)
{
if (lineSpacing == 0) return;
@@ -220,55 +202,6 @@ void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extr
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
* (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 [connect_zigzags])
*
*
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
*
* start boundary from even scanline! :D
*
*
* _____
* | | | ,
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
*/
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
{
// if (in_outline.size() == 0) return;
@@ -303,7 +236,7 @@ void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& resu
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 firstBoundarySegmentEndsInEven = false;
bool isEvenScanSegment = false;
@@ -435,7 +368,7 @@ void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& re
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool firstBoundarySegmentEndsInEven = true;
bool isEvenScanSegment = false;
+114 -39
Ver Arquivo
@@ -5,47 +5,122 @@
#include "utils/polygon.h"
#include "settings.h"
namespace cura {
class Infill
namespace cura
{
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;
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)
: pattern(pattern)
, in_outline(in_outline)
, outlineOffset(outlineOffset)
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
, extrusion_width(extrusion_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connect_zigzags(connect_zigzags)
, use_endPieces(use_endPieces)
class Infill
{
}
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
};
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);
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;
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)
: pattern(pattern)
, in_outline(in_outline)
, outlineOffset(outlineOffset)
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
, extrusion_width(extrusion_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connect_zigzags(connect_zigzags)
, use_endPieces(use_endPieces)
{
}
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
};
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);
/*!
* 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 generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
/*!
* 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
* (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 [connect_zigzags])
*
*
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
*
* start boundary from even scanline! :D
*
*
* _____
* | | | ,
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
*/
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);
}//namespace cura
#endif//INFILL_H
+29
Ver Arquivo
@@ -71,4 +71,33 @@ void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int l
}
}
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
{
// optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[0].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
}
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() > 0)
{
for(int i=1; i<insetCount; i++)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(Polygons());
PolygonUtils::offsetExtrusionWidth(reinforcement_wall.wall_reinforcement_axtra_walls[i-1], true, line_width_x, reinforcement_wall.wall_reinforcement_axtra_walls[i], &part->perimeterGaps, avoidOverlappingPerimeters);
//Finally optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[i].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[i].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
break;
}
}
}
}
}//namespace cura
+10
Ver Arquivo
@@ -36,6 +36,16 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
*/
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the wall reinforcement extra walls for a single layer part.
*
* \param part The part for which to generate the extra walls.
* \param line_width_x line width of the walls
* \param insetCount The number of insets to to generate
* \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 generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+24 -17
Ver Arquivo
@@ -116,11 +116,11 @@ 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);
SettingsBase* last_settings_object = FffProcessor::getInstance();
for(int argn = 2; argn < argc; argn++)
{
@@ -134,20 +134,21 @@ 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
meshgroup->getExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
}
//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_settings_object = meshgroup;
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
@@ -177,8 +178,8 @@ 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;
break;
case 'l':
argn++;
@@ -186,13 +187,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 +205,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.
@@ -240,7 +241,7 @@ void slice(int argc, char **argv)
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_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
meshgroup->createExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // create new extruder train objects or use already existing ones
}
@@ -249,12 +250,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 +264,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 +319,11 @@ int main(int argc, char **argv)
{
slice(argc, argv);
}
else if (stringcasecompare(argv[1], "help") == 0)
{
print_usage();
exit(0);
}
else
{
cura::logError("Unknown command: %s\n", argv[1]);
+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);
}
}
+126 -94
Ver Arquivo
@@ -4,6 +4,7 @@
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <algorithm> // find_if
#include "utils/logoutput.h"
@@ -53,6 +54,16 @@ SettingConfig::SettingConfig(std::string key, std::string label, SettingContaine
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
void SettingContainer::debugOutputAllSettings()
{
std::cerr << "CATEGORY: " << key << std::endl;
for (SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
bool SettingRegistry::settingExists(std::string key) const
{
return settings.find(key) != settings.end();
@@ -184,120 +195,64 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
{
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)
std::string cat_name = category_iterator->name.GetString();
std::list<SettingContainer>::iterator category_found = std::find_if(categories.begin(), categories.end(), [&cat_name](SettingContainer& cat) { return cat.getKey().compare(cat_name) == 0; });
if (category_found != categories.end())
{ // category is already present; add settings to category
SettingContainer* category = &*category_found;
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);
}
}
else
{
_addSettingToContainer(category, setting_iterator, warn_duplicates);
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
{
continue;
}
categories.emplace_back(cat_name, 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"))
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)
{
SettingConfig* conf = getSettingConfig(override_iterator->name.GetString());
_addSettingToContainer(conf, override_iterator, false);
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
_loadSettingValues(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();
}
const rapidjson::Value& data = json_object_it->value;
/// 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("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"];
@@ -306,7 +261,84 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson
_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);
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
}
void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
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 (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());
}
}//namespace cura
/// 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(config, setting_iterator, warn_duplicates, add_to_settings);
}
}
}
}//namespace cura
+35 -1
Ver Arquivo
@@ -5,6 +5,7 @@
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "utils/NoCopy.h"
#include "rapidjson/document.h"
@@ -52,6 +53,8 @@ public:
else
return nullptr;
}
void debugOutputAllSettings();
};
/*!
@@ -112,6 +115,15 @@ public:
{
return unit;
}
void debugOutputAllSettings()
{
std::cerr << key << std::endl;
for (SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
};
/*!
@@ -160,9 +172,21 @@ public:
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename);
void debugOutputAllSettings()
{
for (SettingContainer& cat : categories)
{
cat.debugOutputAllSettings();
}
}
private:
std::string toString(rapidjson::Type type);
/*!
* \param type type to convert to string
* \return human readable version of json type
*/
static std::string toString(rapidjson::Type type);
/*!
* Load a json document.
*
@@ -181,10 +205,20 @@ private:
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates);
/*!
* Get the string from a json value (generally the default value field of a setting)
* \param dflt The value to convert to string
* \param setting_name The name of the setting (in case we need to display an error message)
* \return The string
*/
static std::string toString(const rapidjson::Value& dflt, std::string setting_name = "?");
/*!
* \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);
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
};
}//namespace cura
+98 -3
Ver Arquivo
@@ -14,6 +14,26 @@ namespace cura
#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::REPRAP:
default:
return "RepRap";
}
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
@@ -92,7 +112,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
if (!machine_extruder_trains)
{
logWarning("Error: no machine_extruder_trains category found in JSON!\n");
// no machine_extruder_trains setting present; just use defaults for each train..
return;
}
@@ -100,7 +120,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
if (!train)
{
logError("Not enough extruder trains specified in JSON: %i\n", extruder_nr);
// not enough machine_extruder_trains settings present; just use defaults for this train..
return;
}
@@ -180,6 +200,64 @@ double SettingsBaseVirtual::getSettingInSeconds(std::string key)
return std::max(0.0, atof(value.c_str()));
}
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
{
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)
{
std::string value = getSettingString(key);
@@ -229,7 +307,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "touching_buildplate")
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
@@ -258,5 +336,22 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
return ESurfaceMode::NORMAL;
}
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "nowhere")
{
return FillPerimeterGapMode::NOWHERE;
}
if (value == "everywhere")
{
return FillPerimeterGapMode::EVERYWHERE;
}
if (value == "skin")
{
return FillPerimeterGapMode::SKIN;
}
return FillPerimeterGapMode::NOWHERE;
}
}//namespace cura
+19
Ver Arquivo
@@ -8,6 +8,8 @@
#include "utils/floatpoint.h"
#include "FlowTempGraph.h"
namespace cura
{
@@ -75,6 +77,11 @@ enum class EGCodeFlavor
REPRAP_VOLUMATRIC = 5,
};
/*!
* 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 +131,13 @@ enum class ESurfaceMode
BOTH
};
enum class FillPerimeterGapMode
{
NOWHERE,
EVERYWHERE,
SKIN
};
#define MAX_EXTRUDERS 16
//Maximum number of infill layers that can be combined into a single infill extrusion area.
@@ -165,12 +179,17 @@ public:
double getSettingInPercentage(std::string key);
double getSettingInSeconds(std::string key);
FlowTempGraph getSettingAsFlowTempGraph(std::string key);
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key);
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);
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
};
/*!
+104 -42
Ver Arquivo
@@ -2,15 +2,15 @@
#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& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
@@ -20,39 +20,76 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
}
}
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, 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 = storage.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](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))
result.add(part2.insets.back());
}
return result;
};
if (no_small_gaps_heuristic)
{
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
{
downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
}
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
{
upskin = upskin.difference(getInsidePolygons(storage.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 > 0 && downSkinCount > 0)
{
if (part.boundaryBox.hit(part2.boundaryBox))
upskin = upskin.difference(part2.insets.back());
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.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())
@@ -97,12 +134,16 @@ 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& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = storage.layers[layerNr];
for(SliceLayerPart& part : layer.parts)
{
if (int(part.insets.size()) < wall_line_count)
{
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
@@ -121,31 +162,52 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
}
}
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount)
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
{
SliceLayer* layer = &storage.layers[layerNr];
for(int n=1; n<amount; n++)
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(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.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 = storage.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 = storage.layers.size() - 1 - storage.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 = &storage.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 = &storage.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[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);
}
}
part.infill_area.push_back(result);
}
part.infill_area.push_back(result);
}
}
}
+25 -5
Ver Arquivo
@@ -26,11 +26,14 @@ void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusion
* \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 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
* \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 generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generate the skin areas (outlines)
@@ -40,8 +43,10 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
* \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& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic);
/*!
* Generate the skin insets.
@@ -56,14 +61,29 @@ void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidt
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generate Infill
* Generate Infill by offsetting from the last wall.
*
* The walls should already be generated.
*
* \param layerNr The index of the layer for which to generate the infill
* \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 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& storage, int extrusionWidth, 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 storage The mesh to combine the infill layers of.
* \param amount The number of layers to combine.
*/
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount);
}//namespace cura
+214 -18
Ver Arquivo
@@ -1,5 +1,7 @@
#include "sliceDataStorage.h"
#include "FffProcessor.h" //To create a mesh group with if none is provided.
namespace cura
{
@@ -25,33 +27,227 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
}
}
Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only)
Polygons SliceLayer::getSecondOrInnermostWalls()
{
Polygons total;
for (SliceMeshStorage& mesh : meshes)
Polygons ret;
getSecondOrInnermostWalls(ret);
return ret;
}
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
{
for (SliceLayerPart& part : parts)
{
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));
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(part.insets[1]);
continue;
}
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
// but we'll also take the inner wall if the 2nd doesn't exist
if (part.insets.size() == 1) {
layer_walls.add(part.insets[0]);
continue;
}
total.add(primeTower.ground_poly);
// 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;
}
return total;
}
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(&retraction_config, "MOVE"),
skirt_config(initializeSkirtConfigs()),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], "SUPPORT"),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN"),
max_object_height_second_to_last_extruder(-1)
{
}
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
{
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 (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));
}
}
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)
{
if (layer_nr < 0)
{ // when processing raft
if (include_helper_parts)
{
return raftOutline;
}
else
{
return Polygons();
}
}
else
{
Polygons total;
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(total);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
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_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_extruder_nr")] = true;
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
return ret;
}
+89 -15
Ver Arquivo
@@ -4,6 +4,7 @@
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "utils/NoCopy.h"
#include "mesh.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
@@ -23,6 +24,19 @@ public:
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.
};
/*!
* A ReinforcementWall is like an insulated wall behind the outer walls.
* It consists of an area with (generally more dense) infill and perimeters on the inside.
* On the outside it has the outer walls, or the inner walls of another ReinforcementWall.
*/
class ReinforcementWall
{
public:
Polygons wall_reinforcement_area; //!< The infill of the reinforced wall
std::vector<Polygons> wall_reinforcement_axtra_walls; //!< The extra walls on the inside of the reinforcement infill
};
/*!
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
@@ -36,6 +50,7 @@ public:
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.
std::vector<ReinforcementWall> reinforcement_walls; //!< The reinforcement walls for this part. Order: from outter to inner reinforcement wall.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
@@ -51,8 +66,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)
/*!
* 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);
/*!
* 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);
/*!
* 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();
/*!
* 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);
};
/******************/
@@ -89,9 +131,10 @@ public:
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
GCodePathConfig wall_reinforcement_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, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
{
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
@@ -99,7 +142,7 @@ public:
}
};
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.)
@@ -110,9 +153,14 @@ public:
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)
GCodePathConfig travel_config; //!< The config used for travel moves (only the speed and retraction config are 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;
@@ -144,17 +192,18 @@ public:
}
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()
{
}
/*!
* \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 +212,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);
/*!
* 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);
/*!
* 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
+28 -25
Ver Arquivo
@@ -41,11 +41,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
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, commandSocket);
if (mesh.getSettingBoolean("support_roof_enable"))
{
@@ -79,43 +80,45 @@ 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, CommandSocket* commandSocket)
{
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 supportZDistance = mesh.getSettingInMicrons("support_z_distance");
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 = mesh.getSettingInMicrons("layer_height");
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
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");
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");
// derived settings:
@@ -160,7 +163,7 @@ 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);
bool still_in_upper_empty_layers = true;
@@ -173,7 +176,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
Polygons overhang;
{
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
Polygons supportLayer_supportee = object->layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
@@ -249,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
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(), commandSocket);
}
// do stuff for when support on buildplate only
+2 -2
Ver Arquivo
@@ -26,11 +26,11 @@ 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, CommandSocket* commandSocket);
+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];
+12 -4
Ver Arquivo
@@ -7,10 +7,10 @@
namespace cura
{
/**
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
Some of this code has been addapted from the Marlin sources.
*/
/*!
* The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
* Some of this code has been adapted from the Marlin sources.
*/
class TimeEstimateCalculator
{
@@ -54,6 +54,8 @@ public:
};
private:
double extra_time;
Position previous_feedrate;
double previous_nominal_feedrate;
@@ -61,8 +63,14 @@ private:
std::vector<Block> blocks;
public:
TimeEstimateCalculator()
: extra_time(0.0)
{
}
void setPosition(Position newPos);
void plan(Position newPos, double feedRate);
void addTime(double time);
void reset();
double calculate();
+18 -6
Ver Arquivo
@@ -18,7 +18,7 @@ public:
Point min, max;
AABB()
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
}
AABB(Point&min, Point& max)
@@ -26,7 +26,7 @@ public:
{
}
AABB(Polygons& polys)
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
calculate(polys);
}
@@ -39,10 +39,7 @@ public:
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
include(polys[i][j]);
}
}
}
@@ -55,6 +52,21 @@ public:
if (min.Y > other.max.Y) return false;
return true;
}
/*!
* \brief Includes the specified point in the bounding box.
*
* The bounding box is expanded if the point is not within the bounding box.
*
* \param point The point to include in the bounding box.
*/
void include(Point point)
{
min.X = std::min(min.X,point.X);
min.Y = std::min(min.Y,point.Y);
max.X = std::max(max.X,point.X);
max.Y = std::max(max.Y,point.Y);
}
};
/*!
+11 -7
Ver Arquivo
@@ -1,9 +1,9 @@
#ifndef UTILS_NO_COPY_H
#define UTILS_NO_COPY_H
/**
Util class to base other objects off which should never be copied.
Is needed because C++ has an implicit copy constructor and assign operator when none are defined.
/*!
* Util class to base other objects off which should never be copied.
* Is needed because C++ has an implicit copy constructor and assign operator when none are defined.
*/
class NoCopy
{
@@ -11,12 +11,16 @@ protected:
NoCopy() {}
private:
/// Private copy constructor.
/// Cannot be called because it is private.
/*!
* Private copy constructor.
* Cannot be called because it is private.
*/
NoCopy(const NoCopy&);
/// Private assign operator.
/// Cannot be called because it is private.
/*!
* Private assign operator.
* Cannot be called because it is private.
*/
NoCopy& operator =(const NoCopy&);
};
+74 -11
Ver Arquivo
@@ -6,6 +6,7 @@
#include "polygon.h"
#include "intpoint.h"
#include "AABB.h"
#include "logoutput.h"
namespace cura {
@@ -18,7 +19,8 @@ public:
GRAY,
RED,
BLUE,
GREEN
GREEN,
YELLOW
};
private:
@@ -33,6 +35,7 @@ private:
case SVG::Color::RED: return "red";
case SVG::Color::BLUE: return "blue";
case SVG::Color::GREEN: return "green";
case SVG::Color::YELLOW: return "yellow";
default: return "black";
}
}
@@ -51,8 +54,12 @@ public:
, scale(std::min(double(canvas_size.X - 20) / aabb_size.X, double(canvas_size.Y - 20) / aabb_size.Y))
{
out = fopen(filename, "w");
if(!out)
{
logError("The file %s could not be opened for writing.",filename);
}
fprintf(out, "<!DOCTYPE html><html><body>\n");
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.Y, canvas_size.X);
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.X, canvas_size.Y);
// fprintf(out, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
// fprintf(out, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
@@ -69,7 +76,7 @@ public:
/*!
* transform a point in real space to canvas space
*/
Point transform(Point& p)
Point transform(const Point& p)
{
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + Point(10,10);
}
@@ -123,7 +130,7 @@ public:
for(Point& p : poly)
{
Point fp = transform(p);
fprintf(out, "%lli,%lli ", fp.Y, fp.X);
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
}
if (j == 0)
fprintf(out, "\" style=\"fill:%s;stroke:%s;stroke-width:1\" />\n", toString(color).c_str(), toString(outline_color).c_str());
@@ -133,14 +140,25 @@ public:
}
}
void writePoint(Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
{
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
for(Point& point : polygon) //Add every point to the list of points.
{
Point transformed = transform(point);
fprintf(out,"%lli,%lli ",transformed.X,transformed.Y);
}
fprintf(out,"\" />\n"); //The end of the polygon tag.
}
void writePoint(const Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
Point pf = transform(p);
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.Y, pf.X, size, toString(color).c_str(), toString(color).c_str());
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.X, pf.Y, size, toString(color).c_str(), toString(color).c_str());
if (write_coords)
{
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.Y, pf.X, p.X, p.Y);
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.X, pf.Y, p.X, p.Y);
}
}
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
@@ -159,14 +177,59 @@ public:
}
}
void writeLine(Point& a, Point& b, Color color = Color::BLACK)
/*!
* \brief Draws a polyline on the canvas.
*
* The polyline is the set of line segments between each pair of consecutive
* points in the specified vector.
*
* \param polyline A set of points between which line segments must be
* drawn.
* \param color The colour of the line segments. If this is not specified,
* black will be used.
*/
void writeLines(std::vector<Point> polyline,Color color = Color::BLACK)
{
if(polyline.size() <= 1) //Need at least 2 points.
{
return;
}
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />", a.Y, a.X, b.Y, b.X, toString(color).c_str());
Point transformed = transform(polyline[0]); //Element 0 must exist due to the check above.
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"1\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
for(size_t point = 1;point < polyline.size();point++)
{
transformed = transform(polyline[point]);
fprintf(out,"L%lli,%lli",transformed.X,transformed.Y); //Write a line segment to the next point.
}
fprintf(out,"\" />\n"); //Write the end of the tag.
}
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
}
/*!
* \brief Draws a dashed line on the canvas from point A to point B.
*
* This is useful in the case where multiple lines may overlap each other.
*
* \param a The starting endpoint of the line.
* \param b The ending endpoint of the line.
* \param color The stroke colour of the line.
*/
void writeDashedLine(const Point& a,const Point& b,Color color = Color::BLACK)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out,"<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" stroke=\"%s\" stroke-width=\"1\" stroke-dasharray=\"5,5\" />\n",fa.X,fa.Y,fb.X,fb.Y,toString(color).c_str());
}
template<typename... Args>
void printf(const char* txt, Args... args)
void printf(const char* txt, Args&&... args)
{
fprintf(out, txt, args...);
}
@@ -213,4 +276,4 @@ public:
};
} // namespace cura
#endif // SVG_H
#endif // SVG_H
+5
Ver Arquivo
@@ -126,6 +126,8 @@ public:
};
static Point3 no_point3(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
inline Point3 operator*(const int32_t i, const Point3& rhs) {
return rhs * i;
}
@@ -145,6 +147,8 @@ public:
#define POINT_MIN std::numeric_limits<ClipperLib::cInt>::min()
#define POINT_MAX std::numeric_limits<ClipperLib::cInt>::max()
static Point no_point(std::numeric_limits<int32_t>::infinity(), std::numeric_limits<int32_t>::infinity());
/* Extra operators to make it easier to do math with the 64bit Point objects */
INLINE Point operator-(const Point& p0) { return Point(-p0.X, -p0.Y); }
INLINE Point operator+(const Point& p0, const Point& p1) { return Point(p0.X+p1.X, p0.Y+p1.Y); }
@@ -307,3 +311,4 @@ inline Point operator-(const Point& p2, const Point3& p3) {
}//namespace cura
#endif//INT_POINT_H
+27 -6
Ver Arquivo
@@ -103,12 +103,14 @@ public:
/*!
* Get the squared distance from point \p b to a line *segment* from \p a to \p c.
*
* In case \p b is on \p a or \p c, \p b_is_beyond_ac should become 0.
*
* \param a the first point of the line segment
* \param b the point to measure the distance from
* \param c the second point on the line segment
* \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1)
*/
static int64_t getDist2FromLineSegment(Point& a, Point& b, Point& c, char* b_is_beyond_ac = nullptr)
static int64_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, char* b_is_beyond_ac = nullptr)
{
/*
* a,
@@ -127,27 +129,46 @@ public:
*/
Point ac = c - a;
int64_t ac_size = vSize(ac);
if (ac_size == 0) { return 0; }
Point ab = b - a;
if (ac_size == 0)
{
int64_t ab_dist2 = vSize2(ab);
if (ab_dist2 == 0)
{
*b_is_beyond_ac = 0; // a is on b is on c
}
// otherwise variable b_is_beyond_ac remains its value; it doesn't make sense to choose between -1 and 1
return ab_dist2;
}
int64_t projected_x = dot(ab, ac);
int64_t ax_size = projected_x / ac_size;
if (ax_size < 0)
{// b is 'before' segment ac
if (b_is_beyond_ac) { *b_is_beyond_ac = -1; }
if (b_is_beyond_ac)
{
*b_is_beyond_ac = -1;
}
return vSize2(ab);
}
if (ax_size > ac_size)
{// b is 'after' segment ac
if (b_is_beyond_ac) { *b_is_beyond_ac = 1; }
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 1;
}
return vSize2(b - c);
}
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 0;
}
Point ax = ac * ax_size / ac_size;
Point bx = ab - ax;
if (b_is_beyond_ac) { *b_is_beyond_ac = 0; }
return vSize2(bx);
// return vSize2(ab) - ax_size*ax_size; // less accurate
}
+1 -1
Ver Arquivo
@@ -174,7 +174,7 @@ void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_d
continue;
}
Point& next = thiss[(poly_idx+1) % size()];
char here_is_beyond_line;
char here_is_beyond_line = 0;
int64_t error2 = LinearAlg2D::getDist2FromLineSegment(*last, here, next, &here_is_beyond_line);
if (here_is_beyond_line == 0 && error2 < allowed_error_distance_squared)
{// don't add the point to the result
+5 -2
Ver Arquivo
@@ -30,7 +30,9 @@ enum PolygonType
SupportType,
SkirtType,
InfillType,
SupportInfillType
SupportInfillType,
MoveCombingType,
MoveRetractionType
};
@@ -81,7 +83,7 @@ public:
ClipperLib::Path& operator*() { return *polygon; }
template <typename... Args>
void emplace_back(Args... args)
void emplace_back(Args&&... args)
{
polygon->emplace_back(args...);
}
@@ -357,6 +359,7 @@ public:
}
Polygons() {}
Polygons(const Polygons& other) { polygons = other.polygons; }
Polygons& operator=(const Polygons& other) { polygons = other.polygons; return *this; }
Polygons difference(const Polygons& other) const
+19 -7
Ver Arquivo
@@ -104,8 +104,13 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
Point ab = b - a;
Point ap = p - a;
int64_t ab_length = vSize(ab);
if(ab_length <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
{
p1 = p2; //Skip only one of the points.
continue;
}
int64_t ax_length = dot(ab, ap) / ab_length;
if (ax_length < 0) // x is projected to before ab
if (ax_length <= 0) // x is projected to before ab
{
if (projected_p_beyond_prev_segment)
{ // case which looks like: > .
@@ -120,7 +125,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4));
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
is_inside = dot(inward_dir, p - x) >= 0;
}
@@ -134,7 +139,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
continue;
}
}
else if (ax_length > ab_length) // x is projected to beyond ab
else if (ax_length >= ab_length) // x is projected to beyond ab
{
projected_p_beyond_prev_segment = true;
p0 = p1;
@@ -449,11 +454,19 @@ bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& transf
for(Point p1_ : poly)
{
Point p1 = transformation_matrix.apply(p1_);
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))
{
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
int64_t x;
if(p1.Y == p0.Y)
{
x = p0.X;
}
else
{
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 >= transformed_startPoint.X && x <= transformed_endPoint.X)
return true;
}
p0 = p1;
@@ -461,7 +474,6 @@ bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& transf
return false;
}
bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& startPoint, Point& endPoint)
{
Point diff = endPoint - startPoint;
+64 -32
Ver Arquivo
@@ -136,47 +136,79 @@ public:
/*!
* Checks whether a given line segment collides with a given polygon(s).
* The transformed_startPoint and transformed_endPoint should have the same Y coordinate.
*
* \param poly The polygon
* \param transformed_startPoint The start point transformed such that it is on the same horizontal line as the end point
* \param transformed_endPoint The end point transformed such that it is on the same horizontal line as the start point
* \param transformation_matrix The transformation applied to the start and end point to be applied to the polygon(s)
* \return whether the line segment collides with the boundary of the polygon(s)
*/
* Checks whether a given line segment collides with a given polygon(s).
* The transformed_startPoint and transformed_endPoint should have the same
* Y coordinate.
*
* If the line segment doesn't intersect with any edge of the polygon, but
* merely touches it, a collision is also reported. For instance, a
* collision is reported when the an endpoint of the line is exactly on the
* polygon, and when the line coincides with an edge.
*
* \param poly The polygon
* \param transformed_startPoint The start point transformed such that it is
* on the same horizontal line as the end point
* \param transformed_endPoint The end point transformed such that it is on
* the same horizontal line as the start point
* \param transformation_matrix The transformation applied to the start and
* end point to be applied to the polygon(s)
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
/*!
* Checks whether a given line segment collides with a given polygon(s).
*
* \param poly The polygon
* \param startPoint The start point
* \param endPoint The end point
* \return whether the line segment collides with the boundary of the polygon(s)
*/
* Checks whether a given line segment collides with a given polygon(s).
*
* If the line segment doesn't intersect with any edge of the polygon, but
* merely touches it, a collision is also reported. For instance, a
* collision is reported when the an endpoint of the line is exactly on the
* polygon, and when the line coincides with an edge.
*
* \param poly The polygon
* \param startPoint The start point
* \param endPoint The end point
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(PolygonRef poly, Point& startPoint, Point& endPoint);
/*!
* Checks whether a given line segment collides with a given polygon(s).
* The transformed_startPoint and transformed_endPoint should have the same Y coordinate.
*
* \param poly The polygon
* \param transformed_startPoint The start point transformed such that it is on the same horizontal line as the end point
* \param transformed_endPoint The end point transformed such that it is on the same horizontal line as the start point
* \param transformation_matrix The transformation applied to the start and end point to be applied to the polygon(s)
* \return whether the line segment collides with the boundary of the polygon(s)
*/
* Checks whether a given line segment collides with a given polygon(s).
* The transformed_startPoint and transformed_endPoint should have the same
* Y coordinate.
*
* If the line segment doesn't intersect with any edge of the polygon, but
* merely touches it, a collision is also reported. For instance, a
* collision is reported when the an endpoint of the line is exactly on the
* polygon, and when the line coincides with an edge.
*
* \param poly The polygon
* \param transformed_startPoint The start point transformed such that it is
* on the same horizontal line as the end point
* \param transformed_endPoint The end point transformed such that it is on
* the same horizontal line as the start point
* \param transformation_matrix The transformation applied to the start and
* end point to be applied to the polygon(s)
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
/*!
* Checks whether a given line segment collides with a given polygon(s).
*
* \param poly The polygon
* \param startPoint The start point
* \param endPoint The end point
* \return whether the line segment collides with the boundary of the polygon(s)
*/
* Checks whether a given line segment collides with a given polygon(s).
*
* If the line segment doesn't intersect with any edge of the polygon, but
* merely touches it, a collision is also reported. For instance, a
* collision is reported when the an endpoint of the line is exactly on the
* polygon, and when the line coincides with an edge.
*
* \param poly The polygon
* \param startPoint The start point
* \param endPoint The end point
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(Polygons& polys, Point& startPoint, Point& endPoint);
};
+2 -1
Ver Arquivo
@@ -2,6 +2,7 @@
#ifndef WEAVE_DATA_STORAGE_H
#define WEAVE_DATA_STORAGE_H
#include "utils/NoCopy.h"
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "mesh.h"
@@ -69,7 +70,7 @@ struct WeaveLayer : WeaveConnection
// [connections] are the vertical connections
WeaveRoof roofs; //!< parts which are filled horizontally (both roofs and floors...)
};
struct WireFrame
struct WireFrame : public NoCopy
{
MeshGroup* meshgroup;
WeaveRoof bottom_infill;
+84
Ver Arquivo
@@ -0,0 +1,84 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "GCodePlannerTest.h"
#include "../src/MeshGroup.h" //Needed to construct the GCodePlanner.
#define ALLOWED_ESTIMATE_ERROR 0.1 //Fraction of the time estimates that the estimate is allowed to be off from the ground truth.
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(GCodePlannerTest);
void GCodePlannerTest::setUp()
{
storage = new SliceDataStorage(nullptr); //Empty data.
storage->retraction_config.speed = 1; //We'll set some of the configurations, just in order to get valid g-code (speed not zero, etc.)
storage->retraction_config.primeSpeed = 1;
storage->retraction_config.distance = 10;
storage->travel_config.init(1,1,1);
FanSpeedLayerTimeSettings fan_speed_layer_time_settings; //A dummy fan speed and layer time settings.
fan_speed_layer_time_settings.cool_min_layer_time = 0;
fan_speed_layer_time_settings.cool_min_layer_time_fan_speed_max = 1;
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
fan_speed_layer_time_settings.cool_min_speed = 0.5;
// Command Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
// socket storage nr height position extruder time settings combing offset avoid distance
gCodePlanner = new GCodePlanner(nullptr, *storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
}
void GCodePlannerTest::tearDown()
{
delete gCodePlanner;
gCodePlanner = nullptr;
delete storage;
storage = nullptr;
}
void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
{
TimeMaterialEstimates estimate_empty = gCodePlanner->computeNaiveTimeEstimates(); //First try estimating time and material without any content.
TimeMaterialEstimates estimate_empty_expected(0,0,0,0); //We expect the estimate of all time and material used to be 0.
verifyEstimates(estimate_empty,estimate_empty_expected,"Empty GCodePlanner");
GCodeExport gcode;
GCodePathConfig configuration = storage->travel_config;
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
TimeMaterialEstimates before_retract = gCodePlanner->computeNaiveTimeEstimates();
gCodePlanner->writeRetraction(gcode,(unsigned int)0,(unsigned int)0); //Make a retract.
TimeMaterialEstimates after_retract = gCodePlanner->computeNaiveTimeEstimates();
TimeMaterialEstimates estimate_one_retraction = after_retract - before_retract;
double retract_unretract_time = configuration.retraction_config->distance / configuration.retraction_config->primeSpeed;
TimeMaterialEstimates estimate_one_retraction_expected(0,retract_unretract_time * 0.5,retract_unretract_time * 0.5,0);
verifyEstimates(estimate_one_retraction,estimate_one_retraction_expected,"One retraction");
}
void GCodePlannerTest::verifyEstimates(const TimeMaterialEstimates& observed,const TimeMaterialEstimates& expected,std::string test_description)
{
//Check each of the four estimates in the TimeMaterialEstimate instances.
{
std::stringstream ss;
ss << test_description << ": Extrude time is " << observed.getExtrudeTime() << " instead of the expected " << expected.getExtrudeTime();
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getExtrudeTime() - expected.getExtrudeTime() < expected.getExtrudeTime() * ALLOWED_ESTIMATE_ERROR + 0.001);
}
{
std::stringstream ss;
ss << test_description << ": Unretracted travel time is " << (observed.getTotalUnretractedTime() - observed.getExtrudeTime()) << " instead of the expected " << (expected.getTotalUnretractedTime() - expected.getExtrudeTime());
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getTotalUnretractedTime() - observed.getExtrudeTime() - (expected.getTotalUnretractedTime() - expected.getExtrudeTime()) < (expected.getTotalUnretractedTime() - expected.getExtrudeTime()) * ALLOWED_ESTIMATE_ERROR + 0.001);
}
{
std::stringstream ss;
ss << test_description << ": Retracted travel time is " << (observed.getTotalTime() - observed.getTotalUnretractedTime()) << " instead of the expected " << (expected.getTotalTime() - expected.getTotalUnretractedTime());
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getTotalTime() - observed.getTotalUnretractedTime() - (expected.getTotalTime() - expected.getTotalUnretractedTime()) < (expected.getTotalTime() - expected.getTotalUnretractedTime()) * ALLOWED_ESTIMATE_ERROR + 0.001);
}
{
std::stringstream ss;
ss << test_description << ": Material used is " << observed.getMaterial() << " instead of the expected " << expected.getMaterial();
CPPUNIT_ASSERT_MESSAGE(ss.str(),observed.getMaterial() - expected.getMaterial() < expected.getMaterial() * ALLOWED_ESTIMATE_ERROR + 0.001);
}
}
}
+76
Ver Arquivo
@@ -0,0 +1,76 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef GCODEPLANNERTEST_H
#define GCODEPLANNERTEST_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include "../src/gcodePlanner.h"
#include "../src/sliceDataStorage.h"
namespace cura
{
class GCodePlannerTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(GCodePlannerTest);
CPPUNIT_TEST(computeNaiveTimeEstimatesRetractionTest);
CPPUNIT_TEST_SUITE_END();
public:
/*!
* \brief Sets up the test suite to prepare for testing.
*
* This creates an instance of <em>gcodePlanner</em>, ready for filling with
* data.
*/
void setUp();
/*!
* \brief Tears down the test suite when testing is done.
*
* This destroys the <em>gcodePlanner</em> instance.
*/
void tearDown();
//The actual test cases.
void computeNaiveTimeEstimatesRetractionTest();
private:
/*!
* \brief The instance of gcodePlanner that can be used for testing.
*
* This instance is re-created for each test. Between tests it will be
* <em>nullptr</em>.
*/
GCodePlanner* gCodePlanner;
/*!
* \brief Slice data storage to construct the <em>GCodePlanner</em> with.
*
* It also holds configurations for the paths to add to the
* <em>GCodePlanner</em>.
*/
SliceDataStorage* storage;
/*!
* \brief Asserts that the two time material estimates are equal.
*
* If they are not equal, the error messages are formulated according to the
* specified observed results versus the specified expected results. It will
* include a specified string describing what test it was.
*
* \param observed The observed time material estimates.
* \param expected The expected (true) time material estimates.
* \param test_description A description of the test that was performed to
* get the observed results.
*/
void verifyEstimates(const TimeMaterialEstimates& observed,const TimeMaterialEstimates& expected,std::string test_description);
};
}
#endif // GCODEPLANNERTEST_H
+218
Ver Arquivo
@@ -0,0 +1,218 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "LinearAlg2DTest.h"
#include <../src/utils/linearAlg2D.h>
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(LinearAlg2DTest);
void LinearAlg2DTest::setUp()
{
//Do nothing.
}
void LinearAlg2DTest::tearDown()
{
//Do nothing.
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalNearTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(25,3),9,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalOnTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(25,0),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalBeyondTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(200,0),10000,1);
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalBeforeTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(-100,0),10000,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalCornerTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(-1,-1),2,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentHorizontalPerpendicularTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,0),Point(0,3),9,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalNearTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(5,25),25,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalOnTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,25),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalBeyondTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,200),10000,1);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalBeforeTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(0,-100),10000,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalCornerTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(-1,-1),2,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentVerticalPerpendicularTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,100),Point(3,0),9,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalNearTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(30,20),50,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalOnTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(25,25),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalBeyondTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(200,200),20000,1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalBeforeTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(-100,-100),20000,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalCornerTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(-3,0),9,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonalPerpendicularTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,100),Point(3,-3),9,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2NearTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(20,30),320,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2OnTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(40,20),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2PointTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(0,0),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2BeyondTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(200,100),12500,1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2BeforeTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-100,-50),12500,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2CornerTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-3,0),9,-1);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2PerpendicularTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(100,50),Point(-2,4),20,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentDiagonal2LargeTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(10000,5000),Point(2000,3000),3200000,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentZeroNearTest()
{
int64_t supposed_distance = LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(20,0),Point(0,0));
int64_t actual_distance = 400;
std::stringstream ss;
ss << "Line [0,0] -- [0,0], point [20,0], squared distance was ";
ss << supposed_distance;
ss << " rather than ";
ss << actual_distance;
ss << ".";
CPPUNIT_ASSERT_MESSAGE(ss.str(),std::abs(supposed_distance - actual_distance) <= maximum_error);
}
void LinearAlg2DTest::getDist2FromLineSegmentZeroOnTest()
{
getDist2FromLineSegmentAssert(Point(0,0),Point(0,0),Point(0,0),0,0);
}
void LinearAlg2DTest::getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond)
{
char supposed_is_beyond;
int64_t supposed_distance = LinearAlg2D::getDist2FromLineSegment(line_start,point,line_end,&supposed_is_beyond);
{
std::stringstream ss;
ss << "Line [";
ss << line_start.X;
ss << ",";
ss << line_start.Y;
ss << "] -- [";
ss << line_end.X;
ss << ",";
ss << line_end.Y;
ss << "], point [";
ss << point.X;
ss << ",";
ss << point.Y;
ss << "], squared distance was ";
ss << supposed_distance;
ss << " rather than ";
ss << actual_distance2;
ss << ".";
CPPUNIT_ASSERT_MESSAGE(ss.str(),std::fabs(sqrt(double(supposed_distance)) - sqrt(double(actual_distance2))) <= maximum_error);
}
{
std::stringstream ss;
ss << "Line [";
ss << line_start.X;
ss << ",";
ss << line_start.Y;
ss << "] -- [";
ss << line_end.X;
ss << ",";
ss << line_end.Y;
ss << "], point [";
ss << point.X;
ss << ",";
ss << point.Y;
ss << "], check whether it is beyond was ";
ss << static_cast<int>(supposed_is_beyond);
ss << " rather than ";
ss << static_cast<int>(actual_is_beyond);
ss << ".";
CPPUNIT_ASSERT_MESSAGE(ss.str(),supposed_is_beyond == actual_is_beyond);
}
}
}
+119
Ver Arquivo
@@ -0,0 +1,119 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef LINEARALG2DTEST_H
#define LINEARALG2DTEST_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <../src/utils/intpoint.h>
namespace cura
{
class LinearAlg2DTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(LinearAlg2DTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalOnTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalBeyondTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalBeforeTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalCornerTest);
CPPUNIT_TEST(getDist2FromLineSegmentHorizontalPerpendicularTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalOnTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalBeyondTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalBeforeTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalCornerTest);
CPPUNIT_TEST(getDist2FromLineSegmentVerticalPerpendicularTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalOnTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalBeyondTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalBeforeTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalCornerTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonalPerpendicularTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2NearTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2OnTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2PointTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2BeyondTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2BeforeTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2CornerTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2PerpendicularTest);
CPPUNIT_TEST(getDist2FromLineSegmentDiagonal2LargeTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroNearTest);
CPPUNIT_TEST(getDist2FromLineSegmentZeroOnTest);
CPPUNIT_TEST_SUITE_END();
public:
/*!
* \brief Sets up the test suite to prepare for testing.
*
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
* needs to be created here.
*/
void setUp();
/*!
* \brief Tears down the test suite when testing is done.
*
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
* exists that needs to be destroyed.
*/
void tearDown();
//These are the actual test cases. The name of the function sort of describes what it tests but I refuse to document all of these, sorry.
void getDist2FromLineSegmentHorizontalNearTest();
void getDist2FromLineSegmentHorizontalOnTest();
void getDist2FromLineSegmentHorizontalBeyondTest();
void getDist2FromLineSegmentHorizontalBeforeTest();
void getDist2FromLineSegmentHorizontalCornerTest();
void getDist2FromLineSegmentHorizontalPerpendicularTest();
void getDist2FromLineSegmentVerticalNearTest();
void getDist2FromLineSegmentVerticalOnTest();
void getDist2FromLineSegmentVerticalBeyondTest();
void getDist2FromLineSegmentVerticalBeforeTest();
void getDist2FromLineSegmentVerticalCornerTest();
void getDist2FromLineSegmentVerticalPerpendicularTest();
void getDist2FromLineSegmentDiagonalNearTest();
void getDist2FromLineSegmentDiagonalOnTest();
void getDist2FromLineSegmentDiagonalBeyondTest();
void getDist2FromLineSegmentDiagonalBeforeTest();
void getDist2FromLineSegmentDiagonalCornerTest();
void getDist2FromLineSegmentDiagonalPerpendicularTest();
void getDist2FromLineSegmentDiagonal2NearTest();
void getDist2FromLineSegmentDiagonal2OnTest();
void getDist2FromLineSegmentDiagonal2PointTest();
void getDist2FromLineSegmentDiagonal2BeyondTest();
void getDist2FromLineSegmentDiagonal2BeforeTest();
void getDist2FromLineSegmentDiagonal2CornerTest();
void getDist2FromLineSegmentDiagonal2PerpendicularTest();
void getDist2FromLineSegmentDiagonal2LargeTest();
void getDist2FromLineSegmentZeroNearTest();
void getDist2FromLineSegmentZeroOnTest();
private:
/*!
* \brief The maximum allowed error in distance measurements.
*/
static const int64_t maximum_error = 10;
/*!
* \brief Performs the actual assertion for the getDist2FromLineSegmentTest.
*
* This is essentially a parameterised version of all unit tests pertaining
* to the getDist2FromLineSegment tests.
*
* \param line_start The start of the line to check the distance to.
* \param line_end The end of the line to check the distance to.
* \param point The point to check the distance to the line with.
* \param actual_distance2 The correct distance from the point to the line,
* squared.
* \param actual_is_beyond Whether the point is actually beyond the line.
*/
void getDist2FromLineSegmentAssert(Point line_start,Point line_end,Point point,int64_t actual_distance2,char actual_is_beyond);
};
}
#endif //LINEARALG2DTEST_H
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -0,0 +1,32 @@
Regression test:
---------------
- Slice on old Cura (15.04 and older versions of the new Cura)
- Slice on the current version of Cura
- Print and compare printed results
Compatibility test:
------------------
- Slice on current version of Cura on your own development system
- Slice on current version from build on various operating systems
- diff on gcode to see whether they are the same
==================================================================================
How to slice:
- Load default normal quality settings
- Perform exceptions to above rule (listed below)
- Clear build platform
- Load model
- Don't move or resize model!
- Let it slice...
Exceptions:
3DHackerTest.stl no support
ctrlV_3D_test.stl no support
Debailey_x10.stl
dragon_65_tilted_large.stl
TortureTestV2.stl no support
UltimakerRobot_support_2015.stl no support
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
+25
Ver Arquivo
@@ -0,0 +1,25 @@
//Copyright (c) 2015 Ultimaker B.V.
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/XmlOutputter.h>
/*!
* \brief Runs the test cases.
*/
int main(int argc,char** argv)
{
CppUnit::TextUi::TestRunner runner;
//Set the output type to be JUnit-style XML.
std::ofstream output("output.xml");
CppUnit::XmlOutputter* outputter = new CppUnit::XmlOutputter(&runner.result(), output);
runner.setOutputter(outputter);
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest()); //Makes a test suite from all test cases that are registered with CPPUNIT_TEST_SUITE_REGISTRATION().
bool success = runner.run("", false); //Run the tests!
return success ? 0 : 1;
}
+268 -20
Ver Arquivo
@@ -1,28 +1,276 @@
#!/usr/bin/python
#!/usr/bin/python3
## runtest.py
# The runtest.py script runs regression tests on the CuraEngine.
# It parses the json file for settings to know which settings can be passed towards the engine.
# It runs the following test:
# * Defaults
# * Bounds/possible values
# * Single random value
# * All settings random
import sys
import subprocess
import os
import time
import stat
import argparse
import random
import json
import threading
from xml.etree import ElementTree
def main(engine, model_path):
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
filenames = list(filter(lambda filename: filename.lower().endswith('.stl'), filenames))
for filename in filenames:
print("Slicing: %s (%d/%d)" % (filename, filenames.index(filename), len(filenames)))
t = time.time()
p = subprocess.Popen([engine, '-vv', os.path.join(model_path, filename)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
print ("Engine failed to report success on test object: %s" % (filename))
print(stderr.decode('utf-8', 'replace').split('\n')[-5:])
sys.exit(1)
else:
print("Slicing took: %f" % (time.time() - t))
if __name__ == '__main__':
model_path = 'tests'
if len(sys.argv) > 2:
model_path = sys.argv[2]
main(sys.argv[1], model_path)
## The TestSuite class stores the test results of a single set of tests.
# TestSuite objects are created by the TestResults class.
class TestSuite():
def __init__(self, name):
self._name = name
self._successes = []
self._failures = []
## Add a successfull test result to the test suite.
def success(self, class_name, test_name):
#print('Success:', class_name, test_name)
self._successes.append((class_name, test_name))
## Add a failed test result to the test suite.
def failure(self, class_name, test_name, error_message):
#print('Failure:', class_name, test_name, error_message)
self._failures.append((class_name, test_name, error_message))
## Return the number of tests in this test suite
def getTestCount(self):
return self.getSuccessCount() + self.getFailureCount()
## Return the number of successfull tests in this test suite
def getSuccessCount(self):
return len(self._successes)
## Return the number of failed tests in this test suite
def getFailureCount(self):
return len(self._failures)
## The TestResults class stores a group of TestSuite objects, each TestSuite object contains failed and successful test.
# This class can output the result of the tests in a JUnit xml format for parsing in Jenkins.
class TestResults():
def __init__(self):
self._testsuites = []
## Create a new test suite with the name.
def addTestSuite(self, name):
suite = TestSuite(name)
self._testsuites.append(suite)
return suite
def getFailureCount(self):
result = 0
for testsuite in self._testsuites:
result += testsuite.getFailureCount()
return result
## Save the test results to the file given in the filename.
def saveXML(self, filename):
xml = ElementTree.Element("testsuites")
xml.text = "\n"
for testsuite in self._testsuites:
testsuite_xml = ElementTree.SubElement(xml, "testsuite", {"name": testsuite._name, "errors": "0", "tests": str(testsuite.getTestCount()), "failures": str(testsuite.getFailureCount()), "time": "0", "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())})
testsuite_xml.text = "\n"
testsuite_xml.tail = "\n"
for class_name, test_name in testsuite._successes:
testcase_xml = ElementTree.SubElement(testsuite_xml, "testcase", {"classname": class_name, "name": test_name})
testcase_xml.text = "\n"
testcase_xml.tail = "\n"
for class_name, test_name, error_message in testsuite._failures:
testcase_xml = ElementTree.SubElement(testsuite_xml, "testcase", {"classname": class_name, "name": test_name})
testcase_xml.text = "\n"
testcase_xml.tail = "\n"
failure = ElementTree.SubElement(testcase_xml, "failure", {"message": "test failure"})
failure.text = error_message
failure.tail = "\n"
return ElementTree.ElementTree(xml).write(filename, "utf-8", True)
class Setting():
def __init__(self, key, data):
self._key = key
self._default = data["default"]
self._type = data["type"]
self._min_value = data.get("min_value", None)
self._max_value = data.get("max_value", None)
self._min_value_warning = data.get("min_value_warning", None)
self._max_value_warning = data.get("max_value_warning", None)
self._options = data.get("options", None)
if self._options is not None:
self._options = list(self._options.keys())
## Return a list of possible values for this setting. This list depends on the setting type.
# For number values it contains the minimal and maximal values.
# For enums and booleans it will contain the exact possible values.
# For string settings only the default value is returned.
def getSettingValues(self):
if self._type == "boolean":
return ["True", "False"]
if self._type == "float" or self._type == "int":
ret = [self._default]
if self._min_value is not None:
ret.append(self._min_value)
else:
ret.append(-2)
if self._min_value_warning is not None:
ret.append(self._min_value_warning)
if self._max_value is not None:
ret.append(self._max_value)
else:
if self._max_value_warning is None:
ret.append(10000)
elif self._type == "float":
ret.append(float(self._max_value_warning) + 100)
ret.append(float(self._max_value_warning) * 10)
elif self._type == "int":
ret.append(int(self._max_value_warning) + 100)
ret.append(int(self._max_value_warning) * 10)
# If the type is boolean, string or enum, the warning values make no sense anyway, so don't test them.
if self._max_value_warning is not None:
ret.append(self._max_value_warning)
if self._type == "int":
for n in range(0, len(ret)):
ret[n] = int(ret[n])
return ret
if self._type == "enum":
return self._options
if self._type == "string":
return self._default
print("Unknown setting type:", self._type)
## Return a random value for this setting. The returned value will be a valid value according to the settings json file.
def getRandomValue(self):
if self._type == "float" or self._type == "int":
min = -2
if self._min_value_warning is not None:
min = self._min_value_warning
if self._min_value is not None:
min = self._min_value
max = 10000
if self._max_value_warning is not None:
max = self._max_value_warning
if self._max_value is not None:
max = self._max_value
if self._type == "int":
return random.randint(int(min), int(max))
return random.uniform(float(min), float(max))
return random.choice(self.getSettingValues())
class EngineTest():
def __init__(self, json_filename, engine_filename, models):
self._json_filename = json_filename
self._json = json.load(open(json_filename, "r"))
self._engine = engine_filename
self._models = models
self._settings = {}
self._test_results = TestResults()
self._flattenAllSettings()
def _flattenAllSettings(self):
for key, data in self._json["categories"].items():
self._flattenSettings(data["settings"])
def _flattenSettings(self, settings):
for key, setting in settings.items():
self._settings[key] = Setting(key, setting)
if "children" in setting:
self._flattenSettings(setting["children"])
def testDefaults(self):
suite = self._test_results.addTestSuite("Defaults")
self._runTest(suite, "defaults", {})
return suite.getFailureCount()
def testSingleChanges(self):
suite = self._test_results.addTestSuite("SingleSetting")
for key, setting in self._settings.items():
for value in setting.getSettingValues():
self._runTest(suite, key, {key: value})
return suite.getFailureCount()
def testSingleRandom(self):
suite = self._test_results.addTestSuite("SingleRandom")
for key, setting in self._settings.items():
self._runTest(suite, key, {key: setting.getRandomValue()})
return suite.getFailureCount()
def testDualRandom(self):
suite = self._test_results.addTestSuite("DualRandom")
for key, setting in self._settings.items():
for key2, setting2 in self._settings.items():
if key != key2:
self._runTest(suite, key, {key: setting.getRandomValue(), key2: setting2.getRandomValue()})
return suite.getFailureCount()
def testAllRandom(self, amount):
suite = self._test_results.addTestSuite("AllRandom_%d" % (amount))
for n in range(0, amount):
settings = {}
for key, setting in self._settings.items():
settings[key] = setting.getRandomValue()
self._runTest(suite, "Random", settings)
return suite.getFailureCount()
def _runTest(self, suite, class_name, settings):
test_name = ', '.join("{!s}={!r}".format(key, val) for (key,val) in settings.items())
for model in self._models:
this_test_name = '%s.%s' % (os.path.basename(model), test_name)
cmd = [self._engine, "slice", "-j", self._json_filename, "-o", "/dev/null"]
for key, value in settings.items():
cmd += ['-s', '%s=%s' % (key, value)]
cmd += ["-l", model]
error = self._runProcess(cmd)
if error is not None:
suite.failure(class_name, this_test_name, error)
else:
suite.success(class_name, this_test_name)
def _runProcess(self, cmd):
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.error = ""
t = threading.Thread(target=self._abortProcess, args=(p,))
t.start()
stdout, stderr = p.communicate()
if p.error == "Timeout":
return "Timeout: %s" % (' '.join(cmd))
if p.wait() != 0:
return "Execution failed: %s" % (' '.join(cmd))
return None
def _abortProcess(self, p):
time.sleep(60)
if p.poll() is None:
p.terminate()
p.error = "Timeout"
def getResults(self):
return self._test_results
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CuraEngine testing script")
parser.add_argument("--simple", action="store_true", help="Only run the single test, exit")
parser.add_argument("json", type=str, help="Machine JSON file to use")
parser.add_argument("engine", type=str, help="Engine executable")
parser.add_argument("models", type=str, nargs="+", help="List of models to use for testing")
args = parser.parse_args()
et = EngineTest(args.json, args.engine, args.models)
if et.testDefaults() == 0:
if not args.simple:
et.testSingleChanges()
if et.testSingleRandom() == 0:
et.testDualRandom()
if et.testAllRandom(10) == 0:
et.testAllRandom(100)
et.getResults().saveXML("output.xml")
if args.simple:
if et.getResults().getFailureCount() > 0:
sys.exit(1)
+19658
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff