Comparar commits

..

833 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
Tim Kuipers ab313b8992 doc: explanations on support 2015-09-09 14:18:38 +02:00
Tim Kuipers 7779bb9604 bugfix: support roofs per object 2015-09-09 13:54:18 +02:00
Tim Kuipers 1576d1d19e bugfix: normal support per object 2015-09-09 13:23:17 +02:00
Tim Kuipers 615ed0b384 README now has correct call method 2015-09-09 11:00:29 +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
Tim Kuipers ee09c6da92 lil change fuzzy mode 2015-09-04 10:10:58 +02:00
Tim Kuipers 325345a2ce bugfix: fuzzy skin had too many points; redux can lead to not enough points on poly 2015-09-03 19:15:40 +02:00
Tim Kuipers 29e17d8bdf feat: fuzzy skin magic mode 2015-09-03 18:33:15 +02:00
Tim Kuipers 1f1bfadf9e basic priliminary functionality for tracking print time per feature; safety assertions in writemove(.) which work for UM2 2015-09-03 14:22:41 +02:00
Tim Kuipers 5aca2bde45 bugfix: speeds were too high for merged infill lines 2015-09-03 14:16:38 +02:00
Tim Kuipers ae6426cb6e Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-09-02 13:29:34 +02:00
Arjen Hiemstra 7271693169 Normalize enum value names to reflect changes in the JSON file
We now store options as (key => user visible string) in the json file.
The keys in the json file have been normalized. Now the engine also
reflects that change.
2015-09-01 18:20:22 +02:00
Tim Kuipers d5bbe57571 magic_mesh_surface_mode setting refactor registered 2015-09-01 15:28:11 +02:00
Tim Kuipers 8423d3ed4a lil 2015-08-31 17:09:20 +02:00
Tim Kuipers b1f717ad9b refactor: moved processOozeShield 2015-08-31 15:07:37 +02:00
Tim Kuipers 00eba89494 refactor: made all enums conformant 2015-08-31 14:48:59 +02:00
Tim Kuipers 76e5b8b94b split magic_mesh)surface_mode into Normal, Surface, Both 2015-08-31 14:31:28 +02:00
Tim Kuipers 80e09a05ef lil bugfix: typo in FffPolygonGenerator.cpp 2015-08-31 13:06:58 +02:00
Tim Kuipers e96bd68324 bugfix: SliceDataStorage::getLayerOutlines used ungenerated support 2015-08-31 13:05:36 +02:00
Tim Kuipers 9ba6239219 bugfix: ooze shield was computed before support 2015-08-31 13:04:52 +02:00
Tim Kuipers af099e6896 used SliceDataStorage::getLayerOutlines(.) for raft, skirt, oozeShield, draftShield, combing 2015-08-31 12:36:34 +02:00
Tim Kuipers bcdcc25a51 offsetPolyline(.) 2015-08-31 12:30:54 +02:00
Tim Kuipers 6b9c98914d SliceDataStorage::getLayerOutlines 2015-08-31 12:30:07 +02:00
Tim Kuipers e1d508ade0 bugfix: no support for meshes without insets 2015-08-31 12:28:49 +02:00
Tim Kuipers 919c0ea16d better visualization mesh surface mode and removeEmptyFirstLayers now takes mesh surface mode into account 2015-08-28 16:56:41 +02:00
Tim Kuipers 865b22063b better visualization mesh surface mode 2015-08-28 16:51:46 +02:00
Tim Kuipers 4bfd48efa7 bugfixes: uninitialized support.generated; uninitialized primeTower.extruder_count; layer_nr_max_filled_layer didnt account for mesh surface mode; refactored all magic_polygon_mode to mesh_surface_mode; bugfix mesh surface mode: tried to write lines as polygons 2015-08-28 16:44:08 +02:00
Tim Kuipers 9e2b269c8b refactor: slicer and mesh surface mode names; starting mesh surface mode debug 2015-08-28 11:42:09 +02:00
Tim Kuipers 2e78d60c13 disable comb boundary for magic mesh surface mode or spiralize 2015-08-28 10:20:38 +02:00
Tim Kuipers caf1fd5582 disable comb boundary for magic mesh surface mode or spiralize 2015-08-28 10:20:18 +02:00
Tim Kuipers 8f99483d2c bugfix: moveinside called for spiralize vase 2015-08-28 10:16:26 +02:00
Tim Kuipers fc31b13a5e infill visualized area, not lines... 2015-08-27 14:49:05 +02:00
Tim Kuipers 2ea1bc0e59 refactor: renamed all sparse infill to infill 2015-08-27 14:32:24 +02:00
Tim Kuipers 102fb24d74 bugfix: no infill visualization 2015-08-27 14:26:01 +02:00
Tim Kuipers 381bf1c3d2 bugfix: min area size for infill and skin were too large 2015-08-27 14:17:58 +02:00
Tim Kuipers 03b188aca5 bugfix: now always appending cura profile string 2015-08-27 12:32:10 +02:00
Tim Kuipers d584b19078 refcator: moved FffProcessor.h functions to cpp 2015-08-27 12:24:47 +02:00
Tim Kuipers df1b0372ff refactor: rename fffProcessor to FffProcessor 2015-08-27 12:20:51 +02:00
Tim Kuipers 73de8ac75b made fffProcessor singleton 2015-08-27 12:19:13 +02:00
Tim Kuipers 48464e9d13 feat: cura settings string to log and end of gcode (when not using commandSocket) 2015-08-27 11:15:53 +02:00
Tim Kuipers 51a9d1b44f feat/bugfix: implemented missing -g command line option (switch to meshgroup) 2015-08-27 10:27:06 +02:00
Tim Kuipers 481142ef4a lil cleanup 2015-08-26 16:20:34 +02:00
Tim Kuipers 2eb3d3438a Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-08-26 15:15:08 +02:00
Tim Kuipers eedda865bc feat: settings registry handles overrides; refactor: SettingConfig inherits from SettingsContainer (was SettingsCategory) 2015-08-26 15:14:59 +02:00
Tim Kuipers d9195c276f settingRegistry handles json document inheritance 2015-08-26 15:11:56 +02:00
Tim Kuipers 5accc6adf8 feat: json settings loader now uses recursion to load other json files 2015-08-26 11:34:18 +02:00
Tim Kuipers f7fde50e73 print call when erroneous cpommand line parsing 2015-08-26 10:31:51 +02:00
Tim Kuipers 765b8be00b bugfix: retraction at end of gcode! 2015-08-26 09:57:18 +02:00
Tim Kuipers 756af8601c feat: infill before walls 2015-08-25 16:18:39 +02:00
Tim Kuipers 21661f2f4b lil 2015-08-25 14:36:35 +02:00
Tim Kuipers 1ffa57f740 enabled support roof configurable pattern and line distance 2015-08-25 13:29:39 +02:00
Tim Kuipers 158b944b76 bugfix: concentric dense infill never called 2015-08-25 13:29:07 +02:00
Tim Kuipers cce2efd419 refactor: fill of any sort (skin, support, infill) moved to infill.cpp 2015-08-25 13:09:19 +02:00
Tim Kuipers 1853a41117 made polygonUtils work on const polygons 2015-08-25 13:08:39 +02:00
Tim Kuipers 69412eda52 bugfix: support tried to message polygons while support wasnt generated 2015-08-25 10:35:44 +02:00
Tim Kuipers 567c385e69 introduced generateInfill(pattern, ...) 2015-08-25 10:06:20 +02:00
Tim Kuipers e35f21ef7b bugfix: disable retraction (set to 0) when retraction not enabled 2015-08-24 17:19:04 +02:00
Tim Kuipers ee8b2ed39c bugfix: disable retraction (set to 0) when retraction not enabled 2015-08-24 17:18:33 +02:00
Tim Kuipers 7d93688b9b removed allways_retract and moved some calls to setCombing to the right place 2015-08-24 17:14:34 +02:00
Tim Kuipers 8daa32f757 bugfix: layer height info sent before beginSendSlicedObject called 2015-08-24 16:15:19 +02:00
Tim Kuipers cf05eadfaa polygon settings loading skipped (temp fix) 2015-08-24 13:55:02 +02:00
Tim Kuipers 70093fdb5a feat/bugfix: z seam placement: back,random or shortest (not yet the aligned) 2015-08-24 12:03:44 +02:00
Tim Kuipers 1a7dec654e temp file recording the setting changes between master and 15.06 2015-08-24 10:07:55 +02:00
Tim Kuipers 24f41fe419 refactored calculateMeshorder so that it returns indices instead of the meshes, debug code for z seam in FffGcodeWriter::processInsets 2015-08-20 17:07:31 +02:00
Tim Kuipers 97e89e1a94 bugfix: moveInside didn't want to move inside by default 2015-08-20 16:51:50 +02:00
Tim Kuipers 913f955de8 refactor: moved all polygonUtils into class PolygonUtils with all static methods 2015-08-20 14:24:23 +02:00
Tim Kuipers a2fa27603b simplify: decoupled error_dist from smallest_line_length 2015-08-20 14:10:11 +02:00
Tim Kuipers 9d313910f4 removed debug test main 2015-08-20 14:09:35 +02:00
Tim Kuipers eacc87d435 bugfix: simplify would in some cases move the end point of a long line if it is followed by a lot of small segments 2015-08-20 12:46:37 +02:00
Tim Kuipers 57f276d07e bugfix: simplify would in some cases move the end point of a long line if it is followed by a lot of small segments 2015-08-20 12:36:39 +02:00
Tim Kuipers dd638c32e5 bugfixes: now fully removed polygonOptimizer and finished getDist2FromLineSegment 2015-08-20 12:22:39 +02:00
Tim Kuipers e02ed7c928 removed function [Polygons simplified(args..)] and left [void simplify(args..)] 2015-08-20 12:21:09 +02:00
Tim Kuipers c0007b156f improved getDist2FromLineSegment: may have extra output parameter 2015-08-20 12:16:21 +02:00
Tim Kuipers 2c1f4cf50e removed polygonOptimizer 2015-08-20 12:02:31 +02:00
Tim Kuipers 28612e060c replaced optimizePolygons by simplify 2015-08-20 12:01:40 +02:00
Tim Kuipers 094370db73 refactor: made LinearAlg2D a class with static methods and moved getClosestOnLineSegment to it 2015-08-20 11:22:44 +02:00
Tim Kuipers ecb6b7c914 have doxygen generate dependency graphs etc. 2015-08-20 10:50:00 +02:00
Tim Kuipers 51b2d390c5 includes cleanup main.cpp 2015-08-20 10:46:40 +02:00
Tim Kuipers be157338ad buggy simplify improvement; bug = circular include? 2015-08-20 10:09:29 +02:00
Tim Kuipers 2c54394cd0 removed polygondebug > moved to SVG.h 2015-08-19 14:27:10 +02:00
Tim Kuipers 141f29bc18 centralized SVG output file (debug) 2015-08-19 14:25:59 +02:00
Tim Kuipers 28b27c971e bugfix: AABB perfomred totally unnecessary copies! 2015-08-19 12:23:41 +02:00
Tim Kuipers 7efc41e218 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-08-19 11:12:33 +02:00
Tim Kuipers 7145023b6f lil 2015-08-19 11:12:24 +02:00
Tim Kuipers 8797157351 bugfixes: combing: nor combing during raft, skirt, support, etc. ; not trying to move inside comb boundary when not combing ; skip some trying-to-comb for some travel moves by calling directly addTravel_simple instead of addTravel 2015-08-19 11:11:34 +02:00
Arjen Hiemstra f999e1b75f Extend per-object setting support to MeshGroup 2015-08-18 18:20:12 +02:00
Tim Kuipers 5876964d41 refactor: fill_ > infill_ 2015-08-18 11:49:08 +02:00
Tim Kuipers 61ff30772c bugfix: skin used perimeter overlap; feature: move inside after each layer part 2015-08-17 14:43:38 +02:00
Tim Kuipers e6453082bf bugfix: UMO bed temp error when there is no heated bed 2015-08-14 17:17:32 +02:00
Tim Kuipers c2038f7408 coasting config per mesh 2015-08-14 16:16:17 +02:00
Tim Kuipers ff5a1d82f8 comb offset not depending on general wall line width anymore 2015-08-14 15:11:56 +02:00
Tim Kuipers 1b13dfca97 fixed outer wall width and placement and distinguished between outer wall smaller or larger than nozzle size 2015-08-14 13:48:09 +02:00
Tim Kuipers fed536b079 fixed outer wall width and placement 2015-08-14 12:45:22 +02:00
Tim Kuipers 3cf74b5624 initial_layer_speed per mesh 2015-08-14 12:29:58 +02:00
Tim Kuipers 52a57b1499 moved retractionMinimalDistance, retraction_extrusion_window, retraction_count_max into RetractionConfig 2015-08-14 11:55:02 +02:00
Tim Kuipers 831266db31 retraction config per mesh, retraction config per extruder 2015-08-13 17:01:20 +02:00
Tim Kuipers 5fdb56966f get some settings per mesh instead of from general settings 2015-08-13 13:35:21 +02:00
Tim Kuipers eae4c507a4 optimization: no mesh offset is offset is zero 2015-08-13 13:33:36 +02:00
Tim Kuipers 92b68c5745 mesh positioning retrieved per object 2015-08-13 13:33:04 +02:00
Tim Kuipers 9ade99a9c7 renamed extruder_switch_retract settings and support_extruder_nr_layer_0 2015-08-13 10:31:06 +02:00
Tim Kuipers 097c390fab enabled print time again 2015-08-13 08:48:37 +02:00
Tim Kuipers f982202aa4 bugfix: extruder offset enabled when should have been disabled, and vice versa! (The heat is getting to me) 2015-08-12 15:36:39 +02:00
Tim Kuipers c3f474e229 Revert "refactor: pulled writePath out of GCodePlanner::writeGCode"
This commit introduced major bugs.

This reverts commit 125af2915f.
2015-08-12 15:19:23 +02:00
Tim Kuipers 125af2915f refactor: pulled writePath out of GCodePlanner::writeGCode 2015-08-12 12:31:35 +02:00
Tim Kuipers 7b26d08d25 lil optimization: earlier travel path gcode generation 2015-08-12 12:20:44 +02:00
Tim Kuipers a2b631b61b bugfix: infill lines merger ignored travel path before it 2015-08-12 12:16:30 +02:00
Tim Kuipers e3df22dab9 combing debug, bugfixes, enhancements 2015-08-12 11:27:53 +02:00
Tim Kuipers 7ad1b32ef3 feat: optimization: one less copy if inset is already computed 2015-08-11 17:21:48 +02:00
Tim Kuipers 98a7c65d66 bugfix: no return when GCodePlanner::makeRetractSwitchRetract reached end of all paths in gcode_layer 2015-08-11 16:15:38 +02:00
Tim Kuipers b60ad09882 bugfix: combing crossed perimeters due to starting points being too close to a perimeter; they weren't moved inside the polygons 2015-08-11 13:43:38 +02:00
Tim Kuipers 0f35450440 undoing last lil refactor, was erroneous 2015-08-11 13:17:47 +02:00
Tim Kuipers 0adc1b44e7 lil refactor 2015-08-11 11:59:28 +02:00
Tim Kuipers 8d084e727a bugfix: when skirt got doubled the second skirt line wasnt a convex hull 2015-08-11 11:25:15 +02:00
Tim Kuipers 916005b6ad feat: better conical support; new settings: support_conical_min_width, support_conical_angle 2015-08-10 16:08:57 +02:00
Tim Kuipers c7ecc23053 feat: disable xy-shifted gcode: machine_use_extruder_offset_to_offset_coords 2015-08-10 15:20:30 +02:00
Tim Kuipers 9d36987124 bugfix: disable wipe when printing prime tower with same nozzle (no nozzle switch) 2015-08-10 15:19:28 +02:00
Tim Kuipers 0fa3cab356 feature: support expansion offset 2015-08-10 14:09:53 +02:00
Tim Kuipers 31226c0b68 feat: no wipe when prime tower isn't after a nozzle switch 2015-08-10 12:08:40 +02:00
Tim Kuipers ca23a77ad0 bugfix: MergeInfillLines caused infinite speed when lines were in each others extension 2015-08-10 11:52:57 +02:00
Tim Kuipers b052cd431c bugfix: nozzle switch retractions at wrong places and too many; bugfix: removed unnecessary swaps when seconf mesh is empty at a layer 2015-08-10 10:56:08 +02:00
Tim Kuipers f00cb00f52 make last retraction of a nozzle a nozzle switch retraction 2015-08-07 16:59:41 +02:00
Tim Kuipers f236969909 bugfixes: double prime tower during skirt; wrong support order; sliceDataStorage.meshes reallocation 2015-08-07 15:30:57 +02:00
Tim Kuipers 70dde36d85 bugfix: extruder train settings overrided general setting defaults; now they have their own category in the JSON file 2015-08-06 17:14:10 +02:00
Tim Kuipers 52f8a7500e lil bugfixes: getting settings via mesh instead of general settings 2015-08-06 15:37:58 +02:00
Tim Kuipers 710138f487 different prime tower pattern 2015-08-06 15:36:32 +02:00
Tim Kuipers f7e3682c35 multi extrusion skirt 2015-08-06 13:52:42 +02:00
Arjen Hiemstra 4fca97352d Fix building on OSX since hash_fun.h does not exist there
The right header is std::hash is located in <functional>
2015-08-06 10:20:07 +02:00
Tim Kuipers 10773b688e readme 2015-08-05 14:49:21 +02:00
Tim Kuipers aae2aad42c new command line arguments handling 2015-08-05 14:45:05 +02:00
Tim Kuipers 79d36d9a49 bugfix: retraction count max; feature: switch positions 2015-08-05 14:17:47 +02:00
Tim Kuipers 96d64b325a gcodeExport: refactored all per extruder settings 2015-08-05 09:47:22 +02:00
Tim Kuipers a316302314 refactor: wire print now retrieves settings from the meshgroup 2015-08-05 09:46:46 +02:00
Tim Kuipers 93b0aad9f0 extruder train defaults per extruder train in fdmPrinter.json 2015-08-04 16:44:46 +02:00
Tim Kuipers 1df22b3cab bugfix: partsView declared as struct instead of class 2015-08-04 12:01:20 +02:00
Tim Kuipers 0a3da66539 bugfix: prime tower fixes maybe: insets 2015-07-31 17:41:59 +02:00
Tim Kuipers 45674d0b1c enhancement: prime tower improvements 2015-07-31 17:37:09 +02:00
Tim Kuipers 834a778f93 refactor: moved prime tower to PrimeTower.h and .cpp 2015-07-31 17:02:24 +02:00
Tim Kuipers 160612f0dc bugfix: setting inheritance structure is now fffProcessor>MeshGroup>ExtruderTrain>Mesh instead of fffProcessor>ExtruderTrain>MeshGroup>Mesh; feat: per object settings command line parsing, per meshgroup command line settings, per extruder train command line settings 2015-07-31 13:20:53 +02:00
Tim Kuipers 0cd463820f SliceDataStorage is now a SettingsMessenger instead of a SettingsBase 2015-07-31 13:18:56 +02:00
Tim Kuipers 73f3995443 include all files in namespace cura and standardize spelling 2015-07-31 13:16:24 +02:00
Tim Kuipers 72762d9db5 SliceDataStorage is now a SettingsMessenger instead of a SettingsBase 2015-07-31 13:15:44 +02:00
Tim Kuipers 22cbeb7c0a bugfix: virutal constructor of SettingsBaseVirtual 2015-07-31 13:14:50 +02:00
Tim Kuipers 819206afff constructors of classes extending SettingsBase now get a SettingsBaseVirtual as argument for their parent 2015-07-31 13:14:08 +02:00
Tim Kuipers 9121c5440d include all files in namespace cura and standardize spelling 2015-07-31 12:41:04 +02:00
Tim Kuipers 587121c58d bugfix: incorrectly escaped % in printf 2015-07-31 12:40:07 +02:00
Tim Kuipers 5d5912f8c2 refactor: merged the two AABB classes and renamed one to AABB3D 2015-07-31 12:33:00 +02:00
Tim Kuipers efd88d4c84 refactor: merged the two AABB classes and renamed one to AABB3D 2015-07-31 12:29:33 +02:00
Tim Kuipers c994d830fa removed empty ExtruderTrain.cpp 2015-07-31 12:26:32 +02:00
Tim Kuipers 186bdb50da per object settings 2015-07-30 17:35:34 +02:00
Tim Kuipers 2b418030bb split up main into slice() and connect() corresponding to CuraEngine slice [options] and CuraEngine connect [options] 2015-07-30 16:52:04 +02:00
Tim Kuipers 874465b450 more commandSocket clarity 2015-07-30 16:39:42 +02:00
Tim Kuipers 7089a2d72a SettingsBase split up into SettingsBaseVirtual, SettingsBase and SettingsMessenger, which only passes on settings from a parent 2015-07-30 16:37:38 +02:00
Tim Kuipers 3d7309bb3d more commandSocket clarity 2015-07-30 16:36:55 +02:00
Tim Kuipers 2f355d1263 PrimeTower.h .cpp (forgotten files), ExtruderTrain.h .cpp 2015-07-30 15:04:13 +02:00
Tim Kuipers 478c667655 different command line calls: CuraEngine slice [options] -l file OR CuraEngine connect 2015-07-30 15:03:21 +02:00
Tim Kuipers 0d07f3b9b0 temp fix: instead of sometimes reporting negative print times, always reeport a print time of 10 2015-07-30 15:01:30 +02:00
Tim Kuipers d7d3fd47f7 refactor: PrintObject => MeshGroup 2015-07-30 11:47:53 +02:00
Tim Kuipers 244a90e4eb refactoring: getting clarity on PrintObject vs Mesh 2015-07-30 11:41:45 +02:00
Tim Kuipers ec5680e2ea halfway refactoring prime tower into its own file 2015-07-30 10:44:52 +02:00
Tim Kuipers f9e562e020 removed unnecessary swaps when usign a prime tower 2015-07-30 09:29:09 +02:00
Tim Kuipers e7379efaf3 lil bugfix: support max height etc; note that unnecessary swaps don't occur anymore, but wipe tower is not added 2015-07-29 16:42:33 +02:00
Tim Kuipers 576d90806d feat: prime_tower_wipe_enabled, refactored all wipe_tower to prime_tower 2015-07-29 16:14:25 +02:00
Tim Kuipers dcedf97493 bugfix: max wipe tower height when using support roofs 2015-07-29 16:09:57 +02:00
Tim Kuipers 76af340433 bugfixes for support_roof_extruder_nr 2015-07-29 15:41:37 +02:00
Tim Kuipers 24fdcd0355 support_roof_extruder_nr 2015-07-29 14:38:58 +02:00
Tim Kuipers 3f6f35bd95 support_roof_extruder_nr 2015-07-29 14:35:15 +02:00
Tim Kuipers b41288c63d adhesion_extruder_nr 2015-07-29 13:13:18 +02:00
Tim Kuipers 75b13046cf lil changes 2015-07-29 12:20:57 +02:00
Tim Kuipers 2433c8521e logWarning 2015-07-29 12:20:14 +02:00
Tim Kuipers 613e65ecba machine_extruder_count 2015-07-28 15:19:55 +02:00
Tim Kuipers 6a7d9a7c18 machine_extruder_count 2015-07-28 15:18:28 +02:00
Tim Kuipers 6e52f81840 combine infill lines finally bugfixed! 2015-07-28 13:29:13 +02:00
Tim Kuipers 7984932307 bugfix: skirt includes support roofs 2015-07-27 16:18:30 +02:00
Tim Kuipers 871fdc3a01 different wipe tower position, closer to far right corner for UM2 2015-07-27 16:17:52 +02:00
Tim Kuipers 807e734fa4 bugfix: wipe_tower inverse order on first layer 2015-07-27 13:04:30 +02:00
Tim Kuipers b59ebe074d Update README.md 2015-07-27 11:11:59 +02:00
Tim Kuipers 81a3b14999 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-24 17:17:18 +02:00
Tim Kuipers a1a3c079bb settings wipe_tower_dir_outward ::bool 2015-07-24 17:14:14 +02:00
Tim Kuipers 33d5c91227 reverse the print order of the wipe tower skirt to inward 2015-07-24 16:55:28 +02:00
Arjen Hiemstra d43774c664 Merge branch '15.06'
* 15.06: (21 commits)
  bugfix: empty arcus messages
  bugfix & feature: insets support smaller outer wall line width
  Statically link libstdc++ on Linux to prevent issues with newer libstc++
  bugfixes for raft problems: unregistered settings, printZ computed doubly, printZ didn't account for difference in initial slice Z and print Z, initial layer height should be appleid when using raft, wrong (negative) layer comments
  bugfix: extrusion per mm to extrusion mm3 per mm is_volumetric set
  bugfix: speed compensation for stable feedrate also changed the extrusion per mm
  bugfix: speed compensation for stable feedrate also changed the extrusion per mm
  bugfix+refactor: extrusion always per mm3
  bugfix: no support for too low models
  bugfix: no support for too low models
  Also send infill polygon data to the UI, not just oultines
  Also send line width to the UI along with the polygon's points
  Add support for sending layer height and thickness to the UI
  Properly reset the extrusion amount between slicing tasks.
  Properly initialize extrusion_volume_per_mm variable
  bugfix: reset filament at new print
  Add missing <string> include so we can build on MacOSX
  Install the created executable
  bugfix gcode.writeMove(x,y,z) where z didn't get processed. = bug for wireprinting
  Fix support generation by making sure "True" is also recognised as true
  ...
2015-07-24 12:44:00 +02:00
Tim Kuipers 6feb5bbe8c Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-24 12:02:31 +02:00
Tim Kuipers ed15bdcc25 bugfixes: progress 2015-07-24 12:01:37 +02:00
Jaime van Kessel 95bbf211ba progress now takes multiple objects for once at a time into account 2015-07-24 11:50:08 +02:00
Jaime van Kessel bb2138b060 bit of refactoring in slices2polygons 2015-07-24 10:55:40 +02:00
Jaime van Kessel debe1c5129 Renamed namespace of protobuff from Cura to cura::proto 2015-07-24 10:06:13 +02:00
Jaime van Kessel 157f0c37f8 fixed one at a time 2015-07-24 09:25:56 +02:00
Jaime van Kessel cac5ee612c Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-24 09:05:48 +02:00
Tim Kuipers a931f2f964 bugfix: no more automatic mesh centering (repositioning); now an option: center_object 2015-07-23 18:06:53 +02:00
Tim Kuipers 6450379a1c feature: conical support half implemented 2015-07-23 16:25:24 +02:00
Tim Kuipers 24ece43700 feature: support_extruder_nr_layer_0 2015-07-23 16:19:02 +02:00
Jaime van Kessel ff59738246 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-23 10:19:42 +02:00
Jaime van Kessel b3de3b7643 Changes to make one at a time work (WIP) 2015-07-23 10:19:23 +02:00
Tim Kuipers 1523350501 UM2 gcode now accepts start gcode (though setting default should be empty) 2015-07-23 10:13:58 +02:00
Tim Kuipers 1a5fd99d7e better starting and ending gcode for wireframe 2015-07-23 09:15:54 +02:00
Tim Kuipers 55ee8f687b Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-22 16:17:16 +02:00
Tim Kuipers 265adb1a5d bugfix/enhancement: better combine infill lines 2015-07-22 16:17:02 +02:00
Jaime van Kessel 235c561bb5 Renamed modelfile to correct name 2015-07-22 15:49:54 +02:00
Tim Kuipers 1753bd566e merge 2015-07-22 15:36:09 +02:00
Jaime van Kessel 5e8c73a0a2 Moar naming 2015-07-22 15:21:44 +02:00
Jaime van Kessel 5752b358f2 More refactoring 2015-07-22 13:47:16 +02:00
Jaime van Kessel 10d4785659 refactoring 2015-07-22 13:34:24 +02:00
Jaime van Kessel 9beaee7161 Added more documentation 2015-07-22 13:31:02 +02:00
Jaime van Kessel c79e052c17 Refactoring so code adheres to codestyle 2015-07-22 13:18:18 +02:00
Tim Kuipers 5b88fda464 bugfix: removed zigzaggifying infill lines 2015-07-22 12:11:23 +02:00
Tim Kuipers 52cd83bac3 bugfixes: combine infill lines 2015-07-22 12:06:18 +02:00
Tim Kuipers a47adb2114 bugfix: support overlapped with model by half the line width 2015-07-22 10:08:34 +02:00
Tim Kuipers e0001ffa5b enhancement: less copies in polygons.removeDegenerateVerts 2015-07-22 09:31:06 +02:00
Tim Kuipers 58734899d7 enhancement: less copies in polygons.removeDegenerateVerts 2015-07-21 17:24:41 +02:00
Tim Kuipers 60b0f43838 bugfix/feature enhance: better combine infill lines into single line 2015-07-21 16:25:53 +02:00
Tim Kuipers 4bed0e3ad1 bugfix: draft shield disabled skirt; bugfix: infill config was retrieved globally instead of local to the mesh 2015-07-21 13:34:22 +02:00
Tim Kuipers 0e27cbfd4f lil draft shield improvement: more skip_layers 2015-07-20 14:59:32 +02:00
Tim Kuipers 94c29cf0af Merge pull request #222 from electrocbd/bugfix2
Infinite loop in cura::FffPolygonGenerator::processDraftShield
2015-07-20 14:58:09 +02:00
Tim Kuipers 7d1d60b422 bugfix: remove degenerate verts: verts connected to overlapping line segments 2015-07-20 14:37:21 +02:00
Christophe Baribaud 7fca86e27a Keep compatibility with 0.2mm layer height 2015-07-20 14:34:52 +02:00
Christophe Baribaud faeaf2816f Infinite loop in cura::FffPolygonGenerator::processDraftShield 2015-07-20 14:15:18 +02:00
Tim Kuipers 9943279361 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-20 09:11:28 +02:00
Tim Kuipers 58b2d42596 Merge pull request #221 from electrocbd/bugfix1
bugfix: Polygons::splitIntoPartsView_processPolyTreeNode had reference to vector element which could change within the recursive call
2015-07-19 22:42:41 +02:00
Christophe Baribaud c42df2e53e Fix: Polygons::splitIntoPartsView_processPolyTreeNode used a pointer to a
vector which is reallocated, invalidating iterators and pointers
2015-07-19 15:21:06 +02:00
Tim Kuipers 3607a88f0a bugfix: (again) speed and extrusion/mm for combined small moves: for moves smaller than nozzle width, speed should be higher and extrusoin/mm lower 2015-07-15 17:13:42 +02:00
Tim Kuipers 8625365cf0 changed fill_overlap dewfault down; new feature: infill_wipe_dist 2015-07-09 16:53:28 +02:00
Tim Kuipers 941354c740 feat: infill_wipe_dist 2015-07-09 16:48:28 +02:00
Tim Kuipers 9ba97a7cd7 reqrote some comments 2015-07-09 11:37:52 +02:00
Tim Kuipers f9bf42f610 feature: draft protection screen 2015-07-08 17:02:30 +02:00
Tim Kuipers ce4a375780 disabled skirt when draft shield is on 2015-07-08 15:55:32 +02:00
Tim Kuipers f0675c88df feature: draft protection screen 2015-07-08 15:20:20 +02:00
Tim Kuipers 0d0dd7292a bugfix & feature: insets support smaller outer wall line width 2015-07-08 13:00:47 +02:00
Tim Kuipers 735f50875e bugfix overlap ocmpensation: removed debug output code 2015-07-07 16:58:28 +02:00
Tim Kuipers f0fd1a654e bugfix for wall overlap compensation (unknown bug origin, but seems fixed by keeping endings separate from normal overlap points). CONTAINS DEBUG output code. 2015-07-07 16:52:35 +02:00
Tim Kuipers 82532a3ddf fix lil 2015-07-03 14:22:39 +02:00
Tim Kuipers 7075ca34d4 bugfixes for raft problems: unregistered settings, printZ computed doubly, printZ didn't account for difference in initial slice Z and print Z, initial layer height should be appleid when using raft, wrong (negative) layer comments 2015-07-03 13:23:23 +02:00
Tim Kuipers 8c9625f578 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-07-01 17:19:26 +02:00
Tim Kuipers 7c4064aa5f travel_compensate_overlapping_walls_enabled fixed 2015-07-01 17:19:16 +02:00
Tim Kuipers 6d82284545 Update README.md 2015-07-01 10:34:23 +02:00
Tim Kuipers fe512222e5 bugfix: extrusion per mm to extrusion mm3 per mm is_volumetric set 2015-06-30 17:20:02 +02:00
Tim Kuipers a18cfd9af1 halfway bugfix+refactor: extrusion per mm to extrusion mm3 per mm 2015-06-30 16:16:02 +02:00
Tim Kuipers 7469a48449 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-06-30 15:38:55 +02:00
Tim Kuipers 9c8bac7a99 halfway bugfix+refactor: extrusion per mm to extrusion mm3 per mm 2015-06-30 15:38:53 +02:00
Tim Kuipers 9baaa93159 bugfix: speed compensation for stable feedrate also changed the extrusion per mm 2015-06-30 14:47:40 +02:00
Tim Kuipers 9b92c42947 bugfix: speed compensation for stable feedrate also changed the extrusion per mm 2015-06-30 14:46:26 +02:00
Tim Kuipers 0a77aaa385 overlap compensation 2015-06-30 09:31:57 +02:00
Roy Spliet a54e70e48f CMake: Add packaging definition
Signed-off-by: Roy Spliet <r.spliet@ultimaker.com>
2015-06-26 13:57:19 +02:00
daid d3e41f3ce2 Add missing rapidjson license. 2015-06-26 11:13:47 +02:00
Tim Kuipers a9b4aa7640 bugfix: no more removal of last point (caused by overwriting each point by the last) 2015-06-24 17:15:26 +02:00
Tim Kuipers 95ff26940e bugfix: disabled overlap compensation due to temporary bug 2015-06-24 16:33:54 +02:00
Tim Kuipers e5f87bcbc7 bugfixes: hasSetting removed due to new setting system introduced months ago, mesh positioning bugfix 2015-06-24 16:22:30 +02:00
Tim Kuipers 210636e205 lil 2015-06-24 15:32:27 +02:00
Tim Kuipers 028c8c6e42 no more debug out bs 2015-06-24 15:00:25 +02:00
Tim Kuipers 73f5691651 bugfix: nozzle offset 2015-06-24 15:00:09 +02:00
Tim Kuipers ddc2c1e574 dgf 2015-06-24 14:27:26 +02:00
Tim Kuipers fcbcf380ae Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-06-24 13:14:25 +02:00
Tim Kuipers eccda61b55 bugfix: machine_extruder_offset >> machine_nozzle_offset 2015-06-24 13:14:15 +02:00
Tim Kuipers 0f8b52d1a0 overlap compensation halfway 2015-06-24 13:12:17 +02:00
Tim Kuipers 35373a5b1a overlap compensation half way : location based 2015-06-23 13:45:38 +02:00
Tim Kuipers 094d0e84a2 bugfix: no support for too low models 2015-06-23 10:11:09 +02:00
Tim Kuipers a5beedf4f6 bugfix: no support for too low models 2015-06-23 10:07:47 +02:00
Tim Kuipers a77f0bf728 first wall overlap compensation approximation (links a point to multiple overlaps to account for threeway overlaps) 2015-06-22 12:22:28 +02:00
Ghostkeeper 8a75b638b7 Restore documentation output directory
The output directory had to be changed back to docs for the automatic
generation of documentation.
2015-06-19 10:24:24 +02:00
Ghostkeeper 0815a834d8 Emptied the documentation output directory
This output directory needs to be empty in order to automatically
publish the documentation.
2015-06-18 16:12:19 +02:00
Tim Kuipers 93c9864f17 split remove_overlapping_perimeters into outer and other 2015-06-18 13:25:05 +02:00
Tim Kuipers 2444667a70 lil 2015-06-18 10:06:48 +02:00
Tim Kuipers 0ec956c653 enhanced support roof direction 2015-06-17 15:03:28 +02:00
Tim Kuipers ccd1c44315 stop wipe tower after last neccesary switch (+bugfixes) 2015-06-17 14:56:00 +02:00
Tim Kuipers bed85c7e68 stop wipe tower after last neccesary switch 2015-06-16 17:26:54 +02:00
Tim Kuipers 4e40b2bc09 support roof lil stuff 2015-06-16 14:42:27 +02:00
Tim Kuipers 6fbe9c7de8 bugfix: setting typo 2015-06-16 12:36:35 +02:00
Tim Kuipers 5b70a57700 documenting AABB 2015-06-16 12:11:33 +02:00
Tim Kuipers 1ce42b6b93 lil 2015-06-16 12:07:20 +02:00
Tim Kuipers e5b79ea847 lil 2015-06-16 12:06:58 +02:00
Tim Kuipers 70168cde07 introduced AABB (axis aligned bounding box) and cura'd a related bug 2015-06-16 12:04:34 +02:00
Tim Kuipers 7dad3d5018 different command line execution example 2015-06-16 11:25:02 +02:00
Tim Kuipers 87fbaf51f9 different command line execution example 2015-06-16 11:23:10 +02:00
Tim Kuipers 5cf401cde7 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-06-16 11:18:31 +02:00
Tim Kuipers 136aea0c15 support roof settings 2015-06-16 11:18:18 +02:00
Tim Kuipers 8e43531e10 more lenient settings handling so that you can load multiple json files after eachother 2015-06-16 11:16:36 +02:00
Ghostkeeper 7ac77ba24b Readme correction: There is an external dependency
The external dependency is libArcus. A reference to libArcus is added in
the place where it said there are no external dependencies.
2015-06-12 13:45:25 +02:00
Tim Kuipers 1406c8f2ea magic_polygon mode => magic_mesh_surface_mode 2015-06-11 14:32:55 +02:00
Tim Kuipers c0715fd490 first approximation support roofs 2015-06-11 13:57:23 +02:00
Tim Kuipers 267e0b1fa9 LARGE REFACTOR: all variables which eventually get values from double settings have been changed from int to double 2015-06-10 11:19:37 +02:00
Tim Kuipers dcf56c5a8e new feature: alternate skin rotation (45 degrees for every two pairs of layers 2015-06-09 17:24:47 +02:00
Tim Kuipers bf23ca38ba bugfix: support line width, Fixes daid/Cura#1274 2015-06-09 14:58:05 +02:00
Tim Kuipers b851be6a06 progress takes care of timeKeeper 2015-06-09 14:31:46 +02:00
Tim Kuipers 84b3176ac3 progress docs and sendProgressStage 2015-06-09 13:41:33 +02:00
Tim Kuipers b9f8325534 better progress calculation 2015-06-09 13:09:30 +02:00
Tim Kuipers 9fa902ad6d license correction 2015-06-09 13:09:12 +02:00
Tim Kuipers 68888fc0b7 license correction 2015-06-09 13:07:41 +02:00
Tim Kuipers 136e199225 lil 2015-06-09 13:06:43 +02:00
Tim Kuipers 295e51309f bugfix: incorrect model translation upward 2015-06-08 16:57:54 +02:00
Tim Kuipers 5f09352a24 bugfix: retract on very first move, causing the head to move to an impossible position 2015-06-08 15:24:35 +02:00
Tim Kuipers 94faff843f Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-06-08 13:33:53 +02:00
Tim Kuipers 2efce629f7 coasting finished + settings 2015-06-08 13:24:09 +02:00
Tim Kuipers 6964c7122b removed move_inside_comb_boundary, since that is already dont by the combing 2015-06-08 10:04:42 +02:00
Tim Kuipers 6b97779ac8 Update README.md 2015-06-08 09:23:50 +02:00
Tim Kuipers 5d9c64a140 differentiate between move-coasting and retract-coasting 2015-06-05 12:41:19 +02:00
Tim Kuipers 5ca046fec2 differentiate between move-coasting and retract-coasting 2015-06-05 12:40:31 +02:00
Tim Kuipers 5657577938 moved coasting to gcodePlanner.writeGcode() and introduced coasting priming 2015-06-05 12:14:42 +02:00
Tim Kuipers 224fec0b79 first coasting code part and gcodeLayer.writeGcode refactors 2015-06-03 15:44:36 +02:00
Tim Kuipers ae2b67ef69 lil 2015-06-03 13:05:38 +02:00
Tim Kuipers 3b8ad5e55c added setting travel_avoid_other_parts 2015-06-03 13:04:00 +02:00
Tim Kuipers abd2c586db moved fdmPrinter.json to the frontend (merged) 2015-06-02 17:09:35 +02:00
Tim Kuipers 0a8b508b4c lil 2015-06-02 11:45:30 +02:00
Tim Kuipers df97f36315 better retraction handling 2015-06-02 11:43:10 +02:00
Tim Kuipers cc3ce8ae6b add setting travel_avoid_distance 2015-06-02 11:32:29 +02:00
Tim Kuipers ba2f47aa67 Merge branch 'masterhou-master-hyt' 2015-06-01 17:10:37 +02:00
Tim Kuipers e516682744 corrected wrong initialization in polygon.cpp 2015-06-01 16:07:25 +02:00
Tim Kuipers f43f4285a8 Merge branch 'master-hyt' of https://github.com/masterhou/CuraEngine into masterhou-master-hyt 2015-06-01 15:59:15 +02:00
Tim Kuipers 99c756aa90 lil 2015-06-01 14:15:45 +02:00
Tim Kuipers a8a2d69a5f better comb inside offset, don't use comb path when zhop is used. 2015-06-01 12:32:45 +02:00
Tim Kuipers f7ac889bb4 more retractions between support (but not always) 2015-06-01 11:26:05 +02:00
Tim Kuipers a326e73e4a cleaning up combing, explanations, comments 2015-06-01 11:23:49 +02:00
Thomas Hou 2796103a2b some optimization
1.load stl
2.use std::unordered_map instead of std::map
3.slicer, polygon close, find face by segment idx.
4.insert <k,v> fater than [k]=v;
2015-05-29 16:39:16 +08:00
Tim Kuipers 14c4ac55f1 Merge branch 'combingOverhaul' 2015-05-28 16:02:55 +02:00
Tim Kuipers 831b9d94f8 combing cleanup etc 2015-05-28 15:59:55 +02:00
Tim Kuipers 5fa793ece7 lil 2015-05-28 14:20:55 +02:00
Tim Kuipers 4065e6f7e8 refactor combing: no more force retraction, no more boundary with extra offset 2015-05-28 14:13:05 +02:00
Tim Kuipers 7ff00dfbb0 combing seems to be working 2015-05-26 17:04:26 +02:00
Tim Kuipers 84909e2e77 Merge pull request #211 from foosel/fix/usage_summary
Usage summary should match actual usage
2015-05-26 15:25:28 +02:00
Gina Häußge 9d75cdf0f7 Usage summary should match actual usage
Removed obsolete `-m` and `-c` and added missing
`--connect`, `-j` and `-p`.
2015-05-26 15:00:06 +02:00
Tim Kuipers c0c842649f lil commit 2015-05-26 12:26:42 +02:00
Tim Kuipers e8d117d62f UltiGCode starting gcode fallback option when not using command sockets 2015-05-26 11:50:24 +02:00
Tim Kuipers 87ae0dc870 bugfix for boundary point with offset for outside combing 2015-05-26 11:44:27 +02:00
Tim Kuipers e26ee1a113 debugging, testing 2015-05-26 11:12:08 +02:00
Tim Kuipers 9d31a525c9 UltiGCode starting gcode fallback option when not using command sockets 2015-05-26 10:56:26 +02:00
Tim Kuipers ffb08f9f8d more cdombing overhaul (debug state) 2015-05-26 10:46:23 +02:00
Tim Kuipers 294dec007a Merge pull request #210 from osbornm/patch-1
Update README to display build steps as list
2015-05-26 10:32:01 +02:00
Matthew Osborn 93a19422e8 Update README to display build steps as list
This way the steps show up as a list and stand out so it's easier to see.
2015-05-25 22:37:26 -07:00
Tim Kuipers c44adab86d combing cleanup 2015-05-19 12:31:39 +02:00
Tim Kuipers 7bb5cbe98e first combing overhaul commit which seems to work 2015-05-19 10:52:04 +02:00
Tim Kuipers 0b0db9f864 comb boundary = whole layer 2015-05-18 13:33:03 +02:00
Arjen Hiemstra 64ddaf2ff5 Fix support generation by making sure "True" is also recognised as true 2015-05-18 11:11:59 +02:00
Tim Kuipers a901e88aaa bugfix gcode.writeMove(x,y,z) where z didn't get processed. = bug for wireprinting 2015-05-18 10:32:39 +02:00
Tim Kuipers fe03259704 more refactoring 2015-05-18 09:25:01 +02:00
Tim Kuipers cf209a157b first working comb 2015-05-15 13:30:43 +02:00
Tim Kuipers 0f547ae354 test cleanup 2015-05-15 13:30:19 +02:00
Tim Kuipers 2ebfbe837d includes cleanup 2015-05-15 13:29:58 +02:00
Tim Kuipers af87a00eaa bugfixes splitIntoPartsView 2015-05-15 13:27:28 +02:00
Tim Kuipers 09543636c7 more combing refactor 2015-05-13 17:08:09 +02:00
Tim Kuipers f5a56a5048 some new polygon functionality and linear algebra 2015-05-13 17:07:54 +02:00
Tim Kuipers 16185bd88c more combing refactor 2015-05-13 09:18:36 +02:00
Tim Kuipers 47479a9fd5 further combing refactoring 2015-05-12 17:24:49 +02:00
Tim Kuipers fce31b0c32 Merge branch 'master' into combingOverhaul 2015-05-12 15:29:03 +02:00
Tim Kuipers d44c68a28b further crystallized addMeshLayerToGcode 2015-05-12 15:18:21 +02:00
Tim Kuipers 5f793943f2 small refactor 2015-05-12 13:04:09 +02:00
Tim Kuipers 3d81f84c0f totally broke apart the writeGcode function 2015-05-12 12:48:10 +02:00
Tim Kuipers 9f59447a81 fff classes extend settingbase again, instead of having a settings field 2015-05-12 11:39:12 +02:00
Tim Kuipers 67c9b3dd23 totally broke apart what was formerly known as fffProcessor::prepareModel(.) 2015-05-11 14:50:46 +02:00
Tim Kuipers f1028b55a2 refactored and commented FffPolygonGenerator 2015-05-11 14:20:35 +02:00
Tim Kuipers 4cb11122dc renamed FffAreagenerator to FffPolygonGenerator 2015-05-11 14:01:34 +02:00
Tim Kuipers 58438bf56e renamed FffAreagenerator to FffPolygonGenerator 2015-05-11 13:59:44 +02:00
Tim Kuipers e93ba3d37f refactored some FffAreaGenerator functionality 2015-05-11 13:54:37 +02:00
Tim Kuipers 9566e60037 imports cleanup 2015-05-11 13:39:34 +02:00
Tim Kuipers b0b06635ac split off FffAreaGenerator from fffProcessor 2015-05-11 13:36:50 +02:00
Tim Kuipers 6dfacb7d2c rename GcodeWriter to FffGcodeWriter 2015-05-11 13:07:29 +02:00
Tim Kuipers f1774665df moved writeGcode to cpp file 2015-05-11 12:27:15 +02:00
Tim Kuipers 45c8439446 cleaned up some imports 2015-05-11 12:13:53 +02:00
Tim Kuipers 1d0fded682 split fffProcessor into fffProcessor and GcodeWriter 2015-05-11 12:03:07 +02:00
Tim Kuipers 31a81c5476 forgotten polygon.cpp 2015-05-11 09:27:52 +02:00
Tim Kuipers 229d3b1a2f halfway refactoring combing 2015-05-11 09:21:01 +02:00
Tim Kuipers 796ced3508 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-05-07 11:28:36 +02:00
daid c91137cc03 Merge branch '15.06' 2015-05-06 09:24:24 +02:00
Tim Kuipers d061420750 improved Comb::move inside 2015-05-04 17:23:03 +02:00
Tim Kuipers 50ef87d41f combing debug 2015-05-04 16:39:39 +02:00
Tim Kuipers c4952dd28f removed 'using default' msg 2015-05-04 16:39:22 +02:00
Tim Kuipers 6c5c5c2e34 removed comment 2015-05-04 16:38:24 +02:00
Tim Kuipers 1520e010e7 removed unneccesary import 2015-05-04 16:38:04 +02:00
Tim Kuipers 30bd8b5c67 support everywhere as default 2015-05-04 11:00:54 +02:00
Tim Kuipers 8cc54c60de refactored combing: one combing path per boundary polygon, needs more cleaning up and testing 2015-05-01 16:20:18 +02:00
Tim Kuipers a4a5e22b50 refactored combing more 2015-05-01 15:12:49 +02:00
Tim Kuipers 4529585acb made outlines into PolygonPart 2015-05-01 11:56:14 +02:00
Tim Kuipers d1bc5f8002 moved multiVolumeOverlap code from processSliceData to prepareModel, cause it should be done before layer parts are generated 2015-05-01 11:47:37 +02:00
Tim Kuipers ab68ee508e removed remove-first-empty-layers code which was old and at the wrong place 2015-05-01 11:21:36 +02:00
Tim Kuipers db7eb35e34 introduced a new class to signify that a Polygons object is a single area: PolygonsPart 2015-05-01 10:34:32 +02:00
Tim Kuipers 216ab79521 bit more explanation findSmallestConnection 2015-05-01 09:48:48 +02:00
Tim Kuipers 2a74c39642 removed slow findSmallestConnection 2015-05-01 09:45:37 +02:00
Tim Kuipers eeaa53c118 lil comments 2015-05-01 09:43:11 +02:00
Tim Kuipers effff44843 commenting and refactoring comb.h &.cpp 2015-04-30 17:12:34 +02:00
Tim Kuipers 99ea16e251 refactoring and commenting and correcting grammar ((TM) grammar nazi) 2015-04-30 14:54:19 +02:00
Tim Kuipers 574a89ad93 refactoring and commenting combing 2015-04-30 12:34:40 +02:00
131 arquivos alterados com 1938179 adições e 5386 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
+65 -13
Ver Arquivo
@@ -2,7 +2,14 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
find_package(Arcus REQUIRED)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -10,62 +17,107 @@ 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")
if(NOT APPLE AND NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
elseif(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
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
src/FffGcodeWriter.cpp
src/FffPolygonGenerator.cpp
src/FffProcessor.cpp
src/gcodeExport.cpp
src/gcodePlanner.cpp
src/infill.cpp
src/inset.cpp
src/layerPart.cpp
src/main.cpp
src/LayerPlanBuffer.cpp
src/MergeInfillLines.cpp
src/mesh.cpp
src/MeshGroup.cpp
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/polygonOptimizer.cpp
src/PrimeTower.cpp
src/Progress.cpp
src/raft.cpp
src/settingRegistry.cpp
src/settings.cpp
src/skin.cpp
src/skirt.cpp
src/sliceDataStorage.cpp
src/slicer.cpp
src/support.cpp
src/timeEstimate.cpp
src/wallOverlap.cpp
src/Weaver.cpp
src/Wireframe2gcode.cpp
src/modelFile/modelFile.cpp
src/utils/gettime.cpp
src/utils/logoutput.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
)
# 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
)
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
target_link_libraries(CuraEngine clipper Arcus)
# 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 ()
add_executable(Test src/test.cpp)
target_link_libraries(Test clipper)
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)
+20
Ver Arquivo
@@ -0,0 +1,20 @@
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 "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)"
"protobuf (>= 3.0.0)"
"libstdc++6 (>= 4.9.0)"
"libgcc1 (>= 4.9.0)"
)
string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS})
include(CPack)
+14
Ver Arquivo
@@ -0,0 +1,14 @@
Changelog CuraEngine
====================
- Feature: infill_wipe_dist. Add a travel move after an infill line in order to let it stick better to the walls.
- Feature: Draft Protection Screen. A shell similar to the ooze shield providing protection from gusts of wind and acting similar to a heated chamber
Release 15.06.01
-----
- [Not documented]
+21 -7
Ver Arquivo
@@ -1,13 +1,22 @@
syntax = "proto3";
package Cura;
package cura.proto;
// typeid 1
message ObjectList {
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
}
message Object {
// typeid 1
message Slice
{
repeated ObjectList object_lists = 1;
}
message Object
{
int64 id = 1;
bytes vertices = 2; //An array of 3 floats.
bytes normals = 3; //An array of 3 floats.
@@ -16,16 +25,19 @@ message Object {
}
// typeid 3
message Progress {
message Progress
{
float amount = 1;
}
// typeid 2
message SlicedObjectList {
message SlicedObjectList
{
repeated SlicedObject objects = 1;
}
message SlicedObject {
message SlicedObject
{
int64 id = 1;
repeated Layer layers = 2;
@@ -50,6 +62,8 @@ message Polygon {
SkirtType = 5;
InfillType = 6;
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
}
Type type = 1;
bytes points = 2;
+3 -2
Ver Arquivo
@@ -58,7 +58,8 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = documentation
#LEAVE THIS DIRECTORY EMPTY IF THIS DOCUMENTATION NEEDS TO BE PUBLISHED AUTOMATICALLY TO GITHUB PAGES!
OUTPUT_DIRECTORY = docs
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@@ -2049,7 +2050,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO
# The default value is: NO.
HAVE_DOT = NO
HAVE_DOT = YES
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
+9 -8
Ver Arquivo
@@ -3,9 +3,9 @@ CuraEngine
The CuraEngine is a C++ console application for 3D printing GCode generation. It has been made as a better and faster alternative to the old Skeinforge engine.
The CuraEngine is pure C++ and uses Clipper from http://www.angusj.com/delphi/clipper.php
There are no external dependences and Clipper is included in the source code without modifications.
Furthermore it depends on libArcus by Ultimaker, which can be found at http://github.com/Ultimaker/libArcus
This is just a console application for GCode generation. For a full graphical application look at https://github.com/daid/Cura which is the graphical frontend for CuraEngine.
This is just a console application for GCode generation. For a full graphical application look at https://github.com/Ultimaker/Cura which is the graphical frontend for CuraEngine.
The CuraEngine can be used seperately or in other applications. Feel free to add it to your application. But please take note of the License.
@@ -24,6 +24,7 @@ How to Install
In order to compile CuraEngine, either use CMake or start a project in your preferred IDE.
CMake compilation:
1. Navigate to the CuraEngine directory and execute the following commands
2. $ mkdir build && cd build
3. $ cmake ..
@@ -48,16 +49,16 @@ Installing Protobuf
Running
=======
Other than running CuraEngine from a frontend, such as PluggableCura, one can run CuraEngine from the command line.
Other than running CuraEngine from a frontend, such as Ultimaker/Cura, one can run CuraEngine from the command line.
For that one needs a settings JSON file, which can be found in the Ultimaker/Cura repository.
An example run for an UM2 machine looks as follows:
* Navigate to the CuraEngine directory and execute the following
```
./build/CuraEngine -v -j fdmprinter.json -s machine_start_gcode=";FLAVOR:UltiGCode
;TIME:10000000
;MATERIAL:2000
;MATERIAL2:0" -s mesh_position_x=115.0 -s mesh_position_y=112.5 -s mesh_position_z=0 -s material_diameter=1.128 -o "output/test.gcode" "/path/to/model.stl"
./build/CuraEngine slice -v -j ../Cura/resources/machines/dual_extrusion_printer.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
```
Run `CuraEngine help` for a general description of how to use the CuraEngine tool.
Internals
=========
@@ -119,4 +120,4 @@ The GCode generation is quite a large bit of code. As a lot is going on here. Im
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
* Comb: The combing code is the code that tries to avoid holes when moving the head around without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax;meshmdhfdhfdhf to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
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
~~~~~~~~~~~~~~~
+1
Ver Arquivo
@@ -4,6 +4,7 @@ Glossary
Term/Synonyms | Meaning
--- | ---
Extruder Train | The whole of a feeder, bowden tube and a nozzle
Island/Part | isolated/unconnected part in 2D slice
Inset | perimeter, the perimeters which are laid down around the infill
Slicing | The act of extracting the contours of the object at a certain height (not the whole process which would also include gcode generation etc.)
-1325
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+57
Ver Arquivo
@@ -0,0 +1,57 @@
Tencent is pleased to support the open source community by making RapidJSON available.
Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License.
If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON.
A copy of the MIT License is included in this file.
Other dependencies and licenses:
Open Source Software Licensed Under the BSD License:
--------------------------------------------------------------------
The msinttypes r29
Copyright (c) 2006-2013 Alexander Chemeris
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Open Source Software Licensed Under the JSON License:
--------------------------------------------------------------------
json.org
Copyright (c) 2002 JSON.org
All Rights Reserved.
JSON_checker
Copyright (c) 2002 JSON.org
All Rights Reserved.
Terms of the JSON License:
---------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Terms of the MIT License:
--------------------------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+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
+130
Ver Arquivo
@@ -0,0 +1,130 @@
[ problem ]
gantry_height introduced by Jaime while there already was machine_nozzle_gantry_distance
[ RENAMES ]
raft_base_linewidth > raft_base_line_width
raft_interface_linewidth > raft_interface_line_width
fill_overlap > infill_overlap
fill_pattern > infill_pattern
fill_sparse_combine > infill_sparse_combine
fill_sparse_density > infill_sparse_density
fill_sparse_thickness > infill_sparse_thickness
support_fill_rate > support_infill_rate
[ SPLITS ]
raft_line_spacing > raft_base_line_spacing (, raft_interface_line_spacing, raft_surface_line_spacing)
wall_overlap_avoid_enabled > remove_overlapping_walls_enabled (, remove_overlapping_walls_0_enabled, remove_overlapping_walls_x_enabled)
retraction_minimal_extrusion > retraction_extrusion_window (+ retraction_count_max = 1)
magic_mesh_surface_mode : false >> "Normal"
magic_mesh_surface_mode : true >> "Surface"
[ NEW ]
alternate_extra_perimeter
coasting_enable
coasting_min_volume
coasting_min_volume_move
coasting_min_volume_retract
coasting_speed
coasting_speed_move
coasting_speed_retract
coasting_volume
coasting_volume_move
coasting_volume_retract
fill_perimeter_gaps
draft_shield_dist
draft_shield_enabled
draft_shield_height
draft_shield_height_limitation
infill_wipe_dist
line_width (was wall_line_width)
machine_extruder_count
machine_head_polygon
machine_head_with_fans_polygon
machine_heat_zone_length
magic_mesh_surface_mode
meshfix_extensive_stitching
meshfix_keep_open_polygons
meshfix_union_all
meshfix_union_all_remove_holes
print_sequence
raft_base_line_spacing (from raft_line_spacing)
raft_base_line_width
raft_fan_speed
raft_interface_fan_speed
raft_interface_line_spacing
raft_interface_speed
raft_speed
raft_surface_fan_speed
raft_surface_line_spacing
raft_surface_line_width
raft_surface_speed
raft_surface_thickness
remove_overlapping_walls_0_enabled
remove_overlapping_walls_enabled
remove_overlapping_walls_x_enabled
retraction_count_max
retraction_extrusion_window (from retraction_minimal_extrusion)
retraction_extra_prime_amount
skin_alternate_rotation
speed_support_lines
speed_support_roof
support_conical_angle
support_conical_enabled
support_conical_min_width
support_offset
support_roof_enable
support_roof_height
support_roof_line_width
travel_avoid_distance
travel_avoid_other_parts
travel_compensate_overlapping_walls_enabled
z_seam_type
[ DUAL EXTRUSION ]
extruder_nr
machine_use_extruder_offset_to_offset_coords
machine_nozzle_offset_x
machine_nozzle_offset_y
machine_extruder_start_code
machine_extruder_start_pos_abs
machine_extruder_start_pos_x
machine_extruder_start_pos_y
machine_extruder_end_pos_abs
machine_extruder_end_pos_x
machine_extruder_end_pos_y
machine_extruder_end_code
prime_tower_enable
prime_tower_size
prime_tower_position_x
prime_tower_position_y
prime_tower_flow
prime_tower_wipe_enabled
ooze_shield_enabled
ooze_shield_angle
ooze_shield_dist
+23
Ver Arquivo
@@ -0,0 +1,23 @@
#ifndef EXTRUDER_TRAIN_H
#define EXTRUDER_TRAIN_H
#include "settings.h"
namespace cura
{
class ExtruderTrain : public SettingsBase
{
int extruder_nr;
public:
int getExtruderNr() { return extruder_nr; }
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
: SettingsBase(settings)
, extruder_nr(extruder_nr)
{ }
};
}//namespace cura
#endif // EXTRUDER_TRAIN_H
+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
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+358
Ver Arquivo
@@ -0,0 +1,358 @@
#ifndef GCODE_WRITER_H
#define GCODE_WRITER_H
#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"
#include "bridge.h"
#include "pathOrderOptimizer.h"
#include "gcodePlanner.h"
#include "gcodeExport.h"
#include "commandSocket.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "LayerPlanBuffer.h"
namespace cura
{
/*!
* Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation.
* Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines,
* while other polygons signify the contours which should be printed.
*
* The main function of this class is FffGcodeWriter::writeGCode().
*/
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;
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
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;
command_socket = NULL;
}
void resetFileNumber()
{
meshgroup_number = 1;
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
}
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (command_socket)
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
}
bool setTargetFile(const char* filename)
{
output_file.open(filename);
if (output_file.is_open())
{
gcode.setOutputStream(&output_file);
return true;
}
return false;
}
void setTargetStream(std::ostream* stream)
{
gcode.setOutputStream(stream);
}
double getTotalFilamentUsed(int e)
{
return gcode.getTotalFilamentUsed(e);
}
double getTotalPrintTime()
{
return gcode.getTotalPrintTime();
}
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
private:
void setConfigFanSpeedLayerTime();
void setConfigCoasting(SliceDataStorage& storage);
//Setup the retraction parameters.
void setConfigRetraction(SliceDataStorage& storage);
/*!
* initialize GcodePathConfig config parameters which don't change over all layers
*/
void initConfigs(SliceDataStorage& storage);
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
/*!
* Set temperatures and perform initial priming.
* \param storage Input: where the slice data is stored.
*/
void processStartingCode(SliceDataStorage& storage);
/*!
* Move up and over the just printed model to print the next model.
* \param storage Input: where the slice data is stored.
*/
void processNextMeshGroupCode(SliceDataStorage& storage);
/*!
* Add raft gcode.
* \param storage Input: where the slice data is stored.
* \param total_layers The total number of layers.
*/
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 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 total_layers, bool has_raft);
/*!
* Add the skirt to the gcode.
* \param storage Input: where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param extruder_nr The extrudewr train for which to process the skirt
*/
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
/*!
* Adds the ooze shield to the print.
* \param storage Input: where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Adds the draft protection screen to the print.
* \param storage Input: where the slice data is stored.
* \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 processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Calculate in which order to print the meshes.
* \param storage Input: where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of mesh indices ordered on print order.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
/*!
* Add a single layer from a single mesh-volume to the GCode in mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* \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 addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the open polylines from a single layer from a single mesh-volume to the GCode for mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh for which to add to the gcode.
* \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 addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add a single layer from a single mesh-volume to the GCode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* \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 addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Add normal sparse infill for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
/*!
* 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.
* \param mesh The mesh for which to add to the gcode.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
*/
void processInsets(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type);
/*!
* Add the gcode of the top/bottom skin of the given part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width);
/*!
* Add the support to the gcode of the current layer.
* \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.
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
* \param before_rest Whether the function has been called before adding the rest to the gcode, or after.
*/
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
/*!
* Add the support lines/walls to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* \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 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.
* \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 addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
*
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
*
* \param storage Input: where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr The extruder to which to switch
*/
void setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr);
/*!
* Add the prime tower gcode for the current layer.
* \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.
* \param prev_extruder The current extruder with which we last printed.
*/
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
/*!
* Add the end gcode and set all temperatures to zero.
*/
void finalize();
};
}//namespace cura
#endif // GCODE_WRITER_H
+576
Ver Arquivo
@@ -0,0 +1,576 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include "slicer.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "MeshGroup.h"
#include "support.h"
#include "multiVolumes.h"
#include "layerPart.h"
#include "inset.h"
#include "skirt.h"
#include "skin.h"
#include "infill.h"
#include "raft.h"
#include "debug.h"
#include "Progress.h"
namespace cura
{
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (commandSocket)
commandSocket->beginSendSlicedObject();
if (!sliceModel(meshgroup, timeKeeper, storage))
{
return false;
}
slices2polygons(storage, timeKeeper);
return true;
}
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
storage.model_size = storage.model_max - storage.model_min;
log("Slicing model...\n");
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
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++)
{
Mesh& mesh = meshgroup->meshes[mesh_idx];
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
slicerList.push_back(slicer);
/*
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
bool has_raft = meshStorage.getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
//Add the raft offset to each layer.
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
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("raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap");
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (commandSocket)
{
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
}
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
return true;
}
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
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++)
{
processInsets(storage, layer_number);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
}
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
if (total_layers < 1)
{
log("Stopping process because there are no layers.\n");
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
int mesh_max_bottom_layer_count = 0;
if (getSettingBoolean("magic_spiralize"))
{
for(SliceMeshStorage& mesh : storage.meshes)
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(storage, layer_number);
}
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
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);
storage.primeTower.generatePaths(storage, total_layers);
processOozeShield(storage, total_layers);
processDraftShield(storage, total_layers);
processPlatformAdhesion(storage);
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
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"));
}
}
}
}
}
void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() > 0)
{
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // 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);
}
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (PolygonRef polyline : layer->openPolyLines)
{
Polygons segments;
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
{
PolygonRef segment = segments.newPoly();
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
}
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
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 < total_layers; layer_idx++)
{
bool layer_is_empty = true;
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_idx];
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
layer_is_empty = false;
break;
}
}
if (layer_is_empty)
{
n_empty_first_layers++;
} else
{
break;
}
}
if (n_empty_first_layers > 0)
{
log("Removing %d layers because they are empty\n", n_empty_first_layers);
for (SliceMeshStorage& mesh : storage.meshes)
{
std::vector<SliceLayer>& layers = mesh.layers;
layers.erase(layers.begin(), layers.begin() + n_empty_first_layers);
for (SliceLayer& layer : layers)
{
layer.printZ -= n_empty_first_layers * layer_height;
}
}
total_layers -= n_empty_first_layers;
}
}
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 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, 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 = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
}
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
}
bool frontend_can_show_polygon_as_filled_polygon = false;
if (frontend_can_show_polygon_as_filled_polygon)
{
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
}
}
}
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
return;
}
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
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<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<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=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 total_layers)
{
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
int layer_height_0 = getSettingInMicrons("layer_height_0");
int layer_height = getSettingInMicrons("layer_height");
if (draft_shield_height < layer_height_0)
{
return;
}
unsigned int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
int layer_skip = 500 / layer_height + 1;
Polygons& draft_shield = storage.draft_protection_shield;
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));
}
storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist);
}
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
{
switch(getSettingAsPlatformAdhesion("adhesion_type"))
{
case EPlatformAdhesion::SKIRT:
if (getSettingInMicrons("draft_shield_height") == 0)
{ // draft screen replaces skirt
generateSkirt(storage, getSettingInMicrons("skirt_gap"), getSettingAsCount("skirt_line_count"), getSettingInMicrons("skirt_minimal_length"));
}
break;
case EPlatformAdhesion::BRIM:
generateSkirt(storage, 0, getSettingAsCount("brim_line_count"), getSettingInMicrons("skirt_minimal_length"));
break;
case EPlatformAdhesion::RAFT:
generateRaft(storage, getSettingInMicrons("raft_margin"));
break;
}
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
}
void FffPolygonGenerator::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 (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;
Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0];
for (PolygonRef poly : skin)
{
// 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)
{
int r = rand() % (fuzziness * 2) - fuzziness;
Point perp_to_p0p1 = crossZ(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = *p0 + normal(p0p1, p0pa_dist) + 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);
}
}
skin = results;
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}//namespace cura
+152
Ver Arquivo
@@ -0,0 +1,152 @@
#ifndef FFF_AREA_GENERATOR_H
#define FFF_AREA_GENERATOR_H
#include "MeshGroup.h"
#include "utils/polygonUtils.h"
#include "utils/NoCopy.h"
#include "utils/gettime.h"
#include "settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
namespace cura
{
/*!
* 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.
* After slicing, the layers are processed; for example the wall insets are generated, and the areas which are to be filled with support and infill, which are all represented by polygons.
* In this stage nothing other than areas and circular paths are generated, which are both represented by polygons.
* No infill lines or support pattern etc. is generated.
*
* The main function of this class is FffPolygonGenerator::generateAreas().
*/
class FffPolygonGenerator : public SettingsMessenger, NoCopy
{
private:
CommandSocket* commandSocket;
public:
/*!
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
*/
FffPolygonGenerator(SettingsBase* settings_)
: SettingsMessenger(settings_)
, commandSocket(nullptr)
{
}
/*!
* Set the FffAreaGenerator::commandSocket
*/
void setCommandSocket(CommandSocket* socket)
{
commandSocket = socket;
}
/*!
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
*
* \param object The object to slice.
* \param timeKeeper Object which keeps track of timings of each stage.
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
*/
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* Send polygons over the command socket, if there is one.
* \param type The type of polygon to send
* \param layer_nr The layer number at which the polygons occur
* \param polygons The polygons to be sent
*/
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (commandSocket)
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
}
/*!
* Slice the \p object and store the outlines in the \p storage.
*
* \param object The object to slice.
* \param timeKeeper Object which keeps track of timings of each stage.
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
*
* \return Whether the process succeeded (always true).
*/
bool sliceModel(MeshGroup* object, TimeKeeper& timeKeeper, SliceDataStorage& storage); /// slices the model
/*!
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, support area polygons, etc.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param timeKeeper Object which keeps track of timings of each stage.
*/
void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);
/*!
* 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 total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
/*!
* Generate the inset polygons which form the walls.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
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 total_layers The total number of layers
*/
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 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 total_layers The total number of layers
*/
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
*/
void processPlatformAdhesion(SliceDataStorage& storage);
/*!
* Make the outer wall 'fuzzy'
*/
void processFuzzyWalls(SliceMeshStorage& mesh);
};
}//namespace cura
#endif // FFF_AREA_GENERATOR_H
+110
Ver Arquivo
@@ -0,0 +1,110 @@
#include "FffProcessor.h"
namespace cura
{
FffProcessor FffProcessor::instance; // definition must be in cpp
std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup)
{
std::stringstream sstream;
if (first_meshgroup)
{
sstream << " -g";
}
else
{
sstream << " --next";
}
sstream << meshgroup.getAllLocalSettingsString();
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
{
ExtruderTrain* train = meshgroup.getExtruderTrain(extruder_nr);
sstream << " -e" << extruder_nr << train->getAllLocalSettingsString();
}
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup.meshes[mesh_idx];
sstream << " -e" << mesh.getSettingAsCount("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
}
sstream << "\n";
return sstream.str();
}
bool FffProcessor::processFiles(const std::vector< std::string >& files)
{
time_keeper.restart();
MeshGroup* meshgroup = new MeshGroup(this);
for(std::string filename : files)
{
log("Loading %s from disk...\n", filename.c_str());
FMatrix3x3 matrix;
if (!loadMeshIntoMeshGroup(meshgroup, filename.c_str(), matrix))
{
logError("Failed to load model: %s\n", filename.c_str());
return false;
}
}
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", time_keeper.restart());
return processMeshGroup(meshgroup);
}
bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, first_meshgroup).c_str()); }
time_keeper.restart();
if (!meshgroup)
return false;
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");
Weaver w(this);
w.weave(meshgroup, command_socket);
log("starting Neith Gcode generation...\n");
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
gcoder.writeGCode(command_socket);
log("finished Neith Gcode generation...\n");
} else
{
SliceDataStorage storage(meshgroup);
if (!polygon_generator.generateAreas(storage, meshgroup, time_keeper))
{
return false;
}
gcode_writer.setCommandSocket(command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
gcode_writer.writeGCode(storage, time_keeper);
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
first_meshgroup = false;
return true;
}
} // namespace cura
+97
Ver Arquivo
@@ -0,0 +1,97 @@
#ifndef FFF_PROCESSOR_H
#define FFF_PROCESSOR_H
#include "settings.h"
#include "FffGcodeWriter.h"
#include "FffPolygonGenerator.h"
#include "commandSocket.h"
#include "Weaver.h"
#include "Wireframe2gcode.h"
#include "Progress.h"
#include "utils/gettime.h"
#include "utils/NoCopy.h"
#define SHOW_ALL_SETTINGS true
namespace cura {
//FusedFilamentFabrication processor. Singleton class
class FffProcessor : public SettingsBase , NoCopy
{
private:
static FffProcessor instance;
FffProcessor()
: polygon_generator(this)
, gcode_writer(this)
, first_meshgroup(true)
{
command_socket = NULL;
}
public:
static FffProcessor* getInstance()
{
return &instance;
}
private:
FffPolygonGenerator polygon_generator;
FffGcodeWriter gcode_writer;
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
bool first_meshgroup;
std::string profile_string = "";
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
public:
std::string getProfileString() { return profile_string; }
TimeKeeper time_keeper; // TODO: use singleton time keeper
void resetFileNumber()
{
gcode_writer.resetFileNumber();
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
gcode_writer.setCommandSocket(socket);
polygon_generator.setCommandSocket(socket);
}
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
}
void setTargetStream(std::ostream* stream)
{
return gcode_writer.setTargetStream(stream);
}
double getTotalFilamentUsed(int e)
{
return gcode_writer.getTotalFilamentUsed(e);
}
double getTotalPrintTime()
{
return gcode_writer.getTotalPrintTime();
}
void finalize()
{
gcode_writer.finalize();
}
bool processFiles(const std::vector<std::string> &files);
bool processMeshGroup(MeshGroup* meshgroup);
};
}//namespace cura
#endif//FFF_PROCESSOR_H
+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
+155
Ver Arquivo
@@ -0,0 +1,155 @@
#include "MergeInfillLines.h"
#include <algorithm> // min
namespace cura
{
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width)
{
double old_line_width = INT2MM(last_path.config->getLineWidth());
double new_line_width_mm = INT2MM(new_line_width);
double speed_mod = old_line_width / new_line_width_mm;
double extrusion_mod = new_line_width_mm / old_line_width;
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
{ //Check for lots of small moves and combine them into one large line
Point prev_middle;
Point last_middle;
int64_t line_width;
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
{
GCodePath& move_path = paths[path_idx];
for(unsigned int point_idx = 0; point_idx < move_path.points.size() - 1; point_idx++)
{
gcode.writeMove(move_path.points[point_idx], speed, move_path.getExtrusionMM3perMM());
}
gcode.writeMove(prev_middle, travelConfig.getSpeed(), 0);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, speed, last_path, line_width);
}
path_idx += 2;
extruder_plan.handleInserts(path_idx, gcode);
for (; isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, speed, last_path, line_width);
}
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
extruder_plan.handleInserts(path_idx, gcode);
return true;
}
return false;
};
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
{
int64_t max_line_width = nozzle_size * 3 / 2;
unsigned int idx = path_idx_first_move;
if (idx + 3 > paths.size()-1) return false;
if (paths[idx+0].config != &travelConfig) return false;
if (paths[idx+1].points.size() > 1) return false;
if (paths[idx+1].config == &travelConfig) return false;
// if (paths[idx+2].points.size() > 1) return false;
if (paths[idx+2].config != &travelConfig) return false;
if (paths[idx+3].points.size() > 1) return false;
if (paths[idx+3].config == &travelConfig) return false;
Point& a = paths[idx+0].points.back(); // first extruded line from
Point& b = paths[idx+1].points.back(); // first extruded line to
Point& c = paths[idx+2].points.back(); // second extruded line from
Point& d = paths[idx+3].points.back(); // second extruded line to
Point ab = b - a;
Point cd = d - c;
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
return false; // extrusion moves not in the same or opposite diraction
if (prod < 0) { ab = ab * -1; }
Point infill_vector = (cd + ab) / 2;
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
first_middle = (use_second_middle_as_first)?
second_middle :
(a + b) / 2;
second_middle = (c + d) / 2;
Point dir_vector_perp = crossZ(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0) return false;
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (line_width > max_line_width) return false; // combined lines would be too wide
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
{ // check whether the two lines are adjacent
Point ca = first_middle - c;
double ca_size = vSizeMM(ca);
double cd_size = vSizeMM(cd);
double prod = INT2MM(dot(ca, cd));
double fraction = prod / ( ca_size * cd_size );
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
}
return true;
};
/*
void MergeInfillLines::merge(Point& from, Point& p0, Point& p1)
{ //Check for lots of small moves and combine them into one large line
if (path->points.size() == 1 && path->config != &travelConfig); // && shorterThen(from - path->points[0], path->config->getLineWidth() * 2))
{
Point p0 = path->points[0];
unsigned int path_idx_last = path_idx + 1; // index of the last short move
while(path_idx_last < paths.size() && paths[path_idx_last].points.size() == 1 && shorterThen(p0 - paths[path_idx_last].points[0], path->config->getLineWidth() * 2))
{
p0 = paths[path_idx_last].points[0];
path_idx_last ++;
}
if (paths[path_idx_last-1].config == &travelConfig)
path_idx_last --;
if (path_idx_last > path_idx + 2)
{
p0 = from;
for(unsigned int path_idx_short = path_idx; path_idx_short < path_idx_last-1; path_idx_short+=2)
{
int64_t oldLen = vSize(p0 - paths[path_idx_short].points[0]);
Point newPoint = (paths[path_idx_short].points[0] + paths[path_idx_short+1].points[0]) / 2;
int64_t newLen = vSize(from - newPoint);
if (newLen > 0)
{
if (oldLen > 0)
gcode.writeMove(newPoint, speed * oldLen / newLen, path->getExtrusionMM3perMM() * newLen / oldLen);
else
gcode.writeMove(newPoint, speed, path->getExtrusionMM3perMM());
}
}
gcode.writeMove(paths[path_idx_last-1].points[0], speed, path->getExtrusionMM3perMM());
path_idx = path_idx_last - 1;
continue;
}
}
}*/
}//namespace cura
+66
Ver Arquivo
@@ -0,0 +1,66 @@
#ifndef MERGE_INFILL_LINES_H
#define MERGE_INFILL_LINES_H
#include "utils/intpoint.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
namespace cura
{
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
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
/*!
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
*
* \param to The point to move to
* \param speed The original speed
* \param old_path The original path
* \param new_line_width The width of the convewrted line (approximately the length of the original line)
*/
void writeCompensatedMove(Point& to, double speed, GCodePath& old_path, int64_t new_line_width);
public:
/*!
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
*/
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.
* Updates \p path_idx to the next path which is not combined.
*
* \param gcode Where to write the combined line to
* \param paths The paths currently under consideration
* \param travelConfig The travel settings used to see whether a path is a travel path or an extrusion path
* \param nozzle_size The diameter of the hole in the nozzle
* \param speed A factor used to scale the movement speed
* \param path_idx Input/Output parameter: The current index in \p paths where to start combining and the current index after combining as output parameter.
* \return Whether lines have been merged and normal path-to-gcode generation can be skipped for the current resulting \p path_idx .
*/
bool mergeInfillLines(double speed, unsigned int& path_idx);
};
}//namespace cura
#endif // MERGE_INFILL_LINES_H
+27 -24
Ver Arquivo
@@ -3,9 +3,12 @@
#include <strings.h>
#include <stdio.h>
#include "modelFile.h"
#include "../utils/logoutput.h"
#include "../utils/string.h"
#include "MeshGroup.h"
#include "utils/logoutput.h"
#include "utils/string.h"
namespace cura
{
FILE* binaryMeshBlob = nullptr;
@@ -25,7 +28,7 @@ void* fgets_(char* ptr, size_t len, FILE* f)
return nullptr;
}
bool loadModelSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rt");
char buffer[1024];
@@ -58,7 +61,7 @@ bool loadModelSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return true;
}
bool loadModelSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
char buffer[80];
@@ -77,35 +80,29 @@ bool loadModelSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
mesh->faces.reserve(faceCount);
mesh->vertices.reserve(faceCount);
for(unsigned int i=0;i<faceCount;i++)
{
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
{
fclose(f);
return false;
}
float v[9];
if (fread(v, sizeof(float) * 9, 1, f) != 1)
if (fread(buffer, 50, 1, f) != 1)
{
fclose(f);
return false;
}
float *v= ((float*)buffer)+3;
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
mesh->addFace(v0, v1, v2);
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
{
fclose(f);
return false;
}
}
fclose(f);
mesh->finish();
return true;
}
bool loadModelSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
@@ -122,7 +119,7 @@ bool loadModelSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
buffer[5] = '\0';
if (stringcasecompare(buffer, "solid") == 0)
{
bool load_success = loadModelSTL_ascii(mesh, filename, matrix);
bool load_success = loadMeshSTL_ascii(mesh, filename, matrix);
if (!load_success)
return false;
@@ -131,20 +128,26 @@ bool loadModelSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
if (mesh->faces.size() < 1)
{
mesh->clear();
return loadModelSTL_binary(mesh, filename, matrix);
return loadMeshSTL_binary(mesh, filename, matrix);
}
return true;
}
return loadModelSTL_binary(mesh, filename, matrix);
return loadMeshSTL_binary(mesh, filename, matrix);
}
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix)
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
{
const char* ext = strrchr(filename, '.');
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
{
object->meshes.emplace_back(object);
return loadModelSTL(&object->meshes[object->meshes.size()-1], filename, matrix);
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.push_back(mesh);
return true;
}
}
return false;
}
}//namespace cura
+147
Ver Arquivo
@@ -0,0 +1,147 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MESH_GROUP_H
#define MESH_GROUP_H
#include "utils/NoCopy.h"
#include "mesh.h"
#include "ExtruderTrain.h"
namespace cura
{
/*!
* A MeshGroup is a collection with 1 or more 3D meshes.
*
* 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, NoCopy
{
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
int extruder_count;
public:
int getExtruderCount()
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
{}
~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
/*!
* 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])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
}
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
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 max() //! maximal corner of bounding box
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void finalize()
{
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
};
/*!
* Load a Mesh from file and store it in the \p meshgroup.
*
* \param meshgroup The meshgroup where to store the mesh
* \param filename The filename of the mesh file
* \param transformation The transformation applied to all vertices
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
* \return whether the file could be loaded
*/
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
}//namespace cura
#endif//MESH_GROUP_H
+178
Ver Arquivo
@@ -0,0 +1,178 @@
#ifndef PREHEAT_H
#define PREHEAT_H
#include <cassert>
#include <algorithm> // max
#include "utils/logoutput.h"
#include "MeshGroup.h"
#include "FlowTempGraph.h"
namespace cura
{
/*!
* Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature.
*/
class Preheat
{
/*!
* The nozzle and material temperature settings for an extruder train.
*/
class Config
{
public:
double time_to_heatup_1_degree; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature)
double time_to_cooldown_1_degree; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature)
double heatup_cooldown_time_mod_while_printing; //!< The time to be added to Preheat::time_to_heatup_1_degree and subtracted from Preheat::time_to_cooldown_1_degree to get the timings while printing
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
double material_print_temperature; //!< default print temp (backward compatilibily)
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
};
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
public:
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
* \return the standby temp
*/
double getStandbyTemp(int extruder)
{
return config_per_extruder[extruder].standby_temp;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
*/
void setConfig(MeshGroup& settings)
{
for (int extruder_nr = 0; extruder_nr < settings.getExtruderCount(); extruder_nr++)
{
assert(settings.getExtruderTrain(extruder_nr) != nullptr);
ExtruderTrain& extruder_train = *settings.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
config.time_to_cooldown_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed"); // 0.5
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature"); // 220
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
}
}
bool usesFlowDependentTemp(int extruder_nr)
{
return config_per_extruder[extruder_nr].flow_dependent_temperature;
}
private:
/*!
* Calculate time to heat up from standby temperature to a given temperature.
* Assumes @p temp is higher than the standby temperature.
*
* \param extruder The extruder for which to get the time
* \param temp The temperature to be reached
*/
double timeToHeatFromStandbyToPrintTemp(unsigned int extruder, double temp)
{
return (temp - config_per_extruder[extruder].standby_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
public:
/*!
* Get the optimal temperature corresponding to a given average flow.
* \param extruder The extruder train
* \param flow The flow for which to get the optimal temperature
* \return The corresponding optimal temperature
*/
double getTemp(unsigned int extruder, double flow)
{
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
/*!
* Decide when to start warming up again after starting to cool down towards the standby temperature.
* Two cases are considered:
* the case where the standby temperature is reached \__/ .
* and the case where it isn't \/ .
*
* IT is assumed that the printer is not printing during this cool down and warm up time.
*
* Assumes from_temp is approximately the same as @p temp
*
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \return The time before the end of the @p time_window to insert the preheat command
*/
double timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
{
double time_ratio_cooldown_heatup = config_per_extruder[extruder].time_to_cooldown_1_degree / config_per_extruder[extruder].time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = timeToHeatFromStandbyToPrintTemp(extruder, temp);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_standby_temp < time_window)
{
return time_to_heat_from_standby_to_print_temp;
}
else
{
return time_window * config_per_extruder[extruder].time_to_heatup_1_degree / (config_per_extruder[extruder].time_to_cooldown_1_degree + config_per_extruder[extruder].time_to_heatup_1_degree);
}
}
/*!
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
* If the printer is printing in the mean time the warming up will take longer.
*
*
* \param from_temp The temperature at which the nozzle was before
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \param printing Whether the printer is printing in the time to heat up the nozzle
* \return The time needed to reach the desired temperature (@p temp)
*/
double timeBeforeEndToInsertPreheatCommand_warmUp(double from_temp, unsigned int extruder, double temp, bool printing)
{
if (temp > from_temp)
{
if (printing)
{
return (temp - from_temp) * (config_per_extruder[extruder].time_to_heatup_1_degree + config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
else
{
return (temp - from_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
}
else
{
if (printing)
{
return (from_temp - temp) * config_per_extruder[extruder].time_to_cooldown_1_degree;
}
else
{
return (from_temp - temp) * std::max(0.0, config_per_extruder[extruder].time_to_cooldown_1_degree - config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
}
}
};
} // namespace cura
#endif // PREHEAT_H
+299
Ver Arquivo
@@ -0,0 +1,299 @@
#include "PrimeTower.h"
#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "infill.h"
namespace cura
{
PrimeTower::PrimeTower()
{
}
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++)
{
GCodePathConfig& conf = config_per_extruder[extr];
conf.setLayerHeight(layer_thickness);
}
}
void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{ // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the prime tower
extruder_count = storage.getSettingAsCount("machine_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
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_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] =
std::max( max_object_height_per_extruder[support_roof_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
}
{ // // compute max_object_height_second_to_last_extruder
int extruder_max_object_height = 0;
for (int extruder_nr = 1; extruder_nr < extruder_count; extruder_nr++)
{
if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_max_object_height])
{
extruder_max_object_height = extruder_nr;
}
}
int extruder_second_max_object_height = -1;
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
if (extruder_nr == extruder_max_object_height) { continue; }
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;
}
}
if (extruder_second_max_object_height < 0)
{
storage.max_object_height_second_to_last_extruder = -1;
}
else
{
storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height];
}
}
}
void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
&& storage.getSettingInMicrons("prime_tower_size") > 0)
{
generatePaths3(storage);
}
}
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)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
}
void PrimeTower::generatePaths2(SliceDataStorage& storage) // half baked attempt at spiral shaped prime tower pattern
{
// extruder_count = storage.getSettingAsCount("machine_extruder_count");
//
// int64_t line_dists[extruder_count + 1]; // distance between the lines of different extruders, and half the line dist for beginning and ending
// int64_t total_width = 0;
// {
// int64_t last_line_width = 0;
// for (int extr = 0; extr < extruder_count; extr++)
// {
// int64_t line_width = config_per_extruder[extr].getLineWidth();
// line_dists[extr] = (line_width + last_line_width) / 2;
// total_width += line_width;
// last_line_width = line_width;
// }
// line_dists[extruder_count] = last_line_width / 2;
// }
//
}
void PrimeTower::generatePaths3(SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
double infill_overlap = 15; // so that it can't be zero
generateGroundpoly(storage);
for (int extruder = 0; extruder < extruder_count; extruder++)
{
int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
patterns_per_extruder.emplace_back(n_patterns);
std::vector<Polygons>& patterns = patterns_per_extruder.back();
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
}
}
}
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
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
{
return;
}
bool prime_tower_added = false;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++)
{
prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr);
}
if (prime_tower_added)
{ // don't print the prime tower if it has been printed already
return;
}
if (prev_extruder == gcodeLayer.getExtruder())
{
wipe = false;
}
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, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
Polygons& pattern = patterns_per_extruder[new_extruder][layer_nr % 2];
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config);
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, CommandSocket* command_socket)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
int64_t offset = -config_per_extruder[new_extruder].getLineWidth();
if (layer_nr > 0)
offset *= 2;
//If we changed extruder, print the wipe/prime tower for this nozzle;
std::vector<Polygons> insets;
{ // generate polygons
if ((layer_nr % 2) == 1)
insets.push_back(storage.primeTower.ground_poly.offset(offset / 2));
else
insets.push_back(storage.primeTower.ground_poly);
while(true)
{
Polygons new_inset = insets[insets.size() - 1].offset(offset);
if (new_inset.size() < 1)
break;
insets.push_back(new_inset);
}
}
for(unsigned int n=0; n<insets.size(); n++)
{
GCodePathConfig& config = config_per_extruder[new_extruder];
gcodeLayer.addPolygonsByOptimizer(insets[(prime_tower_dir_outward)? insets.size() - 1 - n : n], &config);
}
last_prime_tower_poly_printed[new_extruder] = layer_nr;
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));
}
};
}//namespace cura
+68
Ver Arquivo
@@ -0,0 +1,68 @@
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
#include "gcodeExport.h" // GCodePathConfig
#include "MeshGroup.h"
#include "utils/polygon.h" // Polygons
namespace cura
{
class SliceDataStorage;
class GCodePlanner;
class GCodeExport;
typedef std::vector<IntPoint> PolyLine;
class PrimeTower
{
private:
int extruder_count;
std::vector<GCodePathConfig> config_per_extruder;
class WallInfill
{
};
public:
void initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder);
void setConfigs(MeshGroup* configs, int layer_thickness);
Polygons ground_poly;
std::vector<PolyLine> extruder_paths;
void generateGroundpoly(SliceDataStorage& storage);
std::vector<std::vector<Polygons>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
void generatePaths3(SliceDataStorage& storage);
void generatePaths2(SliceDataStorage& storage);
/*!
* 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 total_layers The total number of layers
*/
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, 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);
};
}//namespace cura
#endif // PRIME_TOWER_H
+27
Ver Arquivo
@@ -0,0 +1,27 @@
#ifndef PRINT_FEATURE
#define PRINT_FEATURE
namespace cura
{
enum class EPrintFeature : unsigned int // unused!!
{ // TODO: use in gcodePathConfigs ?
OUTER_WALL,
INNER_WALLS,
INFILL,
SKIN,
HELPERS,
UNCLASSIFIED,
ENUM_COUNT
};
} // namespace cura
#endif // PRINT_FEATURE
+101
Ver Arquivo
@@ -0,0 +1,101 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "Progress.h"
#include "commandSocket.h"
#include "utils/gettime.h"
namespace cura {
double Progress::times [] =
{
0.0,
5.269,
1.533,
22.953,
51.009,
48.858,
154.62,
0.1
};
std::string Progress::names [] =
{
"start",
"slice",
"layerparts",
"inset",
"support",
"skin",
"export",
"process"
};
double Progress::accumulated_times [N_PROGRESS_STAGES] = {-1};
double Progress::total_timing = -1;
/*
const Progress::Stage Progress::stages[] =
{
Progress::Stage::START,
Progress::Stage::SLICING,
Progress::Stage::PARTS,
Progress::Stage::INSET,
Progress::Stage::SUPPORT,
Progress::Stage::SKIN,
Progress::Stage::EXPORT,
Progress::Stage::FINISH
};
*/
float Progress::calcOverallProgress(Stage stage, float stage_progress)
{
return ( accumulated_times[(int)stage] + stage_progress * times[(int)stage] ) / total_timing;
}
void Progress::init()
{
double accumulated_time = 0;
for (int stage = 0; stage < N_PROGRESS_STAGES; stage++)
{
accumulated_times[(int)stage] = accumulated_time;
accumulated_time += times[(int)stage];
}
total_timing = accumulated_time;
}
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
{
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
if (command_socket)
{
command_socket->sendProgress(percentage);
}
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
}
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
{
if (command_socket)
{
command_socket->sendProgressStage(stage);
}
if (time_keeper)
{
if ((int)stage > 0)
{
log("Progress: %s accomplished in %5.3fs\n", names[(int)stage-1].c_str(), time_keeper->restart());
}
else
{
time_keeper->restart();
}
if ((int)stage < (int)Stage::FINISH)
log("Starting %s...\n", names[(int)stage].c_str());
}
}
}// namespace cura
+75
Ver Arquivo
@@ -0,0 +1,75 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_H
#define PROGRESS_H
#include <string>
#include "utils/logoutput.h"
#include "utils/gettime.h"
namespace cura {
class CommandSocket;
#define N_PROGRESS_STAGES 8
/*!
* Class for handling the progress bar and the progress logging.
*
* The progress bar is based on a single slicing of a rather large model which needs some complex support;
* the relative timing of each stage is currently based on that of the slicing of dragon_65_tilted_large.stl
*/
class Progress
{
public:
/*!
* The stage in the whole slicing process
*/
enum class Stage : unsigned int
{
START = 0,
SLICING = 1,
PARTS = 2,
INSET = 3,
SUPPORT = 4,
SKIN = 5,
EXPORT = 6,
FINISH = 7
};
private:
static double times [N_PROGRESS_STAGES]; //!< Time estimates per stage
static std::string names[N_PROGRESS_STAGES]; //!< name of each stage
static double accumulated_times [N_PROGRESS_STAGES]; //!< Time past before each stage
static double total_timing; //!< An estimate of the total time
/*!
* Give an estimate between 0 and 1 of how far the process is.
*
* \param stage The current stage of processing
* \param stage_process How far we currently are in the \p stage
* \return An estimate of the overall progress.
*/
static float calcOverallProgress(Stage stage, float stage_progress);
public:
static void init(); //!< Initialize some values needed in a fast computation of the progress
/*!
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
*
* \param stage The current stage of processing
* \param progress_in_stage Any number giving the progress within the stage
* \param progress_in_stage_max The maximal value of \p progress_in_stage
* \param commandSocket The command socket over which to communicate the progress.
*/
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
/*!
* Message the progress stage over the command socket.
*
* \param stage The current stage
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
* \param commandSocket The command socket over which to communicate (optional)
*/
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
};
} // name space cura
#endif//PROGRESS_H
+42 -26
Ver Arquivo
@@ -4,14 +4,17 @@
#include <fstream> // debug IO
#include <unistd.h>
#include "Progress.h"
#include "weaveDataStorage.h"
namespace cura
{
void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
{
int maxz = object->max().z;
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{
wireFrame.meshgroup = meshgroup;
int maxz = meshgroup->max().z;
int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;
@@ -19,13 +22,12 @@ void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
std::vector<cura::Slicer*> slicerList;
for(Mesh& mesh : object->meshes)
for(Mesh& mesh : meshgroup->meshes)
{
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
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++)
@@ -53,17 +55,25 @@ void Weaver::weave(PrintObject* object, 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)
starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
else
starting_point_in_layer = (Point(0,0) + object->max() + object->min()) / 2;
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
logProgress("inset", layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
@@ -97,9 +107,10 @@ void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
logProgress("skin", layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
@@ -131,16 +142,21 @@ void Weaver::weave(PrintObject* object, 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);
}
}
}
@@ -151,7 +167,7 @@ void Weaver::createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer,
{
int64_t bridgable_dist = connectionHeight;
Polygons& polys_below = lower_top_parts;
// Polygons& polys_below = lower_top_parts;
Polygons& polys_here = layer.supported;
Polygons& polys_above = layer_above;
@@ -191,8 +207,8 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
Polygons roof_outlines;
Polygons roof_holes;
{ // split roofs into outlines and holes
std::vector<Polygons> roof_parts = roofs.splitIntoParts();
for (Polygons& roof_part : roof_parts)
std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts();
for (PolygonsPart& roof_part : roof_parts)
{
roof_outlines.add(roof_part[0]);
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
@@ -206,8 +222,8 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
Polygons supporting_outlines;
std::vector<Polygons> supporting_parts = supporting.splitIntoParts();
for (Polygons& supporting_part : supporting_parts)
std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts();
for (PolygonsPart& supporting_part : supporting_parts)
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
@@ -253,11 +269,11 @@ void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int dir
if (floors.size() == 0) return;
std::vector<Polygons> floor_parts = floors.splitIntoParts();
std::vector<PolygonsPart> floor_parts = floors.splitIntoParts();
Polygons floor_outlines;
Polygons floor_holes;
for (Polygons& floor_part : floor_parts)
for (PolygonsPart& floor_part : floor_parts)
{
floor_outlines.add(floor_part[0]);
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
@@ -376,7 +392,7 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
{
const PolygonRef upperPart = parts1[prt];
ClosestPolygonPoint closestInPoly = findClosest(start_close_to, upperPart);
ClosestPolygonPoint closestInPoly = PolygonUtils::findClosest(start_close_to, upperPart);
PolygonRef part_top = result.newPoly();
@@ -387,7 +403,7 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
for (Point upper_point = upperPart[closestInPoly.pos]; found; upper_point = next_upper.location)
{
found = getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
found = PolygonUtils::getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
if (!found)
@@ -437,7 +453,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
for (const Point& upper_point : upperPart)
{
ClosestPolygonPoint lowerPolyPoint = findClosest(upper_point, supporting);
ClosestPolygonPoint lowerPolyPoint = PolygonUtils::findClosest(upper_point, supporting);
Point& lower = lowerPolyPoint.location;
Point3 lower3 = Point3(lower.X, lower.Y, z0);
@@ -469,5 +485,5 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
} // namespace cura
}//namespace cura
+6 -6
Ver Arquivo
@@ -5,9 +5,10 @@
#include "commandSocket.h"
#include "settings.h"
#include "modelFile/modelFile.h" // PrintObject
#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 SettingsBase
class Weaver : public SettingsMessenger, NoCopy
{
friend class Wireframe2gcode;
private:
@@ -40,8 +41,7 @@ private:
public:
Weaver(SettingsBase* settings_base) : SettingsBase(settings_base)
Weaver(SettingsBase* settings_base) : SettingsMessenger(settings_base)
{
initial_layer_thickness = getSettingInMicrons("layer_height_0");
@@ -60,10 +60,10 @@ public:
* This is the main function for Neith / Weaving / WirePrinting / Webbed printing.
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
*
* \param object The object for which to create a wireframe print
* \param objects The objects for which to create a wireframe print
* \param commandSocket the commandSocket
*/
void weave(PrintObject* object, CommandSocket* commandSocket);
void weave(MeshGroup* objects, CommandSocket* commandSocket);
private:
+113 -39
Ver Arquivo
@@ -4,39 +4,38 @@
#include <fstream> // debug IO
#include "weaveDataStorage.h"
#include "Progress.h"
#include "pathOrderOptimizer.h" // for skirt
namespace cura
{
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket, int& maxObjectHeight)
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
{
gcode.preSetup(wireFrame.meshgroup);
if (commandSocket)
commandSocket->beginGCode();
maxObjectHeight = wireFrame.layers.back().z1;
processStartingCode(commandSocket);
{ // starting Gcode
if (hasSetting("material_bed_temperature") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
if (hasSetting("material_print_temperature") && getSettingInDegreeCelsius("material_print_temperature") > 0)
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
if (gcode.getFlavor() == GCODE_FLAVOR_BFB)
{
gcode.writeComment("enable auto-retraction");
std::ostringstream tmp;
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
gcode.writeLine(tmp.str().c_str());
}
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");
@@ -77,18 +76,16 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket, int& maxObjectHei
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
logProgress("export", layer_nr+1, totalLayers); // abuse the progress system of the normal mode of CuraEngine
if (commandSocket) commandSocket->sendProgress(2.0/3.0 + 1.0/3.0 * float(layer_nr) / float(totalLayers));
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];
gcode.writeLayerComment(layer_nr+1);
int fanSpeed = getSettingInPercentage("cool_fan_speed_max");
double fanSpeed = getSettingInPercentage("cool_fan_speed_max");
if (layer_nr == 0)
fanSpeed = getSettingInPercentage("cool_fan_speed_min");
gcode.writeFanCommand(fanSpeed);
@@ -168,12 +165,10 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket, int& maxObjectHei
gcode.writeFanCommand(0);
finalize();
if (commandSocket)
{
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
for(int e=0; e<MAX_EXTRUDERS; e++)
gcode.writeTemperatureCommand(e, 0, false);
commandSocket->sendGCodeLayer();
commandSocket->endSendSlicedObject();
}
@@ -244,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("retractionAmount"))
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;
@@ -406,16 +404,16 @@ void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionP
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& roof_outlines
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& roof_outlines
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler)
{
// bottom:
gcode.writeTypeComment("FILL");
for (unsigned int inset_idx = 0; inset_idx < fill_insets.size(); inset_idx++)
for (unsigned int inset_idx = 0; inset_idx < infill_insets.size(); inset_idx++)
{
WeaveRoofPart& inset = fill_insets[inset_idx];
WeaveRoofPart& inset = infill_insets[inset_idx];
for (unsigned int inset_part_nr = 0; inset_part_nr < inset.connections.size(); inset_part_nr++)
@@ -484,10 +482,10 @@ void Wireframe2gcode::writeMoveWithRetract(Point to)
}
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
: 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");
@@ -500,8 +498,8 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
double filament_area = /* M_PI * */ (INT2MM(filament_diameter) / 2.0) * (INT2MM(filament_diameter) / 2.0);
double lineArea = /* M_PI * */ (INT2MM(extrusionWidth) / 2.0) * (INT2MM(extrusionWidth) / 2.0);
extrusion_per_mm_connection = lineArea / filament_area * double(flowConnection) / 100.0;
extrusion_per_mm_flat = lineArea / filament_area * double(flowFlat) / 100.0;
extrusion_per_mm_connection = lineArea / filament_area * flowConnection / 100.0;
extrusion_per_mm_flat = lineArea / filament_area * flowFlat / 100.0;
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | .
@@ -542,14 +540,90 @@ 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)
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
if (!command_socket)
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
}
}
else
{
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"))
{
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);
}
}
}
}
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
if (gcode.getFlavor() == EGCodeFlavor::BFB)
{
gcode.writeComment("enable auto-retraction");
std::ostringstream tmp;
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000);
gcode.writeLine(tmp.str().c_str());
}
}
} // namespace cura
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(Point(INT32_MIN, INT32_MIN));
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_idx = 0; poly_idx < skirt.size(); poly_idx++)
{
unsigned int actual_poly_idx = order.polyOrder[poly_idx];
PolygonRef poly = skirt[actual_poly_idx];
gcode.writeMove(poly[order.polyStart[actual_poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[actual_poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
}
}
}
void Wireframe2gcode::finalize()
{
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);
}
}//namespace cura
+51 -34
Ver Arquivo
@@ -4,11 +4,13 @@
#include <functional> // passing function pointer or lambda as argument to a function
#include "utils/NoCopy.h"
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings.h"
#include "modelFile/modelFile.h" // PrintObject
#include "MeshGroup.h"
#include "slicer.h"
#include "utils/polygon.h"
@@ -22,7 +24,7 @@ namespace cura
/*!
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
*/
class Wireframe2gcode : public SettingsBase
class Wireframe2gcode : public SettingsMessenger, NoCopy
{
private:
static const int STRATEGY_COMPENSATE = 0;
@@ -32,35 +34,35 @@ private:
int initial_layer_thickness;
int filament_diameter;
int extrusionWidth;
int flowConnection;// = getSettingInt("wireframeFlowConnection");
int flowFlat; // = getSettingInt("wireframeFlowFlat");
double extrusion_per_mm_connection; // = lineArea / filament_area * double(flowConnection) / 100.0;
double extrusion_per_mm_flat; // = lineArea / filament_area * double(flowFlat) / 100.0;
int nozzle_outer_diameter; // = getSettingInt("machineNozzleTipOuterDiameter"); // ___ ___ .
int nozzle_head_distance; // = getSettingInt("machineNozzleHeadDistance"); // | | .
int nozzle_expansion_angle; // = getSettingInt("machineNozzleExpansionAngle"); // \_U_/ .
int nozzle_clearance; // = getSettingInt("wireframeNozzleClearance"); // at least line width
int nozzle_top_diameter; // = tan(static_cast<double>(nozzle_expansion_angle)/180.0 * M_PI) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
int moveSpeed; // = 40;
int speedBottom; // = getSettingInt("wireframePrintspeedBottom");
int speedUp; // = getSettingInt("wireframePrintspeedUp");
int speedDown; // = getSettingInt("wireframePrintspeedDown");
int speedFlat; // = getSettingInt("wireframePrintspeedFlat");
int connectionHeight; // = getSettingInt("wireframeConnectionHeight");
int roof_inset; // = getSettingInt("wireframeRoofInset");
double flat_delay; // = getSettingInt("wireframeFlatDelay")/100.0;
double bottom_delay; // = getSettingInt("wireframeBottomDelay")/100.0;
double top_delay; // = getSettingInt("wireframeTopDelay")/100.0;
int up_dist_half_speed; // = getSettingInt("wireframeUpDistHalfSpeed");
int top_jump_dist; // = getSettingInt("wireframeTopJump");
int fall_down; // = getSettingInt("wireframeFallDown");
int drag_along; // = getSettingInt("wireframeDragAlong");
int strategy; // = getSettingInt("wireframeStrategy"); // HIGHER_BEND_NO_STRAIGHTEN; // RETRACT_TO_STRAIGHTEN; // MOVE_TO_STRAIGHTEN; //
double go_back_to_last_top; // = false;
int straight_first_when_going_down; // = getSettingInt("wireframeStraightBeforeDown"); // %
int roof_fall_down; // = getSettingInt("wireframeRoofFallDown");
int roof_drag_along; // = getSettingInt("wireframeRoofDragAlong");
double roof_outer_delay; // = getSettingInt("wireframeRoofOuterDelay")/100.0;
double flowConnection;
double flowFlat;
double extrusion_per_mm_connection;
double extrusion_per_mm_flat;
int nozzle_outer_diameter;
int nozzle_head_distance;
double nozzle_expansion_angle;
int nozzle_clearance;
int nozzle_top_diameter;
double moveSpeed;
double speedBottom;
double speedUp;
double speedDown;
double speedFlat;
int connectionHeight;
int roof_inset;
double flat_delay;
double bottom_delay;
double top_delay;
int up_dist_half_speed;
int top_jump_dist;
int fall_down;
int drag_along;
int strategy;
double go_back_to_last_top;
int straight_first_when_going_down;
int roof_fall_down;
int roof_drag_along;
double roof_outer_delay;
RetractionConfig standard_retraction_config; //!< The standard retraction settings used for moves between parts etc.
@@ -69,13 +71,28 @@ public:
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
void writeGCode(CommandSocket* commandSocket, int& maxObjectHeight);
void writeGCode(CommandSocket* commandSocket);
private:
WireFrame wireFrame;
WireFrame& wireFrame;
void writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& outlines
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
*/
void processStartingCode(CommandSocket* command_socket);
/*!
* Lay down a skirt
*/
void processSkirt(CommandSocket* commandSocket);
/*!
* End gcode: nozzle temp down
*/
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
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler);
+2
Ver Arquivo
@@ -1,6 +1,8 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "bridge.h"
#include "sliceDataStorage.h"
namespace cura {
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
+2 -2
Ver Arquivo
@@ -2,9 +2,9 @@
#ifndef BRIDGE_H
#define BRIDGE_H
#include "sliceDataStorage.h"
namespace cura {
class Polygons;
class SliceLayer;
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
+339 -185
Ver Arquivo
@@ -1,236 +1,390 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "comb.h"
#include <algorithm>
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "utils/SVG.h"
namespace cura {
bool Comb::preTest(Point startPoint, Point endPoint)
// boundary_outside is only computed when it's needed!
Polygons* Comb::getBoundaryOutside()
{
if (!boundary_outside)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
}
return boundary_outside;
}
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, avoid_other_parts(travel_avoid_other_parts)
// , boundary_inside( boundary.offset(-offset_from_outlines) ) // TODO: make inside boundary configurable?
, boundary_inside( comb_boundary_inside )
, boundary_outside(nullptr)
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
{
return collisionTest(startPoint, endPoint);
}
bool Comb::collisionTest(Point startPoint, Point endPoint)
Comb::~Comb()
{
if (boundary_outside)
delete boundary_outside;
}
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
{
return true;
}
//Move start and end point inside the comb boundary
unsigned int start_inside_poly = NO_INDEX;
if (startInside)
{
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
if (!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
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
}
if (start_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
{
startInside = false;
}
}
}
unsigned int end_inside_poly = NO_INDEX;
if (endInside)
{
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
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
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
}
if (end_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
{
endInside = false;
}
}
}
unsigned int start_part_boundary_poly_idx;
unsigned int end_part_boundary_poly_idx;
unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx);
unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx);
if (startInside && endInside && start_part_idx == end_part_idx)
{ // 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, 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)
{
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]);
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
middle_from = middle_from_cp.location;
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 if(!startInside && !endInside)
{
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)
{
// 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, 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
if (avoid_other_parts)
{
Polygons& middle = *getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
Point from_outside = middle_from;
if (startInside || middle.inside(from_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, from_outside, -offset_extra_start_end, max_moveInside_distance2);
}
Point to_outside = middle_to;
if (endInside || middle.inside(to_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, to_outside, -offset_extra_start_end, max_moveInside_distance2);
}
combPaths.emplace_back();
combPaths.back().throughAir = true;
if ( vSize(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(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, max_comb_distance_ignored);
}
}
else
{ // directly through air (not avoiding other parts)
combPaths.emplace_back();
combPaths.back().throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
if (endInside)
{
// boundary to end
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
combPaths.emplace_back();
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;
}
}
void LinePolygonsCrossings::calcScanlineCrossings()
{
min_crossing_idx = NO_INDEX;
max_crossing_idx = NO_INDEX;
for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
{
PolyCrossings minMax(poly_idx);
PolygonRef poly = boundary[poly_idx];
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(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) //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;
}
if (minMax.min.point_idx != NO_INDEX)
{ // then also max.point_idx != -1
if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
crossings.push_back(minMax);
}
}
}
bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
{
Point diff = endPoint - startPoint;
matrix = PointMatrix(diff);
sp = matrix.apply(startPoint);
ep = matrix.apply(endPoint);
for(unsigned int n=0; n<boundery.size(); n++)
transformation_matrix = PointMatrix(diff);
transformed_startPoint = transformation_matrix.apply(startPoint);
transformed_endPoint = transformation_matrix.apply(endPoint);
for(PolygonRef poly : boundary)
{
if (boundery[n].size() < 1)
continue;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
Point p0 = transformation_matrix.apply(poly.back());
for(Point p1_ : poly)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
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))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x > sp.X && x < ep.X)
if (x > transformed_startPoint.X && x < transformed_endPoint.X)
return true;
}
p0 = p1;
}
}
return false;
}
void Comb::calcMinMax()
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
{
for(unsigned int n=0; n<boundery.size(); n++)
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
{
minX[n] = INT64_MAX;
maxX[n] = INT64_MIN;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= sp.X && x <= ep.X)
{
if (x < minX[n]) { minX[n] = x; minIdx[n] = i; }
if (x > maxX[n]) { maxX[n] = x; maxIdx[n] = i; }
}
}
p0 = p1;
}
//We're not crossing any boundaries. So skip the comb generation.
combPath.push_back(startPoint);
combPath.push_back(endPoint);
return;
}
calcScanlineCrossings();
CombPath basicPath;
getBasicCombingPath(basicPath);
optimizePath(basicPath, combPath);
}
unsigned int Comb::getPolygonAbove(int64_t x)
void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
{
int64_t min = POINT_MAX;
unsigned int ret = NO_INDEX;
for(unsigned int n=0; n<boundery.size(); n++)
for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
; crossing != nullptr
; crossing = getNextPolygonAlongScanline(crossing->max.x))
{
if (minX[n] > x && minX[n] < min)
getBasicCombingPath(*crossing, combPath);
}
combPath.push_back(endPoint);
}
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
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
for(unsigned int point_idx = polyCrossings.min.point_idx
; point_idx != polyCrossings.max.point_idx
; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
{
min = minX[n];
ret = n;
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
else
{ // 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 - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
}
LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
{
PolyCrossings* ret = nullptr;
for(PolyCrossings& crossing : crossings)
{
if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
{
ret = &crossing;
}
}
return ret;
}
Point Comb::getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx)
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
Point p0 = boundery[polygonNr][(idx > 0) ? (idx - 1) : (boundery[polygonNr].size() - 1)];
Point p1 = boundery[polygonNr][idx];
Point p2 = boundery[polygonNr][(idx < (boundery[polygonNr].size() - 1)) ? (idx + 1) : (0)];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0)));
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0)));
Point n = normal(off0 + off1, MM2INT(0.2));
return p1 + n;
}
Comb::Comb(Polygons& _boundery)
: boundery(_boundery)
{
minX = new int64_t[boundery.size()];
maxX = new int64_t[boundery.size()];
minIdx = new unsigned int[boundery.size()];
maxIdx = new unsigned int[boundery.size()];
}
Comb::~Comb()
{
delete[] minX;
delete[] maxX;
delete[] minIdx;
delete[] maxIdx;
}
bool Comb::moveInside(Point* p, int distance)
{
Point ret = *p;
int64_t bestDist = MM2INT(2.0) * MM2INT(2.0);
for(unsigned int n=0; n<boundery.size(); n++)
optimized_comb_path.push_back(startPoint);
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
{
if (boundery[n].size() < 1)
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
{
continue;
Point p0 = boundery[n][boundery[n].size()-1];
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = boundery[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
int64_t distOnLine = dot(pDiff, *p - p0) / lineLength;
if (distOnLine < 10)
distOnLine = 10;
if (distOnLine > lineLength - 10)
distOnLine = lineLength - 10;
Point q = p0 + pDiff * distOnLine / lineLength;
int64_t dist = vSize2(q - *p);
if (dist < bestDist)
{
bestDist = dist;
ret = q + crossZ(normal(p1 - p0, distance));
}
p0 = p1;
}
}
if (bestDist < MM2INT(2.0) * MM2INT(2.0))
{
*p = ret;
return true;
}
return false;
}
bool Comb::calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, MM2INT(1.5)))
return true;
bool addEndpoint = false;
//Check if we are inside the comb boundaries
if (!boundery.inside(startPoint))
{
if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
combPoints.push_back(startPoint);
}
if (!boundery.inside(endPoint))
{
if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
addEndpoint = true;
}
//Check if we are crossing any bounderies, and pre-calculate some values.
if (!preTest(startPoint, endPoint))
{
//We're not crossing any boundaries. So skip the comb generation.
if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point.
return true;
}
//Calculate the minimum and maximum positions where we cross the comb boundary
calcMinMax();
int64_t x = sp.X;
std::vector<Point> pointList;
//Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
// to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross.
// This gives a path from the start to finish curved around the holes that it encounters.
while(true)
{
unsigned int n = getPolygonAbove(x);
if (n == NO_INDEX) break;
pointList.push_back(matrix.unapply(Point(minX[n] - MM2INT(0.2), sp.Y)));
if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size())
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
{
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0))
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
comb_path.cross_boundary = true;
}
}else{
if (minIdx[n] == 0)
minIdx[n] = boundery[n].size() - 1;
else
minIdx[n]--;
if (maxIdx[n] == 0)
maxIdx[n] = boundery[n].size() - 1;
else
maxIdx[n]--;
optimized_comb_path.push_back(comb_path[point_idx - 1]);
}
else
{
// : dont add the newest point
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1))
// TODO: add the below extra optimization? (+/- 7% extra computation time, +/- 2% faster print for Dual_extrusion_support_generation.stl)
while (optimized_comb_path.size() > 1)
{
pointList.push_back(getBounderyPointWithOffset(n, i));
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx]))
{
break;
}
else
{
optimized_comb_path.pop_back();
}
}
}
pointList.push_back(matrix.unapply(Point(maxX[n] + MM2INT(0.2), sp.Y)));
x = maxX[n];
}
pointList.push_back(endPoint);
//Optimize the pointList, skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip any unneeded corners.
Point p0 = startPoint;
for(unsigned int n=1; n<pointList.size(); n++)
{
if (collisionTest(p0, pointList[n]))
{
if (collisionTest(p0, pointList[n-1]))
return false;
p0 = pointList[n-1];
combPoints.push_back(p0);
}
}
if (addEndpoint)
combPoints.push_back(endPoint);
return true;
}
+238 -25
Ver Arquivo
@@ -4,39 +4,252 @@
#include "utils/polygon.h"
namespace cura {
namespace cura
{
struct CombPath : public std::vector<Point> //!< A single path either inside or outise the parts
{
bool throughAir = false; //!< Whether the path is one which moves through air.
bool cross_boundary = false; //!< Whether the path crosses a boundary.
};
struct CombPaths : public std::vector<CombPath> //!< A list of paths alternating between inside a part and outside a part
{
};
class Comb
/*!
* Class for generating a combing move action from point a to point b and avoiding collision with other parts when moving through air.
* See LinePolygonsCrossings::comb.
*
* The general implementation is by rotating everything such that the the line segment from a to b is aligned with the x-axis.
* We call the line on which a and b lie the 'scanline'.
*
* The basic path is generated by following the scanline until it hits a polygon, then follow the polygon until the last point where it hits the scanline,
* follow the scanline again, etc.
* The path is offsetted from the polygons, so that it doesn't intersect with them.
*
* Next the basic path is optimized by taking shortcuts where possible. Only shortcuts which skip a single point are considered, in order to reduce computational complexity.
*/
class LinePolygonsCrossings
{
private:
Polygons& boundery;
int64_t* minX;
int64_t* maxX;
unsigned int* minIdx;
unsigned int* maxIdx;
PointMatrix matrix;
Point sp;
Point ep;
bool preTest(Point startPoint, Point endPoint);
bool collisionTest(Point startPoint, Point endPoint);
void calcMinMax();
unsigned int getPolygonAbove(int64_t x);
/*!
* A Crossing holds data on a single point where a polygon crosses the scanline.
*/
struct Crossing
{
int64_t x; //!< x coordinate of crossings between the polygon and the scanline.
unsigned int point_idx; //!< The index of the first point of the line segment which crosses the scanline
/*!
* Creates a Crossing with minimal initialization
* \param x The x-coordinate in transformed space
* \param point_idx The index of the first point of the line segment which crosses the scanline
*/
Crossing(int64_t x, unsigned int point_idx)
: x(x), point_idx(point_idx)
{
}
};
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx);
/*!
* A PolyCrossings holds data on where a polygon crosses the scanline. Only the Crossing with lowest Crossing::x and highest are recorded.
*/
struct PolyCrossings
{
unsigned int poly_idx; //!< The index of the polygon which crosses the scanline
Crossing min; //!< The point where the polygon first crosses the scanline.
Crossing max; //!< The point where the polygon last crosses the scanline.
/*!
* Create a PolyCrossings with minimal initialization. PolyCrossings::min and PolyCrossings::max are not yet computed.
* \param poly_idx The index of the polygon in LinePolygonsCrossings::boundary
*/
PolyCrossings(unsigned int poly_idx)
: poly_idx(poly_idx)
, min(INT64_MAX, NO_INDEX), max(INT64_MIN, NO_INDEX)
{
}
};
/*!
* A PolyCrossings list: for every polygon a PolyCrossings.
*/
struct PartCrossings : public std::vector<PolyCrossings>
{
//unsigned int part_idx;
};
PartCrossings crossings; //!< All crossings of polygons in the LinePolygonsCrossings::boundary with the scanline.
unsigned int min_crossing_idx; //!< The index into LinePolygonsCrossings::crossings to the crossing with the minimal PolyCrossings::min crossing of all PolyCrossings's.
unsigned int max_crossing_idx; //!< The index into LinePolygonsCrossings::crossings to the crossing with the maximal PolyCrossings::max crossing of all PolyCrossings's.
Polygons& boundary; //!< The boundary not to cross during combing.
Point startPoint; //!< The start point of the scanline.
Point endPoint; //!< The end point of the scanline.
int64_t dist_to_move_boundary_point_outside; //!< The distance used to move outside or inside so that a boundary point doesn't intersect with the boundary anymore. Neccesary due to computational rounding problems. Use negative value for insicde combing.
PointMatrix transformation_matrix; //!< The transformation which rotates everything such that the scanline is aligned with the x-axis.
Point transformed_startPoint; //!< The LinePolygonsCrossings::startPoint as transformed by Comb::transformation_matrix
Point transformed_endPoint; //!< The LinePolygonsCrossings::endPoint as transformed by Comb::transformation_matrix
/*!
* Check if we are crossing the boundaries, and pre-calculate some values.
*
* Sets Comb::transformation_matrix, Comb::transformed_startPoint and Comb::transformed_endPoint
* \return Whether the line segment from LinePolygonsCrossings::startPoint to LinePolygonsCrossings::endPoint collides with the boundary
*/
bool lineSegmentCollidesWithBoundary();
/*!
* Calculate Comb::crossings, Comb::min_crossing_idx and Comb::max_crossing_idx.
*/
void calcScanlineCrossings();
/*!
* Get the basic combing path and optimize it.
*
* \param combPath Output parameter: the points along the combing path.
*/
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.
*
* Walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
* to the \p combPath and continue with the next boundary we will cross, until there are no more boundaries to cross.
* This gives a path from the start to finish curved around the holes that it encounters.
*
* \param combPath Output parameter: the points along the combing path.
*/
void getBasicCombingPath(CombPath& combPath);
/*!
* Get the basic combing path, following a single boundary polygon when it hits it, until it passes the scanline again.
*
* Find the initial cross point and the exit point. Then add all the points in between
* to the \p combPath and continue with the next boundary we will cross, until there are no more boundaries to cross.
* This gives a path from the start to finish curved around the polygon that it encounters.
*
* \param combPath Output parameter: where to add the points along the combing path.
*/
void getBasicCombingPath(PolyCrossings& crossings, CombPath& combPath);
/*!
* Find the first polygon cutting the scanline after \p x.
*
* Note that this function only looks at the first segment cutting the scanline (see Comb::minX)!
* It doesn't return the next polygon which crosses the scanline, but the first polygon crossing the scanline for the first time.
*
* \param x The point on the scanline from where to look.
* \return The next PolyCrossings fully beyond \p x or one with PolyCrossings::poly_idx set to NO_INDEX if there's none left.
*/
PolyCrossings* getNextPolygonAlongScanline(int64_t x);
/*!
* Optimize the \p comb_path: skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip some unneeded corners.
*
* \param comb_path The unoptimized combing path.
* \param optimized_comb_path Output parameter: The points of optimized combing path
* \return Whether it turns out that the basic comb path already crossed a boundary
*/
bool optimizePath(CombPath& comb_path, CombPath& optimized_comb_path);
/*!
* Create a LinePolygonsCrossings with minimal initialization.
* \param boundary The boundary which not to cross during combing
* \param start the starting point
* \param end the end point
* \param dist_to_move_boundary_point_outside Distance used to move a point from a boundary so that it doesn't intersect with it anymore. (Precision issue)
*/
LinePolygonsCrossings(Polygons& boundary, Point& start, Point& end, int64_t dist_to_move_boundary_point_outside)
: boundary(boundary), startPoint(start), endPoint(end), dist_to_move_boundary_point_outside(dist_to_move_boundary_point_outside)
{
}
public:
/*!
* The main function of this class: calculate one combing path within the boundary.
* \param boundary The polygons to follow when calculating the basic combing path
* \param startPoint From where to start the combing move.
* \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, int64_t max_comb_distance_ignored = MM2INT(1.5))
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
};
};
class SliceDataStorage;
/*!
* Class for generating a full combing actions from a travel move from a start point to an end point.
* A single Comb object is used for each layer.
*
* Comb::calc is the main function of this class.
*
* Typical output: A combing path to the boundary of the polygon + a move through air avoiding other parts in the layer + a combing path from the boundary of the ending polygon to the end point.
* Each of these three is a CombPath; the first and last are within Comb::boundary_inside while the middle is outside of Comb::boundary_outside.
* Between these there is a little gap where the nozzle crosses the boundary of an object approximately perpendicular to its boundary.
*
* As an optimization, the combing paths inside are calculated on specifically those PolygonsParts within which to comb, while the coundary_outside isn't split into outside parts,
* because generally there is only one outside part; encapsulated holes occur less often.
*/
class Comb
{
friend class LinePolygonsCrossings;
private:
SliceDataStorage& storage; //!< The storage from 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 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_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.
/*!
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
*/
Polygons* getBoundaryOutside();
public:
Comb(Polygons& _boundery);
/*!
* 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, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~Comb();
bool inside(const Point p) { return boundery.inside(p); }
bool moveInside(Point* p, int distance = 100);
bool calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints);
/*!
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
*
* \param startPoint Where to start moving from
* \param endPoint Where to move to
* \param combPoints Output parameter: The points along the combing path, excluding the \p startPoint (?) and \p endPoint
* \param startInside Whether we want to start inside the comb boundary
* \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, int64_t max_comb_distance_ignored = MM2INT(1.5));
};
}//namespace cura
+224 -95
Ver Arquivo
@@ -1,202 +1,308 @@
#include "utils/logoutput.h"
#include "commandSocket.h"
#include "fffProcessor.h"
#include "FffProcessor.h"
#include "Progress.h"
#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:
Private()
: processor(nullptr)
, socket(nullptr)
: socket(nullptr)
, object_count(0)
, current_object_number(0)
, currentSlicedObject(nullptr)
, slicedObjects(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
Cura::Layer* getLayerById(int id);
fffProcessor* processor;
cura::proto::Layer* getLayerById(int id);
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
int current_object_number;
// Message that holds a list of sliced objects
std::shared_ptr<cura::proto::SlicedObjectList> sliced_object_list;
// Message that holds the currently sliced object (to be added to sliced_object_list)
cura::proto::SlicedObject* current_sliced_object;
// Number of sliced objects for this sliced object list
int sliced_objects;
std::shared_ptr<Cura::SlicedObjectList> slicedObjectList;
Cura::SlicedObject* currentSlicedObject;
int slicedObjects;
std::vector<int64_t> objectIds;
// 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;
std::string tempGCodeFile;
std::string temp_gcode_file;
std::ostringstream gcode_output_stream;
std::shared_ptr<PrintObject> objectToSlice;
// 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(fffProcessor* processor)
CommandSocket::CommandSocket()
#ifdef ARCUS
: d(new Private)
#endif
{
d->processor = processor;
d->processor->setCommandSocket(this);
#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(2, &Cura::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &Cura::Progress::default_instance());
d->socket->registerMessageType(4, &Cura::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &Cura::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &Cura::SettingList::default_instance());
d->socket->registerMessageType(7, &Cura::GCodePrefix::default_instance());
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
d->socket->connect(ip, port);
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error)
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 && slice_another_time)
{
if(d->objectToSlice)
//If there is an object to slice, do so.
if(d->objects_to_slice.size())
{
//TODO: Support all-at-once/one-at-a-time printing
d->processor->processModel(d->objectToSlice.get());
d->objectToSlice.reset();
d->processor->resetFileNumber();
FffProcessor::getInstance()->resetFileNumber();
for(auto object : d->objects_to_slice)
{
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();
//d->processor->resetFileNumber();
//sendPrintTime();
}
// Actually start handling messages.
Arcus::MessagePtr message = d->socket->takeNextMessage();
Cura::SettingList* settingList = dynamic_cast<Cura::SettingList*>(message.get());
if(settingList)
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if(setting_list)
{
handleSettingList(settingList);
handleSettingList(setting_list);
}
Cura::ObjectList* objectList = dynamic_cast<Cura::ObjectList*>(message.get());
if(objectList)
/*cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
if(object_list)
{
handleObjectList(objectList);
handleObjectList(object_list);
}*/
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get());
if(slice)
{
// Reset object counts
d->object_count = 0;
d->object_ids.clear();
for(auto object : slice->object_lists())
{
handleObjectList(&object);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if(!d->socket->errorString().empty()) {
if(!d->socket->errorString().empty())
{
logError("%s\n", d->socket->errorString().data());
d->socket->clearError();
}
}
#endif
}
void CommandSocket::handleObjectList(Cura::ObjectList* list)
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
{
FMatrix3x3 matrix;
d->object_count = 0;
d->objectIds.clear();
d->objectToSlice = std::make_shared<PrintObject>(d->processor);
//d->object_count = 0;
//d->object_ids.clear();
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
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())
{
d->objectToSlice->meshes.emplace_back(d->objectToSlice.get()); //Construct a new mesh and put it into printObject's mesh list.
Mesh& mesh = d->objectToSlice->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);
int bytesPerFace = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int faceCount = object.vertices().size() / bytesPerFace;
for(int i = 0; i < faceCount; ++i)
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;
for(int i = 0; i < face_count; ++i)
{
//TODO: Apply matrix
std::string data = object.vertices().substr(i * bytesPerFace, bytesPerFace);
const FPoint3* floatVerts = reinterpret_cast<const FPoint3*>(data.data());
std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face);
const FPoint3* float_vertices = reinterpret_cast<const FPoint3*>(data.data());
Point3 verts[3];
verts[0] = matrix.apply(floatVerts[0]);
verts[1] = matrix.apply(floatVerts[1]);
verts[2] = matrix.apply(floatVerts[2]);
verts[0] = matrix.apply(float_vertices[0]);
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());
}
d->objectIds.push_back(object.id());
d->object_ids.push_back(object.id());
mesh.finish();
}
d->object_count++;
d->objectToSlice->finalize();
meshgroup->finalize();
}
void CommandSocket::handleSettingList(Cura::SettingList* list)
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
{
for(auto setting : list->settings())
{
d->processor->setSetting(setting.name(), setting.value());
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
if(!d->currentSlicedObject)
#ifdef ARCUS
if(!d->current_sliced_object)
{
return;
}
Cura::Layer* layer = d->getLayerById(layer_nr);
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)
{
if(!d->currentSlicedObject)
#ifdef ARCUS
if(!d->current_sliced_object)
return;
if (polygons.size() == 0)
return;
Cura::Layer* layer = d->getLayerById(layer_nr);
cura::proto::Layer* layer = d->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
Cura::Polygon* p = layer->add_polygons();
p->set_type(static_cast<Cura::Polygon_Type>(type));
cura::proto::Polygon* p = layer->add_polygons();
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
std::string polydata;
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
p->set_points(polydata);
p->set_line_width(line_width);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
auto message = std::make_shared<Cura::Progress>();
#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)
{
// TODO
}
void CommandSocket::sendPrintTime()
{
auto message = std::make_shared<Cura::ObjectPrintTime>();
message->set_time(d->processor->getTotalPrintTime());
message->set_material_amount(d->processor->getTotalFilamentUsed(0));
#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)
@@ -210,65 +316,88 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
void CommandSocket::beginSendSlicedObject()
{
if(!d->slicedObjectList)
#ifdef ARCUS
if(!d->sliced_object_list)
{
d->slicedObjectList = std::make_shared<Cura::SlicedObjectList>();
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
}
d->currentSlicedObject = d->slicedObjectList->add_objects();
d->currentSlicedObject->set_id(d->objectIds[d->slicedObjects]);
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()
{
d->slicedObjects++;
if(d->slicedObjects >= d->object_count)
#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->slicedObjectList);
d->slicedObjects = 0;
d->slicedObjectList.reset();
d->currentSlicedObject = nullptr;
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()
{
d->processor->setTargetStream(&d->gcode_output_stream);
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
#endif
}
void CommandSocket::sendGCodeLayer()
{
auto message = std::make_shared<Cura::GCodeLayer>();
message->set_id(d->objectIds[0]);
#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)
{
auto message = std::make_shared<Cura::GCodePrefix>();
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
message->set_data(prefix);
d->socket->sendMessage(message);
#endif
}
Cura::Layer* CommandSocket::Private::getLayerById(int id)
#ifdef ARCUS
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
auto itr = std::find_if(currentSlicedObject->mutable_layers()->begin(), currentSlicedObject->mutable_layers()->end(), [id](Cura::Layer& l) { return l.id() == id; });
id += current_layer_offset;
Cura::Layer* layer = nullptr;
if(itr != currentSlicedObject->mutable_layers()->end())
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;
if(itr != current_sliced_object->mutable_layers()->end())
{
layer = &(*itr);
}
else
{
layer = currentSlicedObject->add_layers();
layer = current_sliced_object->add_layers();
layer->set_id(id);
current_layer_count++;
}
return layer;
}
#endif
}//namespace cura
+51 -6
Ver Arquivo
@@ -4,28 +4,71 @@
#include "utils/socket.h"
#include "utils/polygon.h"
#include "settings.h"
#include "Progress.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura {
namespace cura
{
class fffProcessor;
class CommandSocket
{
public:
CommandSocket(fffProcessor* processor);
CommandSocket();
/*!
* Connect with the GUI
* This creates and initialises the arcus socket and then continues listening for messages.
* \param ip string containing the ip to connect with
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
void handleObjectList(Cura::ObjectList* list);
void handleSettingList(Cura::SettingList* list);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
*/
void handleObjectList(cura::proto::ObjectList* list);
/*!
* Handler for SettingList message.
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
#endif
/*!
* Does nothing at the moment
*/
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the engine. This is used for the layerview in the GUI
*/
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
*/
void sendProgress(float amount);
/*!
* Send the current stage of the process to the GUI (starting, slicing infill, etc)
*/
void sendProgressStage(Progress::Stage stage);
/*!
* Send time estimate of how long print would take.
*/
void sendPrintTime();
/*!
* Does nothing at the moment
*/
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
void beginSendSlicedObject();
@@ -35,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
+1 -1
Ver Arquivo
@@ -14,7 +14,7 @@
#define DEBUG 1
#define DEBUG_SHOW_LINE 0
#define DEBUG_SHOW_LINE 1
#if DEBUG_SHOW_LINE == 1
#define DEBUG_FILE_LINE __FILE_NAME__ << "." << __LINE__ << ": "
-1189
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+355 -227
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,73 +9,75 @@
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;
retraction_extrusion_window = 0.0;
extruderSwitchRetraction = 14.5;
current_e_value = 0;
current_extruder = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
{
totalFilament[e] = 0.0;
currentTemperature[e] = 0;
filament_diameter[e] = 0;
}
currentSpeed = 1;
retractionPrimeSpeed = 1;
isRetracted = false;
isZHopped = false;
setFlavor(GCODE_FLAVOR_REPRAP);
memset(extruderOffset, 0, sizeof(extruderOffset));
isZHopped = 0;
setFlavor(EGCodeFlavor::REPRAP);
}
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;
*output_stream << std::fixed;
}
void GCodeExport::setExtruderOffset(int id, Point p)
{
extruderOffset[id] = p;
}
Point GCodeExport::getExtruderOffset(int id)
{
return extruderOffset[id];
return extruder_attr[id].nozzle_offset;
}
void GCodeExport::setSwitchExtruderCode(int id, std::string preSwitchExtruderCode, std::string postSwitchExtruderCode)
Point GCodeExport::getGcodePos(int64_t x, int64_t y, int extruder_train)
{
this->preSwitchExtruderCode[id] = preSwitchExtruderCode;
this->postSwitchExtruderCode[id] = postSwitchExtruderCode;
if (use_extruder_offset_to_offset_coords) { return Point(x,y) - getExtruderOffset(extruder_train); }
else { return Point(x,y); }
}
void GCodeExport::setFlavor(EGCodeFlavor flavor)
{
this->flavor = flavor;
if (flavor == GCODE_FLAVOR_MACH3)
if (flavor == EGCodeFlavor::MACH3)
for(int n=0; n<MAX_EXTRUDERS; n++)
extruderCharacter[n] = 'A' + n;
extruder_attr[n].extruderCharacter = 'A' + n;
else
for(int n=0; n<MAX_EXTRUDERS; n++)
extruderCharacter[n] = 'E';
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
extruder_attr[n].extruderCharacter = 'E';
if (flavor == EGCodeFlavor::ULTIGCODE || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC)
{
is_volumatric = true;
}
else
}
else
{
is_volumatric = false;
}
if (flavor == EGCodeFlavor::BFB || flavor == EGCodeFlavor::REPRAP_VOLUMATRIC || flavor == EGCodeFlavor::ULTIGCODE)
{
firmware_retract = true;
}
else
{
firmware_retract = false;
}
}
EGCodeFlavor GCodeExport::getFlavor()
@@ -82,15 +85,6 @@ EGCodeFlavor GCodeExport::getFlavor()
return this->flavor;
}
void GCodeExport::setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int retraction_extrusion_window, int retraction_count_max)
{
this->extruderSwitchRetraction = INT2MM(extruderSwitchRetraction);
this->extruderSwitchRetractionSpeed = extruderSwitchRetractionSpeed;
this->extruderSwitchPrimeSpeed = extruderSwitchPrimeSpeed;
this->retraction_extrusion_window = INT2MM(retraction_extrusion_window);
this->retraction_count_max = retraction_count_max;
}
void GCodeExport::setZ(int z)
{
this->zPos = z;
@@ -110,43 +104,33 @@ 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;
}
double GCodeExport::getFilamentArea(unsigned int extruder)
void GCodeExport::setFilamentDiameter(unsigned int extruder, int diameter)
{
double r = INT2MM(filament_diameter[extruder]) / 2.0;
double filament_area = M_PI * r * r;
return filament_area;
double r = INT2MM(diameter) / 2.0;
double area = M_PI * r * r;
extruder_attr[extruder].filament_area = area;
}
void GCodeExport::setFilamentDiameter(unsigned int n, int diameter)
{
filament_diameter[n] = diameter;
}
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;
}
}
@@ -154,8 +138,8 @@ double GCodeExport::getExtrusionAmountMM3(unsigned int extruder)
double GCodeExport::getTotalFilamentUsed(int e)
{
if (e == current_extruder)
return totalFilament[e] + getExtrusionAmountMM3(e);
return totalFilament[e];
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
return extruder_attr[e].totalFilament;
}
double GCodeExport::getTotalPrintTime()
@@ -168,10 +152,10 @@ void GCodeExport::resetTotalPrintTimeAndFilament()
totalPrintTime = 0;
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
{
totalFilament[e] = 0.0;
currentTemperature[e] = 0;
extruder_attr[e].totalFilament = 0.0;
extruder_attr[e].currentTemperature = 0;
}
extrusion_amount = 0.0;
current_e_value = 0.0;
estimateCalculator.reset();
}
@@ -183,7 +167,17 @@ void GCodeExport::updateTotalPrintTime()
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)
@@ -195,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";
@@ -202,206 +201,337 @@ void GCodeExport::writeLine(const char* line)
void GCodeExport::resetExtrusionValue()
{
if (extrusion_amount != 0.0 && flavor != GCODE_FLAVOR_MAKERBOT && flavor != GCODE_FLAVOR_BFB)
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruderCharacter[current_extruder] << "0\n";
totalFilament[current_extruder] += getExtrusionAmountMM3(current_extruder);
for (unsigned int i = 0; i < extrusion_amount_at_previous_n_retractions.size(); i++)
extrusion_amount_at_previous_n_retractions[i] -= extrusion_amount;
extrusion_amount = 0.0;
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0\n";
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, int speed, double extrusion_mm3_per_mm)
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
{
writeMove(p.X, p.Y, zPos, speed, extrusion_mm3_per_mm);
}
void GCodeExport::writeMove(Point3 p, int speed, double extrusion_mm3_per_mm)
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::writeMove(int x, int y, int z, int speed, double extrusion_mm3_per_mm)
void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm)
{
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
return;
double extrusion_per_mm = extrusion_mm3_per_mm;
if (!is_volumatric)
{
extrusion_per_mm = extrusion_mm3_per_mm / getFilamentArea(current_extruder);
}
if (flavor == GCODE_FLAVOR_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 != int(rpm * 10))
{
//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 = int(rpm * 10);
}
//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));
//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;
}
}
*output_stream << std::setprecision(3) << "G1 X" << INT2MM(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].Y) << " Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << "\r\n";
}else{
//Normal E handling.
if (extrusion_mm3_per_mm > 0.000001)
{
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
isZHopped = false;
}
if (isRetracted)
{
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_REPRAP_VOLUMATRIC)
{
*output_stream << "G11\n";
//Assume default UM2 retraction settings.
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
}else{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruderCharacter[current_extruder] << 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;
}
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(x - extruderOffset[current_extruder].X) << " Y" << INT2MM(y - extruderOffset[current_extruder].Y);
if (z != currentPosition.z)
*output_stream << " Z" << INT2MM(z);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruderCharacter[current_extruder] << std::setprecision(5) << extrusion_amount;
*output_stream << "\n";
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);
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::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;
#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!");
if (flavor == EGCodeFlavor::BFB)
{
writeMoveBFB(x, y, z, speed, extrusion_mm3_per_mm);
return;
}
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;
}
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
{
*output_stream << "G0";
if (commandSocket)
{
// 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);
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 == GCODE_FLAVOR_BFB)//BitsFromBytes does automatic retraction.
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
{
return;
if (isRetracted)
}
if (extruder_attr[current_extruder].retraction_e_amount_current == config->distance * ((is_volumatric)? extruder_attr[current_extruder].filament_area : 1.0))
{
return;
if (config->amount <= 0)
return;
if (!force && retraction_count_max > 0 && int(extrusion_amount_at_previous_n_retractions.size()) == retraction_count_max - 1
&& extrusion_amount < extrusion_amount_at_previous_n_retractions.back() + retraction_extrusion_window)
}
if (config->distance <= 0)
{
return;
}
if (config->primeAmount > 0)
extrusion_amount += config->primeAmount;
retractionPrimeSpeed = config->primeSpeed;
{ // 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();
}
}
if (flavor == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_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) << " " << extruderCharacter[current_extruder] << 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()) == retraction_count_max)
{
extrusion_amount_at_previous_n_retractions.pop_back();
}
isRetracted = true;
}
void GCodeExport::switchExtruder(int newExtruder)
void GCodeExport::writeRetraction_extruderSwitch()
{
if (current_extruder == newExtruder)
return;
if (flavor == GCODE_FLAVOR_BFB)
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 == GCODE_FLAVOR_ULTIGCODE || flavor == GCODE_FLAVOR_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" << (extruderSwitchRetractionSpeed * 60) << " " << extruderCharacter[current_extruder] << std::setprecision(5) << (extrusion_amount - extruderSwitchRetraction) << "\n";
currentSpeed = extruderSwitchRetractionSpeed;
return;
}
current_extruder = newExtruder;
if (flavor == GCODE_FLAVOR_MACH3)
resetExtrusionValue();
isRetracted = true;
writeCode(preSwitchExtruderCode[current_extruder].c_str());
if (flavor == GCODE_FLAVOR_MAKERBOT)
*output_stream << "M135 T" << current_extruder << "\n";
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;
writeRetraction_extruderSwitch();
resetExtrusionValue(); // should be called on the old extruder
int old_extruder = current_extruder;
current_extruder = new_extruder;
if (flavor == EGCodeFlavor::MACH3)
{
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(postSwitchExtruderCode[current_extruder].c_str());
}
writeCode(extruder_attr[new_extruder].start_code.c_str());
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
currentPosition.z += 1;
@@ -410,26 +540,26 @@ void GCodeExport::switchExtruder(int newExtruder)
void GCodeExport::writeCode(const char* str)
{
*output_stream << str;
if (flavor == GCODE_FLAVOR_BFB)
if (flavor == EGCodeFlavor::BFB)
*output_stream << "\r\n";
else
*output_stream << "\n";
}
void GCodeExport::writeFanCommand(int speed)
void GCodeExport::writeFanCommand(double speed)
{
if (currentFanSpeed == speed)
return;
if (speed > 0)
{
if (flavor == GCODE_FLAVOR_MAKERBOT)
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0\n"; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << "\n";
}
else
{
if (flavor == GCODE_FLAVOR_MAKERBOT)
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M127 T0\n";
else
*output_stream << "M107\n";
@@ -437,9 +567,9 @@ void GCodeExport::writeFanCommand(int speed)
currentFanSpeed = speed;
}
void GCodeExport::writeTemperatureCommand(int extruder, int temperature, bool wait)
void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool wait)
{
if (!wait && currentTemperature[extruder] == temperature)
if (!wait && extruder_attr[extruder].currentTemperature == temperature)
return;
if (wait)
@@ -449,10 +579,10 @@ void GCodeExport::writeTemperatureCommand(int extruder, int temperature, bool wa
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << "\n";
currentTemperature[extruder] = temperature;
extruder_attr[extruder].currentTemperature = temperature;
}
void GCodeExport::writeBedTemperatureCommand(int temperature, bool wait)
void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
{
if (wait)
*output_stream << "M190 S";
@@ -461,12 +591,9 @@ void GCodeExport::writeBedTemperatureCommand(int temperature, bool wait)
*output_stream << temperature << "\n";
}
void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCode)
void GCodeExport::finalize(double moveSpeed, const char* endCode)
{
std::cerr << "maxObjectHeight : " << maxObjectHeight << std::endl;
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)));
@@ -477,3 +604,4 @@ void GCodeExport::finalize(int maxObjectHeight, int moveSpeed, const char* endCo
}
}//namespace cura
+178 -73
Ver Arquivo
@@ -4,83 +4,122 @@
#include <stdio.h>
#include <deque> // for extrusionAmountAtPreviousRetractions
#include <sstream> // for stream.str()
#include "settings.h"
#include "utils/intpoint.h"
#include "utils/NoCopy.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "commandSocket.h"
namespace cura {
struct CoastingConfig
{
bool coasting_enable;
double coasting_volume_move;
double coasting_speed_move;
double coasting_min_volume_move;
double coasting_volume_retract;
double coasting_speed_retract;
double coasting_min_volume_retract;
};
class RetractionConfig
{
public:
double amount; //!< The amount
int speed;
int primeSpeed;
double primeAmount;
int zHop;
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; //!< in mm
int retraction_count_max;
};
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
class GCodePathConfig
{
private:
int speed;
int line_width;
int flow;
int layer_thickness;
double extrusion_mm3_per_mm;
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* retraction_config;
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() : 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_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
void setSpeed(int 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(int flow)
/*!
* 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)
{
this->flow = flow;
calculateExtrusion();
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
void smoothSpeed(int min_speed, int layer_nr, int max_speed_layer)
{
speed = (speed*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;
}
int getSpeed()
/*!
* 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()
{
@@ -90,73 +129,106 @@ 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
{
Point nozzle_offset;
char extruderCharacter;
std::string start_code;
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
double 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)
, 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
double extruderSwitchRetraction;
int extruderSwitchRetractionSpeed;
int extruderSwitchPrimeSpeed;
double retraction_extrusion_window;
int retraction_count_max;
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;
Point extruderOffset[MAX_EXTRUDERS];
char extruderCharacter[MAX_EXTRUDERS];
int currentTemperature[MAX_EXTRUDERS];
int currentSpeed;
int zPos;
bool isRetracted;
bool isZHopped;
int retractionPrimeSpeed;
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)
int current_extruder;
int currentFanSpeed;
EGCodeFlavor flavor;
std::string preSwitchExtruderCode[MAX_EXTRUDERS];
std::string postSwitchExtruderCode[MAX_EXTRUDERS];
double totalFilament[MAX_EXTRUDERS]; // in mm^3
double filament_diameter[MAX_EXTRUDERS]; // in mm^3
double totalPrintTime;
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);
void setExtruderOffset(int id, Point p);
Point getExtruderOffset(int id);
void setSwitchExtruderCode(int id, std::string preSwitchExtruderCode, std::string postSwitchExtruderCode);
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
void setFlavor(EGCodeFlavor flavor);
EGCodeFlavor getFlavor();
void setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int minimalExtrusionBeforeRetraction, int retraction_count_max);
void setZ(int z);
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);
@@ -167,31 +239,64 @@ public:
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);
void writeMove(Point p, int speed, double extrusion_per_mm);
void writeMove(Point p, double speed, double extrusion_per_mm);
void writeMove(Point3 p, int speed, double extrusion_per_mm);
void writeMove(Point3 p, double speed, double extrusion_per_mm);
private:
void writeMove(int x, int y, int z, int speed, double extrusion_per_mm);
void writeMove(int x, int y, int z, double speed, double extrusion_per_mm);
/*!
* The writeMove when flavor == BFB
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_per_mm);
public:
void writeRetraction(RetractionConfig* config, bool force=false);
void writeRetraction_extruderSwitch();
void switchExtruder(int newExtruder);
void writeCode(const char* str);
void writeFanCommand(int speed);
void writeFanCommand(double speed);
void writeTemperatureCommand(int extruder, int temperature, bool wait = false);
void writeBedTemperatureCommand(int temperature, bool wait = false);
void writeTemperatureCommand(int extruder, double temperature, bool wait = false);
void writeBedTemperatureCommand(double temperature, bool wait = false);
void preSetup(MeshGroup* settings)
{
for(int n=0; n<settings->getSettingAsCount("machine_extruder_count"); n++)
{
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[n].end_code = train->getSettingString("machine_extruder_end_code");
extruder_attr[n].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");
}
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
}
void finalize(double moveSpeed, const char* endCode);
void finalize(int maxObjectHeight, int moveSpeed, const char* endCode);
};
}
+732 -180
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+446 -70
Ver Arquivo
@@ -7,118 +7,494 @@
#include "comb.h"
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
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;
bool retract;
int extruder;
std::vector<Point> points;
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.
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.
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();
}
};
//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 ExtruderPlan
{
private:
GCodeExport& gcode;
Point lastPosition;
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 : public NoCopy
{
friend class LayerPlanBuffer;
private:
SliceDataStorage& storage;
CommandSocket* commandSocket;
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;
GCodePathConfig travelConfig;
int extrudeSpeedFactor;
int travelSpeedFactor;
int currentExtruder;
int retractionMinimalDistance;
bool forceRetraction;
bool alwaysRetract;
RetractionConfig* last_retraction_config;
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings;
double extrudeSpeedFactor;
double travelSpeedFactor;
double fan_speed;
double extraTime;
double totalPrintTime;
private:
GCodePath* getLatestPathWithConfig(GCodePathConfig* config);
/*!
* Either create a new path with the given config or return the last path if it already had that config.
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
*
* \param config The config used for the path returned
* \param flow (optional) A ratio for the extrusion speed
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
*
* This function is introduced because in some cases
* GCodePlanner::getLatestPathWithConfig is called consecutively with the same config pointer,
* though the content of the config has changed.
*
* Example cases:
* - when changing extruder, the same travel config is used, but its extruder field is changed.
*/
void forceNewPathStart();
public:
GCodePlanner(GCodeExport& gcode, RetractionConfig* retraction_config, int travelSpeed, int retractionMinimalDistance);
/*!
*
* \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(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();
bool setExtruder(int extruder)
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()
{
if (extruder == currentExtruder)
return false;
currentExtruder = extruder;
return true;
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 setCombBoundary(Polygons* polygons)
void setExtrudeSpeedFactor(double speedFactor)
{
if (comb)
delete comb;
if (polygons)
comb = new Comb(*polygons);
else
comb = nullptr;
}
void setAlwaysRetract(bool alwaysRetract)
{
this->alwaysRetract = alwaysRetract;
}
void forceRetract()
{
forceRetraction = true;
}
void setExtrudeSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->extrudeSpeedFactor = speedFactor;
}
int getExtrudeSpeedFactor()
double getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void setTravelSpeedFactor(int speedFactor)
void setTravelSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
if (speedFactor < 1) speedFactor = 1.0;
this->travelSpeedFactor = speedFactor;
}
int getTravelSpeedFactor()
double getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void addTravel(Point p);
void addExtrusionMove(Point p, GCodePathConfig* config);
void moveInsideCombBoundary(int distance);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config);
void forceMinimalLayerTime(double minTime, int minimalSpeed, double travelTime, double extrusionTime);
void getTimes(double& travelTime, double& extrudeTime);
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.
*
* \param p The point to travel to
*/
void addTravel(Point p);
/*!
* Add a travel path to a certain point and retract if needed.
*
* No combing is performed.
*
* \param p The point to travel to
* \param path (optional) The travel path to which to add the point \p p
*/
void addTravel_simple(Point p, GCodePath* path = nullptr);
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST);
/*!
* Add lines to the gcode with optimized order.
* \param polygons The lines
* \param config The config of the lines
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
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);
/*!
* 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.
* \param coasting_speed_move The speed at which to move during move-coasting.
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
* \param coasting_speed_retract The speed at which to move during retract-coasting.
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
* \return Whether any GCode has been written for the path.
*/
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.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
*
* \param 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.
* \param coasting_volume The volume otherwise leaked.
* \param coasting_speed The speed at which to move during coasting.
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(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(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(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config);
/*!
* 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
+62 -79
Ver Arquivo
@@ -2,8 +2,57 @@
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/AABB.h"
#include "utils/logoutput.h"
namespace cura {
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
const Polygons* outline = &in_outline;
Polygons outline_offsetted;
switch(pattern)
{
case EFillMethod::GRID:
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
break;
case EFillMethod::LINES:
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
break;
case EFillMethod::CONCENTRIC:
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
if (abs(extrusion_width - line_distance) < 10)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
}
else
{
generateConcentricInfill(*outline, result_polygons, line_distance);
}
break;
case EFillMethod::ZIG_ZAG:
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
break;
default:
logError("Fill pattern has unknown value.\n");
break;
}
}
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
@@ -16,7 +65,7 @@ void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons*
result.add(r);
}
Polygons next_outline;
offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
@@ -37,7 +86,7 @@ void generateConcentricInfill(Polygons outline, Polygons& result, int inset_valu
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, int infillOverlap,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
@@ -47,7 +96,7 @@ void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons&
}
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, int infillOverlap,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
@@ -89,28 +138,11 @@ 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, int infillOverlap, double rotation)
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
if (lineSpacing == 0) return;
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 + outlineOffset);
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
@@ -164,62 +196,13 @@ void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons&
}
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
{
if (use_endPieces) return generateZigZagInfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagInfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
}
/*!
* adapted from generateLineInfill(.)
*
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
* sigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (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 generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags)
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;
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
@@ -253,7 +236,7 @@ void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result
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;
@@ -353,7 +336,7 @@ void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result
}
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
@@ -385,7 +368,7 @@ void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& resu
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool firstBoundarySegmentEndsInEven = true;
bool isEvenScanSegment = false;
+116 -9
Ver Arquivo
@@ -3,17 +3,124 @@
#define INFILL_H
#include "utils/polygon.h"
#include "settings.h"
namespace cura {
namespace cura
{
class Infill
{
EFillMethod pattern;
const Polygons& in_outline;
int outlineOffset;
bool avoidOverlappingPerimeters;
int extrusion_width;
int line_distance;
double infill_overlap;
double fill_angle;
bool connect_zigzags;
bool use_endPieces;
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, int infillOverlap, double rotation);
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags);
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
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
+55 -12
Ver Arquivo
@@ -1,13 +1,10 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "polygonOptimizer.h"
#include "utils/polygonUtils.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
int combBoundaryInset = line_width_x/2; // hard coded value
part->combBoundery = part->outline.offset(-combBoundaryInset);
if (insetCount == 0)
{
part->insets.push_back(part->outline);
@@ -19,16 +16,34 @@ void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, in
part->insets.push_back(Polygons());
if (i == 0)
{
offsetSafe(part->outline, - line_width_x/2, line_width_x, part->insets[i], avoidOverlappingPerimeters);
if (line_width_0 < nozzle_width)
{
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
else
{
PolygonUtils::offsetSafe(part->outline, - line_width_0/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
} else if (i == 1)
{
offsetExtrusionWidth(part->insets[i-1], true, line_width_0, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
if (line_width_0 < nozzle_width)
{
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
PolygonUtils::offsetSafe(part->insets[0], nozzle_width/2 - line_width_0 - line_width_x/2, offset_from_first_boundary_for_edge_of_outer_wall, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
else
{
PolygonUtils::offsetSafe(part->insets[0], -line_width_0/2 - line_width_x/2, -line_width_0/2, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
} else
{
offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
PolygonUtils::offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
}
optimizePolygons(part->insets[i]);
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
@@ -37,12 +52,11 @@ void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, in
}
}
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters);
generateInsets(&layer->parts[partNr], nozzle_width, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
@@ -57,4 +71,33 @@ void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int i
}
}
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
+23 -6
Ver Arquivo
@@ -11,23 +11,40 @@ namespace cura
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
* \param line_width_0 Line width of the outer wall
* \param line_width_x Line width of other walls
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by \p line_width_0 instead of the first,
* which leads to better results for a smaller \p line_width_0 than \p line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
* \param line_width_0 Line width of the outer wall
* \param line_width_x Line width of other walls
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
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
+32 -24
Ver Arquivo
@@ -1,8 +1,10 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "layerPart.h"
#include "settings.h"
#include "Progress.h"
#include "utils/SVG.h" // debug output
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
@@ -11,7 +13,7 @@ each of these groups is called a "part", which sometimes are also known as "isla
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
And all every bit inside a single part can be printed without the nozzle leaving the boundary of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
@@ -20,7 +22,7 @@ namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes)
{
storageLayer.openLines = layer->openPolygons;
storageLayer.openPolyLines = layer->openPolylines;
if (union_all_remove_holes)
{
@@ -31,16 +33,15 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
}
}
std::vector<Polygons> result;
std::vector<PolygonsPart> result;
result = layer->polygonList.splitIntoParts(union_layers || union_all_remove_holes);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.push_back(SliceLayerPart());
storageLayer.parts.emplace_back();
storageLayer.parts[i].outline = result[i];
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
@@ -49,41 +50,48 @@ void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_laye
storage.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
storage.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(storage.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
logProgress("layerparts", layer_nr + 1, slicer->layers.size());
}
}
void dumpLayerparts(SliceDataStorage& storage, const char* filename)
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers, int layer_nr)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point3 modelSize = storage.model_size;
Point3 modelMin = storage.model_min;
Point model_min_2d = Point(modelMin.x, modelMin.y);
Point model_max_2d = Point(modelSize.x, modelSize.y) + model_min_2d;
AABB aabb(model_min_2d, model_max_2d);
SVG svg(filename, aabb);
for(SliceMeshStorage& mesh : storage.meshes)
{
for(SliceLayer& layer : mesh.layers)
for(unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
if (!(all_layers || int(layer_idx) == layer_nr)) { continue; }
SliceLayer& layer = mesh.layers[layer_idx];
// fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
for(SliceLayerPart& part : layer.parts)
{
for(unsigned int j=0;j<part.outline.size();j++)
{
fprintf(out, "<polygon points=\"");
for(unsigned int k=0;k<part.outline[j].size();k++)
fprintf(out, "%f,%f ", float(part.outline[j][k].X - modelMin.x)/modelSize.x*500, float(part.outline[j][k].Y - modelMin.y)/modelSize.y*500);
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
}
svg.writeAreas(part.outline);
svg.writePoints(part.outline);
// for(unsigned int j=0;j<part.outline.size();j++)
// {
// fprintf(out, "<polygon points=\"");
// for(unsigned int k=0;k<part.outline[j].size();k++)
// fprintf(out, "%f,%f ", float(part.outline[j][k].X - modelMin.x)/modelSize.x*500, float(part.outline[j][k].Y - modelMin.y)/modelSize.y*500);
// if (j == 0)
// fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
// else
// fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
// }
}
fprintf(out, "</svg>\n");
// fprintf(out, "</svg>\n");
}
}
fprintf(out, "</body></html>");
fclose(out);
}
}//namespace cura
+3 -2
Ver Arquivo
@@ -4,6 +4,7 @@
#include "sliceDataStorage.h"
#include "slicer.h"
#include "commandSocket.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
@@ -12,7 +13,7 @@ each of these groups is called a "part", which sometimes are also known as "isla
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
And all every bit inside a single part can be printed without the nozzle leaving the boundary of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
@@ -23,7 +24,7 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers = true, int layer_nr = -1);
}//namespace cura
+263 -137
Ver Arquivo
@@ -14,30 +14,38 @@
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/string.h"
#include "sliceDataStorage.h"
#include "modelFile/modelFile.h"
#include "settings.h"
#include "FffProcessor.h"
#include "settingRegistry.h"
#include "multiVolumes.h"
#include "polygonOptimizer.h"
#include "slicer.h"
#include "layerPart.h"
#include "inset.h"
#include "skin.h"
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOrderOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#include "fffProcessor.h"
namespace cura
{
void print_usage()
{
cura::logError("usage: CuraEngine [-h] [-v] [-m 3x3matrix] [-c <config file>] [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n");
cura::logError("\n");
cura::logError("usage:\n");
cura::logError("CuraEngine help\n");
cura::logError("\tShow this help message\n");
cura::logError("\n");
cura::logError("CuraEngine connect <host>[:<port>] [-j <settings.json>]\n");
cura::logError(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults\n");
cura::logError("\n");
cura::logError("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
cura::logError(" -v\n\tIncrease the verbose level (show log messages).\n");
cura::logError(" -p\n\tLog progress information.\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults.\n");
cura::logError(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
cura::logError(" -l <model_file>\n\tLoad an STL model. \n");
cura::logError(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
cura::logError(" -e\n\tAdd a new extruder train.\n");
cura::logError(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
cura::logError(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
cura::logError("\n");
cura::logError("The settings are appended to the last supplied object:\n");
cura::logError("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e [extruder train settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
cura::logError("\n");
}
//Signal handler for a "floating point exception", which can also be integer division by zero errors.
@@ -48,6 +56,220 @@ void signal_FPE(int n)
exit(1);
}
void print_call(int argc, char **argv)
{
cura::logError("Command called:\n");
for (int idx= 0; idx < argc; idx++)
cura::logError("%s ", argv[idx]);
cura::logError("\n");
}
void connect(int argc, char **argv)
{
CommandSocket* commandSocket = new CommandSocket();
std::string ip;
int port = 49674;
std::string ip_port(argv[2]);
if (ip_port.find(':') != std::string::npos)
{
ip = ip_port.substr(0, ip_port.find(':'));
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
for(int argn = 3; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'v':
cura::increaseVerboseLevel();
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
break;
}
}
}
}
commandSocket->connect(ip, port);
}
void slice(int argc, char **argv)
{
FffProcessor::getInstance()->time_keeper.restart();
FMatrix3x3 transformation; // the transformation applied to a model when loaded
MeshGroup* meshgroup = new MeshGroup(FffProcessor::getInstance());
int extruder_train_nr = 0;
SettingsBase* last_extruder_train = meshgroup->createExtruderTrain(0);
SettingsBase* last_settings_object = FffProcessor::getInstance();
for(int argn = 2; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
if (str[1] == '-')
{
if (stringcasecompare(str, "--next") == 0)
{
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();
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); // create new extruder train objects or use already existing ones
}
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
// initialize loading of new meshes
FffProcessor::getInstance()->time_keeper.restart();
delete meshgroup;
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_settings_object = meshgroup;
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
}else{
for(str++; *str; str++)
{
switch(*str)
{
case 'v':
cura::increaseVerboseLevel();
break;
case 'p':
cura::enableProgressLogging();
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
break;
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->createExtruderTrain(extruder_train_nr);
last_extruder_train = last_settings_object;
break;
case 'l':
argn++;
log("Loading %s from disk...\n", argv[argn]);
// transformation = // TODO: get a transformation from somewhere
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
}
break;
case 'o':
argn++;
if (!FffProcessor::getInstance()->setTargetFile(argv[argn]))
{
cura::logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 'g':
last_settings_object = meshgroup;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
last_settings_object->setSetting(argv[argn], valuePtr);
}
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
exit(1);
break;
}
}
}
}
else
{
cura::logError("Unknown option: %s\n", argv[argn]);
print_call(argc, argv);
print_usage();
exit(1);
}
}
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->createExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // create new extruder train objects or use already existing ones
}
#ifndef DEBUG
try {
#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();
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
#ifndef DEBUG
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
#endif
//Finalize the processor, this adds the end.gcode. And reports statistics.
FffProcessor::getInstance()->finalize();
delete meshgroup;
}
}//namespace cura
using namespace cura;
int main(int argc, char **argv)
@@ -62,9 +284,10 @@ int main(int argc, char **argv)
signal(SIGFPE, signal_FPE);
#endif
fffProcessor processor;
std::vector<std::string> files;
Progress::init();
logCopyright("\n");
logCopyright("Cura_SteamEngine version %s\n", VERSION);
logCopyright("Copyright (C) 2014 David Braam\n");
logCopyright("\n");
@@ -81,130 +304,33 @@ int main(int argc, char **argv)
logCopyright("You should have received a copy of the GNU Affero General Public License\n");
logCopyright("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
CommandSocket* commandSocket = NULL;
std::string ip;
int port = 49674;
for(int argn = 1; argn < argc; argn++)
if (argc < 2)
{
char* str = argv[argn];
if (str[0] == '-')
{
if (str[1] == '-')
{
if (stringcasecompare(str, "--connect") == 0)
{
commandSocket = new CommandSocket(&processor);
std::string ip_port(argv[argn + 1]);
if (ip_port.find(':') != std::string::npos)
{
ip = ip_port.substr(0, ip_port.find(':'));
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
argn += 1;
}
else if (stringcasecompare(str, "--") == 0)
{
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.
if (files.size() > 0)
processor.processFiles(files);
files.clear();
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
}else{
for(str++; *str; str++)
{
switch(*str)
{
case 'h':
print_usage();
exit(1);
case 'v':
cura::increaseVerboseLevel();
break;
case 'j':
argn++;
if (!SettingRegistry::getInstance()->loadJSON(argv[argn]))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
break;
case 'p':
cura::enableProgressLogging();
break;
case 'o':
argn++;
if (!processor.setTargetFile(argv[argn]))
{
cura::logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
processor.setSetting(argv[argn], valuePtr);
}
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
break;
}
}
}
}else{
files.push_back(argv[argn]);
}
print_usage();
exit(1);
}
if (!SettingRegistry::getInstance()->settingsLoaded())
if (stringcasecompare(argv[1], "connect") == 0)
{
//If no json file has been loaded, try to load the default.
if (!SettingRegistry::getInstance()->loadJSON("fdmprinter.json"))
{
logError("ERROR: Failed to load json file: fdmprinter.json\n");
}
connect(argc, argv);
}
else if (stringcasecompare(argv[1], "slice") == 0)
{
slice(argc, argv);
}
if(commandSocket)
else if (stringcasecompare(argv[1], "help") == 0)
{
commandSocket->connect(ip, port);
print_usage();
exit(0);
}
else
{
#ifndef DEBUG
try {
#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.
if (files.size() > 0)
processor.processFiles(files);
#ifndef DEBUG
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
#endif
//Finalize the processor, this adds the end.gcode. And reports statistics.
processor.finalize();
cura::logError("Unknown command: %s\n", argv[1]);
print_call(argc, argv);
print_usage();
exit(1);
}
return 0;
}
}
+10 -21
Ver Arquivo
@@ -1,6 +1,8 @@
#include "mesh.h"
#include "utils/logoutput.h"
namespace cura
{
const int vertex_meld_distance = MM2INT(0.03);
static inline uint32_t pointHash(Point3& p)
@@ -8,7 +10,7 @@ static inline uint32_t pointHash(Point3& p)
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
}
Mesh::Mesh(SettingsBase* parent)
Mesh::Mesh(SettingsBaseVirtual* parent)
: SettingsBase(parent)
{
}
@@ -55,29 +57,11 @@ void Mesh::finish()
Point3 Mesh::min()
{
if (vertices.size() < 1)
return Point3(0, 0, 0);
Point3 ret = vertices[0].p;
for(unsigned int i=0; i<vertices.size(); i++)
{
ret.x = std::min(ret.x, vertices[i].p.x);
ret.y = std::min(ret.y, vertices[i].p.y);
ret.z = std::min(ret.z, vertices[i].p.z);
}
return ret;
return aabb.min;
}
Point3 Mesh::max()
{
if (vertices.size() < 1)
return Point3(0, 0, 0);
Point3 ret = vertices[0].p;
for(unsigned int i=0; i<vertices.size(); i++)
{
ret.x = std::max(ret.x, vertices[i].p.x);
ret.y = std::max(ret.y, vertices[i].p.y);
ret.z = std::max(ret.z, vertices[i].p.z);
}
return ret;
return aabb.max;
}
int Mesh::findIndexOfVertex(Point3& v)
@@ -93,6 +77,9 @@ int Mesh::findIndexOfVertex(Point3& v)
}
vertex_hash_map[hash].push_back(vertices.size());
vertices.emplace_back(v);
aabb.include(v);
return vertices.size() - 1;
}
@@ -191,3 +178,5 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
if (bestIdx < 0) cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
return bestIdx;
}
}//namespace cura
+20 -4
Ver Arquivo
@@ -2,7 +2,10 @@
#define MESH_H
#include "settings.h"
#include "utils/AABB.h"
namespace cura
{
/*!
Vertex type to be used in a Mesh.
@@ -14,7 +17,7 @@ public:
Point3 p; //!< location of the vertex
std::vector<uint32_t> connected_faces; //!< list of the indices of connected faces
MeshVertex(Point3 p) : p(p) {} //!< doesn't set connected_faces
MeshVertex(Point3 p) : p(p) {connected_faces.reserve(8);} //!< doesn't set connected_faces
};
/*! A MeshFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers
@@ -54,12 +57,13 @@ See MeshFace for the specifics of how/when faces are connected.
class Mesh : public SettingsBase // inherits settings
{
//! The vertex_hash_map stores a index reference of each vertex for the hash of that location. Allows for quick retrieval of points with the same location.
std::map<uint32_t, std::vector<uint32_t> > vertex_hash_map;
std::unordered_map<uint32_t, std::vector<uint32_t> > vertex_hash_map;
AABB3D aabb;
public:
std::vector<MeshVertex> vertices;//!< list of all vertices in the mesh
std::vector<MeshFace> faces; //!< list of all faces in the mesh
Mesh(SettingsBase* parent); //!< initializes the settings
Mesh(SettingsBaseVirtual* parent); //!< initializes the settings
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
void clear(); //!< clears all data
@@ -67,6 +71,18 @@ public:
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
/*!
* Offset the whole mesh (all vertices and the bounding box).
* \param offset The offset byu which to offset the whole mesh.
*/
void offset(Point3 offset)
{
if (offset == Point3(0,0,0)) { return; }
for(MeshVertex& v : vertices)
v.p += offset;
aabb.offset(offset);
}
private:
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
@@ -77,6 +93,6 @@ private:
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
};
}//namespace cura
#endif//MESH_H
-98
Ver Arquivo
@@ -1,98 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MODELFILE_H
#define MODELFILE_H
/**
modelFile contains the model loaders for the slicer. The model loader turns any format that it can read into a list of triangles with 3 X/Y/Z points.
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
**/
#include "../mesh.h"
//A PrintObject is a 3D model with 1 or more 3D meshes.
class PrintObject : public SettingsBase
{
public:
std::vector<Mesh> meshes;
PrintObject(SettingsBase* settings_base)
: SettingsBase(settings_base)
{
}
Point3 min() //! minimal corner of bounding box
{
if (meshes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 max() //! maximal corner of bounding box
{
if (meshes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void clear()
{
for(Mesh& m : meshes)
m.clear();
}
void offset(Point3 offset)
{
for(Mesh& m : meshes)
for(MeshVertex& v : m.vertices)
v.p += offset;
}
void finalize()
{
// If a mesh position was given, put the mesh at this position in 3D space.
if (hasSetting("mesh_position_x") || hasSetting("mesh_position_y") || hasSetting("mesh_position_z"))
{
Point3 object_min = min();
Point3 object_max = max();
Point3 object_size = object_max - object_min;
Point3 object_offset = Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
if (hasSetting("mesh_position_x"))
object_offset.x += getSettingInMicrons("mesh_position_x");
if (hasSetting("mesh_position_y"))
object_offset.y += getSettingInMicrons("mesh_position_y");
if (hasSetting("mesh_position_z"))
object_offset.z += getSettingInMicrons("mesh_position_z");
offset(object_offset);
}
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
if (hasSetting("machine_center_is_zero") && !getSettingBoolean("machine_center_is_zero"))
{
Point3 object_offset = Point3(0, 0, 0);
if (hasSetting("machine_width"))
object_offset.x = getSettingInMicrons("machine_width") / 2;
if (hasSetting("machine_depth"))
object_offset.y = getSettingInMicrons("machine_depth") / 2;
offset(object_offset);
}
}
};
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix);
#endif//MODELFILE_H
+33 -43
Ver Arquivo
@@ -1,58 +1,48 @@
#include "multiVolumes.h"
namespace cura {
void carveMultipleVolumes(std::vector<SliceMeshStorage> &volumes)
namespace cura
{
void carveMultipleVolumes(std::vector<Slicer*> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int idx=0; idx < volumes.size(); idx++)
{
for(unsigned int idx2=0; idx2<idx; idx2++)
{
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
for(unsigned int layerNr=0; layerNr < volumes[idx]->layers.size(); layerNr++)
{
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
}
}
}
}
}
}
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &volumes, int overlap)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
}
}
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
SlicerLayer& layer1 = volumes[idx]->layers[layerNr];
SlicerLayer& layer2 = volumes[idx2]->layers[layerNr];
layer1.polygonList = layer1.polygonList.difference(layer2.polygonList);
}
}
}
}
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes, int overlap)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0]->layers.size(); layerNr++)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
fullLayer = fullLayer.unionPolygons(layer1.polygonList.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
}
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
layer1.polygonList = fullLayer.intersection(layer1.polygonList.offset(overlap / 2));
}
}
}
}//namespace cura
+7 -4
Ver Arquivo
@@ -2,15 +2,18 @@
#define MULTIVOLUMES_H
#include "sliceDataStorage.h"
#include "slicer.h"
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
namespace cura {
void carveMultipleVolumes(std::vector<SliceMeshStorage> &meshes);
void carveMultipleVolumes(std::vector<Slicer*> &meshes);
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &meshes, int overlap);
/*!
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
* This generates some overlap in dual extrusion, for better bonding in touching parts.
*/
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes, int overlap);
}//namespace cura
+43 -11
Ver Arquivo
@@ -1,6 +1,4 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <map>
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
@@ -78,18 +76,30 @@ void PathOrderOptimizer::optimize()
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int i_polygon = polyOrder[n];
int best = getClosestPointInPolygon(prev_point, i_polygon);
polyStart[i_polygon] = best;
prev_point = polygons[i_polygon][best];
int poly_idx = polyOrder[n];
int point_idx = getPolyStart(prev_point, poly_idx);
polyStart[poly_idx] = point_idx;
prev_point = polygons[poly_idx][point_idx];
}
}
inline int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int i_polygon)
int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[i_polygon];
int best = -1;
switch (type)
{
case EZSeamType::BACK: return getFarthestPointInPolygon(poly_idx);
case EZSeamType::RANDOM: return getRandomPointInPolygon(poly_idx);
case EZSeamType::SHORTEST: return getClosestPointInPolygon(prev_point, poly_idx);
default: return getClosestPointInPolygon(prev_point, poly_idx);
}
}
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
@@ -102,11 +112,33 @@ inline int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int i_
dot_score = -dot_score;
if (dist + dot_score < bestDist)
{
best = i_point;
best_point_idx = i_point;
bestDist = dist;
}
}
return best;
return best_point_idx;
}
int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
return rand() % polygons[poly_idx].size();
}
int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float best_y = std::numeric_limits<float>::min();
for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
{
if (poly[point_idx].Y > best_y)
{
best_point_idx = point_idx;
best_y = poly[point_idx].Y;
}
}
return best_point_idx;
}
+16 -9
Ver Arquivo
@@ -4,25 +4,29 @@
#include <stdint.h>
#include "utils/polygon.h"
#include "settings.h"
namespace cura {
//! Parts order optimization class.
/*!
* Utility class for optimizing the path order by minimizing the distance traveled between printing different parts in the layer.
* The order of polygons is optimized and the startingpoint within each polygon is chosen.
*/
* Parts order optimization class.
*
* Utility class for optimizing the path order by minimizing the distance traveled between printing different parts in the layer.
* The order of polygons is optimized and the startingpoint within each polygon is chosen.
*/
class PathOrderOptimizer
{
public:
EZSeamType type;
Point startPoint; //!< The location of the nozzle before starting to print the current layer
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
PathOrderOptimizer(Point startPoint)
PathOrderOptimizer(Point startPoint, EZSeamType type = EZSeamType::SHORTEST)
: type(type)
, startPoint(startPoint)
{
this->startPoint = startPoint;
}
void addPolygon(PolygonRef polygon)
@@ -38,8 +42,11 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
int getClosestPointInPolygon(Point prev, int i_polygon); //!< returns the index of the closest point
private:
int getPolyStart(Point prev_point, int poly_idx);
int getClosestPointInPolygon(Point prev, int i_polygon); //!< returns the index of the closest point
int getFarthestPointInPolygon(int poly_idx); //!< return the index to the point farthest from the front (highest y)
int getRandomPointInPolygon(int poly_idx);
};
-54
Ver Arquivo
@@ -1,54 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "polygonOptimizer.h"
namespace cura {
void optimizePolygon(PolygonRef poly)
{
Point p0 = poly[poly.size()-1];
for(unsigned int i=0;i<poly.size();i++)
{
Point p1 = poly[i];
if (shorterThen(p0 - p1, MICRON2INT(10)))
{
poly.remove(i);
i --;
}else if (shorterThen(p0 - p1, MICRON2INT(500)))
{
Point p2;
if (i < poly.size() - 1)
p2 = poly[i+1];
else
p2 = poly[0];
Point diff0 = normal(p1 - p0, 10000000);
Point diff2 = normal(p1 - p2, 10000000);
int64_t d = dot(diff0, diff2);
if (d < -99999999999999LL)
{
poly.remove(i);
i --;
}else{
p0 = p1;
}
}else{
p0 = p1;
}
}
}
void optimizePolygons(Polygons& polys)
{
for(unsigned int n=0;n<polys.size();n++)
{
optimizePolygon(polys[n]);
if (polys[n].size() < 3)
{
polys.remove(n);
n--;
}
}
}
}//namespace cura
-15
Ver Arquivo
@@ -1,15 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef POLYGON_OPTIMIZER_H
#define POLYGON_OPTIMIZER_H
#include "utils/polygon.h"
namespace cura {
void optimizePolygon(PolygonRef poly);
void optimizePolygons(Polygons& polys);
}//namespace cura
#endif//POLYGON_OPTIMIZER_H
+10 -11
Ver Arquivo
@@ -6,19 +6,18 @@ namespace cura {
void generateRaft(SliceDataStorage& storage, int distance)
{
for(SliceMeshStorage& mesh : storage.meshes)
if (storage.draft_protection_shield.size() > 0)
{
if (mesh.layers.size() < 1) continue;
SliceLayer* layer = &mesh.layers[0];
for(SliceLayerPart& part : layer->parts)
storage.raftOutline = storage.raftOutline.unionPolygons(part.outline.offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
}
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
{
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
}
Polygons support;
if (storage.support.generated)
support = storage.support.supportAreasPerLayer[0];
storage.raftOutline = storage.raftOutline.unionPolygons(support.offset(distance));
storage.raftOutline = storage.raftOutline.unionPolygons(storage.wipeTower.offset(distance));
}
}//namespace cura
+285 -155
Ver Arquivo
@@ -1,27 +1,91 @@
#include "settingRegistry.h"
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <algorithm> // find_if
#include "utils/logoutput.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "utils/logoutput.h"
namespace cura
{
SettingRegistry SettingRegistry::instance; // define settingRegistry
std::string SettingRegistry::toString(rapidjson::Type type)
{
switch (type)
{
case rapidjson::Type::kNullType: return "null";
case rapidjson::Type::kFalseType: return "false";
case rapidjson::Type::kTrueType: return "true";
case rapidjson::Type::kObjectType: return "object";
case rapidjson::Type::kArrayType: return "array";
case rapidjson::Type::kStringType: return "string";
case rapidjson::Type::kNumberType: return "number";
default: return "Unknown";
}
}
SettingContainer::SettingContainer(std::string key, std::string label)
: key(key)
, label(label)
{
}
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
{
children.emplace_back(key, label, nullptr);
return &children.back();
}
SettingConfig::SettingConfig(std::string key, std::string label, SettingContainer* parent)
: SettingContainer(key, label)
, parent(parent)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
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();
}
const SettingConfig* SettingRegistry::getSettingConfig(std::string key)
SettingConfig* SettingRegistry::getSettingConfig(std::string key)
{
if (settings.find(key) == settings.end())
auto it = settings.find(key);
if (it == settings.end())
return nullptr;
return settings[key];
return it->second;
}
SettingContainer* SettingRegistry::getCategory(std::string key)
{
for (SettingContainer& cat : categories)
if (cat.getKey().compare(key) == 0)
return &cat;
return nullptr;
}
SettingRegistry::SettingRegistry()
{
}
@@ -31,184 +95,250 @@ bool SettingRegistry::settingsLoaded()
return settings.size() > 0;
}
bool SettingRegistry::loadJSON(std::string filename)
int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
cura::logError("Couldn't open JSON file.\n");
return 1;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return 2;
}
return 0;
}
int SettingRegistry::loadJSONsettings(std::string filename)
{
rapidjson::Document json_document;
int err = loadJSON(filename, json_document);
if (err) { return err; }
if (json_document.HasMember("inherits"))
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
return false;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return false;
}
std::string filename_copy = std::string(filename.c_str()); // copy the string because dirname(.) changes the input string!!!
char* filename_cstr = (char*)filename_copy.c_str();
int err = loadJSONsettings(std::string(dirname(filename_cstr)) + std::string("/") + json_document["inherits"].GetString());
if (err) { return err; }
return loadJSONsettingsFromDoc(json_document, false);
}
else
{
return loadJSONsettingsFromDoc(json_document, true);
}
}
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates)
{
if (!json_document.IsObject())
{
return false;
}
if (!json_document.HasMember("categories"))
{
return false;
cura::logError("JSON file is not an object.\n");
return 3;
}
categories.emplace_back("machine_settings", "Machine Settings");
SettingCategory* category_machine_settings = &categories.back();
_addSettingsToCategory(category_machine_settings, json_document["machine_settings"], NULL);
categories.emplace_back("mesh_settings", "TEMPORARY");
SettingCategory* category_mesh_settings = &categories.back();
if (json_document.HasMember("machine_extruder_trains"))
{
SettingConfig* config = category_mesh_settings->addChild("mesh_position_x", "mesh_position_x");
config->setDefault("0");
if (settingExists(config->getKey()))
categories.emplace_back("machine_extruder_trains", "Extruder Trains Settings Objects");
SettingContainer* category_trains = &categories.back();
const rapidjson::Value& trains = json_document["machine_extruder_trains"];
if (trains.IsArray())
{
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
if (trains.Size() > 0 && trains[0].IsObject())
{
unsigned int idx = 0;
for (auto it = trains.Begin(); it != trains.End(); ++it)
{
SettingConfig* child = category_trains->addChild(std::to_string(idx), std::to_string(idx));
for (rapidjson::Value::ConstMemberIterator setting_iterator = it->MemberBegin(); setting_iterator != it->MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(child, setting_iterator, warn_duplicates, false);
}
idx++;
}
}
}
else
{
logError("Error: JSON machine_extruder_trains is not an array!\n");
}
settings[config->getKey()] = config;
}
if (json_document.HasMember("machine_settings"))
{
SettingConfig* config = category_mesh_settings->addChild("mesh_position_y", "mesh_position_y");
config->setDefault("0");
if (settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
}
settings[config->getKey()] = config;
}
{
SettingConfig* config = category_mesh_settings->addChild("mesh_position_z", "mesh_position_z");
config->setDefault("0");
if (settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
}
settings[config->getKey()] = config;
}
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
{
if (!category_iterator->value.IsObject())
{
continue;
}
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
{
continue;
}
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
{
continue;
}
categories.emplace_back("machine_settings", "Machine Settings");
SettingContainer* category_machine_settings = &categories.back();
categories.emplace_back(category_iterator->name.GetString(), category_iterator->value["label"].GetString());
SettingCategory* category = &categories.back();
_addSettingsToCategory(category, category_iterator->value["settings"], NULL);
const rapidjson::Value& json_object_container = json_document["machine_settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category_machine_settings, setting_iterator, warn_duplicates);
}
}
if (json_document.HasMember("categories"))
{
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
{
if (!category_iterator->value.IsObject())
{
continue;
}
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
{
continue;
}
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
{
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);
}
}
}
}
return true;
if (json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
_loadSettingValues(conf, override_iterator, false);
}
}
return 0;
}
void SettingRegistry::_addSettingsToCategory(SettingCategory* category, const rapidjson::Value& json_object, SettingConfig* parent)
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object.MemberBegin(); setting_iterator != json_object.MemberEnd(); ++setting_iterator)
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")))
{
const rapidjson::Value& data = setting_iterator->value;
std::string label;
if (!setting_iterator->value.HasMember("label") || !data["label"].IsString())
{
label = "N/A";
}
else
{
label = data["label"].GetString();
}
/// Create the new setting config object.
SettingConfig* config;
if (parent)
config = parent->addChild(setting_iterator->name.GetString(), label);
else
config = category->addChild(setting_iterator->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"))
{
if (data["default"].IsString())
{
config->setDefault(data["default"].GetString());
}
else if (data["default"].IsTrue())
{
config->setDefault("true");
}
else if (data["default"].IsFalse())
{
config->setDefault("false");
}
else if (data["default"].IsNumber())
{
std::ostringstream ss;
ss << data["default"].GetDouble();
config->setDefault(ss.str());
}
}
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
/// Register the setting in the settings map lookup.
if (settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s\n", config->getKey().c_str());
}
settings[config->getKey()] = config;
/// When this setting has children, add those children to this setting.
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())
{
_addSettingsToCategory(category, data["children"], config);
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
}
}
return;
}
std::string label;
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
{
label = "N/A";
}
else
{
label = data["label"].GetString();
}
/// Create the new setting config object.
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
_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());
}
/// 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);
}
}
}
SettingCategory::SettingCategory(std::string key, std::string label)
: label(label), key(key)
{
}
SettingConfig* SettingCategory::addChild(std::string key, std::string label)
{
children.emplace_back(key, label, nullptr);
return &children.back();
}
SettingConfig::SettingConfig(std::string key, std::string label, SettingConfig* parent)
: label(label), key(key), parent(parent)
{
}
SettingConfig* SettingConfig::addChild(std::string key, std::string label)
{
children.emplace_back(key, label, this);
return &children.back();
}
}//namespace cura
+132 -20
Ver Arquivo
@@ -3,11 +3,16 @@
#include <vector>
#include <list>
#include <map>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
// Forward declaration
class SettingConfig;
@@ -15,16 +20,41 @@ class SettingConfig;
* Setting category.
* Filled from the fdmprinter.json file. Contains one or more children settings.
*/
class SettingCategory
class SettingContainer
{
friend class SettingConfig;
private:
std::string label;
std::string key;
std::string label;
std::list<SettingConfig> children;
public:
SettingCategory(std::string key, std::string label);
std::string getKey() const { return key; }
std::string getLabel() const { return label; }
SettingContainer(std::string key, std::string label);
SettingConfig* addChild(std::string key, std::string label);
/*!
* Get the \p idx th child.
*
* This is used to get a specific extruder train in Settingsbase::setExtruderTrainDefaults
*
* \param idx The index in the list of children
* \return The \p idx th child
*/
const SettingConfig* getChild(unsigned int idx) const
{
if (idx < children.size())
{
auto it = children.begin();
while (idx > 0) { ++it; idx--; }
return &*it;
}
else
return nullptr;
}
void debugOutputAllSettings();
};
/*!
@@ -32,20 +62,24 @@ public:
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
* setting registry with it's key.
*/
class SettingConfig
class SettingConfig : public SettingContainer
{
private:
std::string label;
std::string key;
std::string type;
std::string default_value;
std::string unit;
SettingConfig* parent;
std::list<SettingConfig> children;
SettingContainer* parent;
public:
SettingConfig(std::string key, std::string label, SettingConfig* parent);
SettingConfig* addChild(std::string key, std::string label);
SettingConfig(std::string key, std::string label, SettingContainer* parent);
/*!
* Get the SettingConfig::children.
*
* This is used to get the extruder trains; see Settingsbase::setExtruderTrainDefaults
*
* \return SettingConfig::children
*/
const std::list<SettingConfig>& getChildren() const { return children; }
std::string getKey() const
{
@@ -81,6 +115,15 @@ public:
{
return unit;
}
void debugOutputAllSettings()
{
std::cerr << key << std::endl;
for (SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
};
/*!
@@ -89,25 +132,94 @@ public:
* This registry contains all known setting keys.
* The registry also contains the settings categories to build up the setting hiarcy from the json file.
*/
class SettingRegistry
class SettingRegistry : NoCopy
{
private:
static SettingRegistry instance;
std::map<std::string, SettingConfig*> settings;
std::list<SettingCategory> categories;
SettingRegistry();
std::unordered_map<std::string, SettingConfig*> settings;
std::list<SettingContainer> categories;
public:
/*!
* Get the SettingRegistry.
*
* This is a singleton class.
*
* \return The SettingRegistry
*/
static SettingRegistry* getInstance() { return &instance; }
bool settingExists(std::string key) const;
const SettingConfig* getSettingConfig(std::string key);
SettingConfig* getSettingConfig(std::string key);
/*!
* Return the first category with the given key as name, or a null pointer.
*
* \param key the key as it is in the JSON file
* \return The first category in the list having the \p key
*/
SettingContainer* getCategory(std::string key);
bool settingsLoaded();
bool loadJSON(std::string filename);
private:
SettingRegistry();
/*!
* Load settings from a json file and all the parents it inherits from.
*
* Uses recursion to load the parent json file.
*
* \param filename The filename of the json file to parse
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename);
void debugOutputAllSettings()
{
for (SettingContainer& cat : categories)
{
cat.debugOutputAllSettings();
}
}
void _addSettingsToCategory(SettingCategory* category, const rapidjson::Value& json_object, SettingConfig* parent);
private:
/*!
* \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.
*
* \param filename The filename of the json file to parse
* \param json_document (output) the document to be loaded
* \return an error code or zero of succeeded
*/
int loadJSON(std::string filename, rapidjson::Document& json_document);
/*!
* Load settings from a single json file.
*
* \param filename The filename of the json file to parse
* \param warn_duplicates whether to warn for duplicate definitions
* \return an error code or zero of succeeded
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates);
/*!
* 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
#endif//SETTING_REGISTRY_H
+222 -53
Ver Arquivo
@@ -7,21 +7,58 @@
#include "settings.h"
#include "settingRegistry.h"
namespace cura
{
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
SettingsBase::SettingsBase()
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)
{
}
SettingsBase::SettingsBase(SettingsBase* parent)
SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
: parent(parent)
{
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
{
}
SettingsBase::SettingsBase(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
SettingsMessenger::SettingsMessenger(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
void SettingsBase::setSetting(std::string key, std::string value)
{
if (SettingRegistry::getInstance()->settingExists(key))
@@ -49,7 +86,6 @@ std::string SettingsBase::getSettingString(std::string key)
if (SettingRegistry::getInstance()->settingExists(key))
{
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
cura::logError("Using default for: %s = %s\n", key.c_str(), setting_values[key].c_str());
}
else
{
@@ -59,45 +95,69 @@ std::string SettingsBase::getSettingString(std::string key)
return setting_values[key];
}
bool SettingsBase::hasSetting(std::string key)
void SettingsMessenger::setSetting(std::string key, std::string value)
{
if (setting_values.find(key) != setting_values.end())
parent->setSetting(key, value);
}
std::string SettingsMessenger::getSettingString(std::string key)
{
return parent->getSettingString(key);
}
void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
{
const SettingContainer* machine_extruder_trains = SettingRegistry::getInstance()->getCategory(std::string("machine_extruder_trains"));
if (!machine_extruder_trains)
{
return true;
}
if (parent)
{
return parent->hasSetting(key);
// no machine_extruder_trains setting present; just use defaults for each train..
return;
}
return false;
const SettingConfig* train = machine_extruder_trains->getChild(extruder_nr);
if (!train)
{
// not enough machine_extruder_trains settings present; just use defaults for this train..
return;
}
for (const SettingConfig& setting : train->getChildren())
{
if (setting_values.find(setting.getKey()) == setting_values.end())
{
setSetting(setting.getKey(), setting.getDefaultValue());
}
}
}
int SettingsBase::getSettingAsIndex(std::string key)
int SettingsBaseVirtual::getSettingAsIndex(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBase::getSettingAsCount(std::string key)
int SettingsBaseVirtual::getSettingAsCount(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBase::getSettingInMicrons(std::string key)
int SettingsBaseVirtual::getSettingInMicrons(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) * 1000.0;
}
double SettingsBase::getSettingInAngleRadians(std::string key)
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 180.0 * M_PI;
}
bool SettingsBase::getSettingBoolean(std::string key)
bool SettingsBaseVirtual::getSettingBoolean(std::string key)
{
std::string value = getSettingString(key);
if (value == "on")
@@ -106,83 +166,192 @@ bool SettingsBase::getSettingBoolean(std::string key)
return true;
if (value == "true" or value == "True") //Python uses "True"
return true;
return atoi(value.c_str()) != 0;
int num = atoi(value.c_str());
return num != 0;
}
double SettingsBase::getSettingInDegreeCelsius(std::string key)
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBase::getSettingInMillimetersPerSecond(std::string key)
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key)
{
std::string value = getSettingString(key);
return std::max(1.0, atof(value.c_str()));
}
double SettingsBase::getSettingInPercentage(std::string key)
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBase::getSettingInSeconds(std::string key)
double SettingsBaseVirtual::getSettingInPercentage(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
EGCodeFlavor SettingsBase::getSettingAsGCodeFlavor(std::string key)
double SettingsBaseVirtual::getSettingInSeconds(std::string key)
{
std::string value = getSettingString(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);
if (value == "RepRap")
return GCODE_FLAVOR_REPRAP;
return EGCodeFlavor::REPRAP;
else if (value == "UltiGCode")
return GCODE_FLAVOR_ULTIGCODE;
return EGCodeFlavor::ULTIGCODE;
else if (value == "Makerbot")
return GCODE_FLAVOR_MAKERBOT;
return EGCodeFlavor::MAKERBOT;
else if (value == "BFB")
return GCODE_FLAVOR_BFB;
return EGCodeFlavor::BFB;
else if (value == "MACH3")
return GCODE_FLAVOR_MACH3;
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return GCODE_FLAVOR_REPRAP_VOLUMATRIC;
return GCODE_FLAVOR_REPRAP;
return EGCodeFlavor::REPRAP_VOLUMATRIC;
return EGCodeFlavor::REPRAP;
}
EFillMethod SettingsBase::getSettingAsFillMethod(std::string key)
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
{
std::string value = getSettingString(key);
if (value == "Lines")
return Fill_Lines;
if (value == "Grid")
return Fill_Grid;
if (value == "Triangles")
return Fill_Triangles;
if (value == "Concentric")
return Fill_Concentric;
if (value == "ZigZag")
return Fill_ZigZag;
return Fill_None;
if (value == "lines")
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
}
EPlatformAdhesion SettingsBase::getSettingAsPlatformAdhesion(std::string key)
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key)
{
std::string value = getSettingString(key);
if (value == "Brim")
return Adhesion_Brim;
if (value == "Raft")
return Adhesion_Raft;
return Adhesion_None;
if (value == "brim")
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
return EPlatformAdhesion::SKIRT;
}
ESupportType SettingsBase::getSettingAsSupportType(std::string key)
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
{
std::string value = getSettingString(key);
if (value == "Everywhere")
return Support_Everywhere;
if (value == "Touching Buildplate")
return Support_PlatformOnly;
return Support_None;
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
{
std::string value = getSettingString(key);
if (value == "random")
return EZSeamType::RANDOM;
if (value == "shortest")
return EZSeamType::SHORTEST;
if (value == "back")
return EZSeamType::BACK;
return EZSeamType::SHORTEST;
}
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "normal")
return ESurfaceMode::NORMAL;
if (value == "surface")
return ESurfaceMode::SURFACE;
if (value == "both")
return ESurfaceMode::BOTH;
return ESurfaceMode::NORMAL;
}
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
+147 -40
Ver Arquivo
@@ -3,9 +3,16 @@
#include <vector>
#include <map>
#include <unordered_map>
#include <sstream>
#include "utils/floatpoint.h"
#include "FlowTempGraph.h"
namespace cura
{
#ifndef VERSION
#define VERSION "DEV"
#endif
@@ -14,7 +21,7 @@
* Different flavors of GCode. Some machines require different types of GCode.
* The GCode flavor definition handles this as a big setting to make major or minor modifications to the GCode.
*/
enum EGCodeFlavor
enum class EGCodeFlavor
{
/**
* RepRap flavored GCode is Marlin/Sprinter/Repetier based GCode.
@@ -24,7 +31,7 @@ enum EGCodeFlavor
* Retraction is done on E values with G1. Start/end code is added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_REPRAP = 0,
REPRAP = 0,
/**
* UltiGCode flavored is Marlin based GCode.
* UltiGCode uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode.
@@ -34,7 +41,7 @@ enum EGCodeFlavor
* Start/end code is not added.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_ULTIGCODE = 1,
ULTIGCODE = 1,
/**
* Makerbot flavored GCode.
* Looks a lot like RepRap GCode with a few changes. Requires MakerWare to convert to X3G files.
@@ -44,7 +51,7 @@ enum EGCodeFlavor
* Fan OFF is M127 T0
* Homing is done with G162 X Y F2000
**/
GCODE_FLAVOR_MAKERBOT = 2,
MAKERBOT = 2,
/**
* Bits From Bytes GCode.
@@ -52,13 +59,13 @@ enum EGCodeFlavor
* Need X,Y,Z,F on every line.
* Needs extruder ON/OFF (M101, M103), has auto-retrection (M227 S[2560*mm] P[2560*mm])
**/
GCODE_FLAVOR_BFB = 3,
BFB = 3,
/**
* MACH3 GCode
* MACH3 is CNC control software, which expects A/B/C/D for extruders, instead of E.
**/
GCODE_FLAVOR_MACH3 = 4,
MACH3 = 4,
/**
* RepRap volumatric flavored GCode is Marlin based GCode.
* Volumatric uses less settings on the slicer and puts more settings in the firmware. This makes for more hardware/material independed GCode.
@@ -67,70 +74,99 @@ enum EGCodeFlavor
* Retraction is done with G10 and G11. Retraction settings are ignored. G10 S1 is used for multi-extruder switch retraction.
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
GCODE_FLAVOR_REPRAP_VOLUMATRIC = 5,
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.
* The different methods are used for top/bottom, support and sparse infill.
*/
enum EFillMethod
enum class EFillMethod
{
Fill_Lines,
Fill_Grid,
Fill_Triangles,
Fill_Concentric,
Fill_ZigZag,
Fill_None
LINES,
GRID,
TRIANGLES,
CONCENTRIC,
ZIG_ZAG,
NONE
};
/*!
* Type of platform adheasion
*/
enum EPlatformAdhesion
enum class EPlatformAdhesion
{
Adhesion_None,
Adhesion_Brim,
Adhesion_Raft
SKIRT,
BRIM,
RAFT
};
/*!
* Type of support material to generate
*/
enum ESupportType
enum class ESupportType
{
Support_None,
Support_PlatformOnly,
Support_Everywhere
NONE,
PLATFORM_ONLY,
EVERYWHERE
};
enum class EZSeamType
{
RANDOM,
SHORTEST,
BACK
};
enum class ESurfaceMode
{
NORMAL,
SURFACE,
BOTH
};
enum class FillPerimeterGapMode
{
NOWHERE,
EVERYWHERE,
SKIN
};
#define MAX_EXTRUDERS 16
//Maximum number of sparse layers that can be combined into a single sparse extrusion.
#define MAX_SPARSE_COMBINE 8
//Maximum number of infill layers that can be combined into a single infill extrusion area.
#define MAX_INFILL_COMBINE 8
class SettingsBase;
/*!
* Base class for every object that can hold settings.
* The SettingBase object can hold multiple key-value pairs that define settings.
* The settings that are set on a SettingBase are checked against the SettingRegistry to ensure keys are valid.
* Different conversion functions are available for settings to increase code clarity and in the future make
* unit conversions possible.
* An abstract class for classes that can provide setting values.
* These are: SettingsBase, which contains setting information
* and SettingsMessenger, which can pass on setting information from a SettingsBase
*/
class SettingsBase
class SettingsBaseVirtual
{
private:
std::map<std::string, std::string> setting_values;
SettingsBase* parent;
protected:
SettingsBaseVirtual* parent;
public:
SettingsBase();
SettingsBase(SettingsBase* parent);
bool hasSetting(std::string key);
void setSetting(std::string key, std::string value);
virtual std::string getSettingString(std::string key) = 0;
virtual void setSetting(std::string key, std::string value) = 0;
virtual ~SettingsBaseVirtual() {}
SettingsBaseVirtual(); //!< SettingsBaseVirtual without a parent settings object
SettingsBaseVirtual(SettingsBaseVirtual* parent); //!< construct a SettingsBaseVirtual with a parent settings object
void setParent(SettingsBaseVirtual* parent) { this->parent = parent; }
SettingsBaseVirtual* getParent() { return parent; }
std::string getSettingString(std::string key);
int getSettingAsIndex(std::string key);
int getSettingAsCount(std::string key);
@@ -139,14 +175,85 @@ public:
bool getSettingBoolean(std::string key);
double getSettingInDegreeCelsius(std::string key);
double getSettingInMillimetersPerSecond(std::string key);
double getSettingInCubicMillimeters(std::string key);
double getSettingInPercentage(std::string key);
double getSettingInSeconds(std::string key);
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);
};
/*!
* Base class for every object that can hold settings.
* The SettingBase object can hold multiple key-value pairs that define settings.
* The settings that are set on a SettingBase are checked against the SettingRegistry to ensure keys are valid.
* Different conversion functions are available for settings to increase code clarity and in the future make
* unit conversions possible.
*/
class SettingsBase : public SettingsBaseVirtual
{
private:
std::unordered_map<std::string, std::string> setting_values;
public:
SettingsBase(); //!< SettingsBase without a parent settings object
SettingsBase(SettingsBaseVirtual* parent); //!< construct a SettingsBase with a parent settings object
/*!
* Retrieve the defaults for each extruder train from the machine_extruder_trains settings
* and set the general settings to those defaults if they haven't been set yet.
*
* Only sets those settings which haven't already been set on that level - not looking at its parent (FffProcessor, meshgroup) or children (meshes).
*
* \param extruder_nr The index of which extruder train in machine_extruder_trains to get the settings from
*/
void setExtruderTrainDefaults(unsigned int extruder_nr);
void setSetting(std::string key, std::string value);
std::string getSettingString(std::string key); //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
std::string getAllLocalSettingsString()
{
std::stringstream sstream;
for (auto pair : setting_values)
{
if (!pair.second.empty())
{
sstream << " -s " << pair.first << "=\"" << pair.second << "\"";
}
}
return sstream.str();
}
void debugOutputAllLocalSettings()
{
for (auto pair : setting_values)
std::cerr << pair.first << " : " << pair.second << std::endl;
}
};
/*!
* Base class for an object which passes on settings from another object.
* An object which is a subclass of SettingsMessenger can be handled as a SettingsBase;
* the difference is that such an object cannot hold any settings, but can only pass on the settings from its parent.
*/
class SettingsMessenger : public SettingsBaseVirtual
{
public:
SettingsMessenger(SettingsBaseVirtual* parent); //!< construct a SettingsMessenger with a parent settings object
void setSetting(std::string key, std::string value); //!< Set a setting of the parent SettingsBase to a given value
std::string getSettingString(std::string key); //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
};
}//namespace cura
#endif//SETTINGS_H
+124 -61
Ver Arquivo
@@ -1,69 +1,107 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skin.h"
#include "polygonOptimizer.h"
#include "utils/polygonUtils.h"
#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)
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++)
{
SliceLayerPart* part = &layer->parts[partNr];
generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters);
generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
}
}
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];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
SliceLayer& layer = storage.layers[layer_nr];
if (downSkinCount == 0 && upSkinCount == 0)
{
SliceLayerPart* part = &layer->parts[partNr];
Polygons upskin = part->insets.back().offset(-extrusionWidth/2);
Polygons downskin = upskin;
return;
}
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
{
SliceLayerPart& part = layer.parts[partNr];
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);
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3; // TODO: hardcoded value!
skin.removeSmallAreas(minAreaSize);
for (Polygons& skin_area_part : skin.splitIntoParts())
skin.removeSmallAreas(MIN_AREA_SIZE);
for (PolygonsPart& skin_area_part : skin.splitIntoParts())
{
part->skin_parts.emplace_back();
part->skin_parts.back().outline = skin_area_part;
part.skin_parts.emplace_back();
part.skin_parts.back().outline = skin_area_part;
}
}
}
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters)
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
if (insetCount == 0)
{
@@ -77,15 +115,16 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
skin_part.insets.push_back(Polygons());
if (i == 0)
{
offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters);
PolygonUtils::offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters_0);
Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
skin_part.perimeterGaps.add(in_between);
} else
{
offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
PolygonUtils::offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
}
optimizePolygons(skin_part.insets[i]);
// optimize polygons: remove unnnecesary verts
skin_part.insets[i].simplify();
if (skin_part.insets[i].size() < 1)
{
skin_part.insets.pop_back();
@@ -95,13 +134,17 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateSparse(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)
{
Polygons sparse = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
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)
{
@@ -109,41 +152,62 @@ void generateSparse(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
{
for(SkinPart& skin_part : part2.skin_parts)
{
sparse = sparse.difference(skin_part.outline);
infill = infill.difference(skin_part.outline);
}
}
}
sparse.removeSmallAreas(3.0);//(2 * M_PI * INT2MM(config.extrusionWidth) * INT2MM(config.extrusionWidth)) * 3;
infill.removeSmallAreas(MIN_AREA_SIZE);
part.sparse_outline.push_back(sparse.offset(infill_skin_overlap));
part.infill_area.push_back(infill.offset(infill_skin_overlap));
}
}
void combineSparseLayers(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.sparse_outline[n - 1].intersection(part2.sparse_outline[0]).offset(-200).offset(200);
result.add(intersection);
part.sparse_outline[n - 1] = part.sparse_outline[n - 1].difference(intersection);
part2.sparse_outline[0] = part2.sparse_outline[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.sparse_outline.push_back(result);
}
}
}
@@ -176,8 +240,7 @@ void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusio
}
part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
}
double minAreaSize = (2 * M_PI * INT2MM(extrusionWidth) * INT2MM(extrusionWidth)) * 0.3; // TODO: hardcoded value!
part.perimeterGaps.removeSmallAreas(minAreaSize);
part.perimeterGaps.removeSmallAreas(MIN_AREA_SIZE);
}
}
+28 -6
Ver Arquivo
@@ -26,10 +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);
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)
@@ -39,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.
@@ -49,19 +55,35 @@ void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidt
* \param part The part where the skin outline information (input) is stored and where the skin insets (output) are stored.
* \param extrusionWidth extrusionWidth
* \param insetCount The number of perimeters to surround the skin
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
*/
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters);
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 generateSparse(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
void combineSparseLayers(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
+58 -65
Ver Arquivo
@@ -2,94 +2,87 @@
#include "skirt.h"
#include "support.h"
#include <queue>
namespace cura
{
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength)
void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength)
{
if (count == 0) return;
bool externalOnly = (distance > 0);
bool externalOnly = (distance > 0); // whether to include holes or not
Polygons support;
if (storage.support.generated)
support = storage.support.supportAreasPerLayer[0];
{ // get support polygons
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.layers.size() < 1) continue;
SliceLayer* layer = &mesh.layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
support = support.difference(layer->parts[i].outline);
}
// expand and contract to smooth the final polygon
if (count == 1 && distance > 0)
{
int dist = extrusionWidth * 5;
support = support.offset(dist).offset(-dist);
}
}
int primary_extruder = 0; // TODO allow for other extruder to be primary
int primary_extrusion_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");
int overshoot = 0; // distance by which to expand and contract the skirt to approximate the convex hull of the first layer
if (count == 1 && distance > 0)
{
overshoot = 100000; // 10 cm
}
Polygons& skirt_primary_extruder = storage.skirt[primary_extruder];
bool get_convex_hull = count == 1 && distance > 0;
Polygons first_layer_outline = storage.getLayerOutlines(0, true, externalOnly);
std::vector<Polygons> skirts;
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2 + overshoot;
int offsetDistance = distance + primary_extrusion_width * skirtNr + primary_extrusion_width / 2;
Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance));
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.layers.size() < 1) continue;
SliceLayer* layer = &mesh.layers[0];
for(unsigned int i=0; i<layer->parts.size(); i++)
{
if (externalOnly)
{
Polygons p;
p.add(layer->parts[i].outline[0]);
skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance, ClipperLib::jtRound));
}
else
{
skirtPolygons = skirtPolygons.unionPolygons(layer->parts[i].outline.offset(offsetDistance, ClipperLib::jtRound));
}
}
}
skirtPolygons = skirtPolygons.unionPolygons(support.offset(offsetDistance, ClipperLib::jtRound));
skirts.emplace_back(first_layer_outline.offset(offsetDistance, ClipperLib::jtRound));
Polygons& skirt_polygons = skirts.back();
//Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for(unsigned int n=0; n<skirtPolygons.size(); n++)
for(unsigned int n=0; n<skirt_polygons.size(); n++)
{
double area = skirtPolygons[n].area();
if (area < 0 && area > -extrusionWidth * extrusionWidth * 100)
skirtPolygons.remove(n--);
double area = skirt_polygons[n].area();
if (area < 0 && area > -primary_extrusion_width * primary_extrusion_width * 100)
skirt_polygons.remove(n--);
}
if (get_convex_hull)
{
skirt_polygons = skirt_polygons.convexHull();
}
storage.skirt.add(skirtPolygons);
skirt_primary_extruder.add(skirt_polygons);
int lenght = storage.skirt.polygonLength();
if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength) // make brim have more lines when total length is too small
int length = skirt_primary_extruder.polygonLength();
if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
count++;
}
//Add a skirt under the wipetower to make it stick better.
Polygons wipe_tower = storage.wipeTower.offset(-extrusionWidth / 2);
while(wipe_tower.size() > 0)
{
storage.skirt.add(wipe_tower);
wipe_tower = wipe_tower.offset(-extrusionWidth);
if (false) // the code below is for the old prime tower
{ //Add a skirt UNDER the prime tower to make it stick better.
Polygons prime_tower = storage.primeTower.ground_poly.offset(-primary_extrusion_width / 2);
std::queue<Polygons> prime_tower_insets;
while(prime_tower.size() > 0)
{
prime_tower_insets.emplace(prime_tower);
prime_tower = prime_tower.offset(-primary_extrusion_width);
}
while (!prime_tower_insets.empty())
{
Polygons& inset = prime_tower_insets.back();
skirt_primary_extruder.add(inset);
prime_tower_insets.pop();
}
}
if (overshoot > 0)
{
storage.skirt = storage.skirt.offset(-overshoot, ClipperLib::jtRound);
{ // process other extruders' brim/skirt (as one brim line around the old brim)
int offset_distance = 0;
int last_width = primary_extrusion_width;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{
if (extruder == primary_extruder) { continue; }
int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
offset_distance += last_width / 2 + width/2;
last_width = width;
while (storage.skirt[extruder].polygonLength() < minLength)
{
storage.skirt[extruder].add(skirts.back().offset(offset_distance, ClipperLib::jtRound));
offset_distance += width;
}
}
}
}
+1 -2
Ver Arquivo
@@ -12,11 +12,10 @@ namespace cura
*
* \param storage Storage containing the parts at the first layer
* \param distance The distance of the first outset from the parts at the first layer
* \param extrusionWidth extrusionWidth
* \param count Number of outsets / brim lines
* \param minLength The minimum length the skirt should have (enforced by taking more outsets)
*/
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength);
void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength);
}//namespace cura
+278
Ver Arquivo
@@ -0,0 +1,278 @@
#include "sliceDataStorage.h"
#include "FffProcessor.h" //To create a mesh group with if none is provided.
namespace cura
{
Polygons SliceLayer::getOutlines(bool external_polys_only)
{
Polygons ret;
getOutlines(ret, external_polys_only);
return ret;
}
void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
{
for (SliceLayerPart& part : parts)
{
if (external_polys_only)
{
result.add(part.outline.outerPolygon());
}
else
{
result.add(part.outline);
}
}
}
Polygons SliceLayer::getSecondOrInnermostWalls()
{
Polygons ret;
getSecondOrInnermostWalls(ret);
return ret;
}
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
{
for (SliceLayerPart& part : parts)
{
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(part.insets[1]);
continue;
}
// 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;
}
// 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;
}
}
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;
}
} // namespace cura
+165 -28
Ver Arquivo
@@ -4,37 +4,53 @@
#include "utils/intpoint.h"
#include "utils/polygon.h"
#include "utils/NoCopy.h"
#include "mesh.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "PrimeTower.h"
namespace cura
{
/*!
* A SkinPart is a connected area designated as top and/or bottom skin.
* Surrounding each non-bridged skin area with an outline may result in better top skins.
* It's filled during fffProcessor.processSliceData(.) and used in fffProcessor.writeGCode(.) to generate the final gcode.
* It's filled during FffProcessor.processSliceData(.) and used in FffProcessor.writeGCode(.) to generate the final gcode.
*/
class SkinPart
{
public:
Polygons outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
/*!
* 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.
Finally it's used in the fffProcessor.writeGCode(.) to generate the final gcode.
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
Finally it's used in the FffProcessor.writeGCode(.) to generate the final gcode.
*/
class SliceLayerPart
{
public:
AABB boundaryBox; //!< The boundaryBox is an axis-aligned bounardy box which is used to quickly check for possible collision between different parts on different layers. It's an optimalization used during skin calculations.
Polygons outline; //!< The outline is the first member that is filled, and it's filled with polygons that match a cross section of the 3D model. The first polygon is the outer boundary polygon and the rest are holes.
Polygons combBoundery; //!< The combBoundery is generated from the online. It's the area in which the nozzle tries to stay during traveling.
PolygonsPart outline; //!< The outline is the first member that is filled, and it's filled with polygons that match a cross section of the 3D model. The first polygon is the outer boundary polygon and the rest are holes.
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> sparse_outline; //!< The sparse_outline are the areas which need to be filled with sparse (0-99%) infill. The sparse_outline is an array to support thicker layers of sparse infill. sparse_outline[n] is sparse outline of (n+1) layers thick.
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.
};
@@ -48,63 +64,184 @@ public:
// TODO: remove this /\ unused member!
int printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft.
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 openLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
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);
};
/******************/
class SupportLayer
{
public:
Polygons supportAreas; //!< normal support areas
Polygons roofs; //!< the support areas which are to be printed as denser roofs. Note that the roof areas and support areas are mutually exclusive.
};
class SupportStorage
{
public:
bool generated; //!< whether generateSupportGrid(.) has completed (successfully)
std::vector<Polygons> supportAreasPerLayer;
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
std::vector<SupportLayer> supportLayers;
SupportStorage(){}
~SupportStorage(){supportAreasPerLayer.clear(); }
SupportStorage() : generated(false), layer_nr_max_filled_layer(-1) { }
~SupportStorage(){ supportLayers.clear(); }
};
/******************/
class SliceMeshStorage
class SliceMeshStorage : public SettingsMessenger // passes on settings from a Mesh object
{
public:
SettingsBase* settings;
std::vector<SliceLayer> layers;
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
RetractionConfig retraction_config;
GCodePathConfig inset0_config;
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
GCodePathConfig infill_config[MAX_SPARSE_COMBINE];
std::vector<GCodePathConfig> infill_config;
GCodePathConfig wall_reinforcement_config;
SliceMeshStorage(SettingsBase* settings)
: settings(settings), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN")
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"), wall_reinforcement_config(&retraction_config, "SUPPORT")
{
for(int n=0; n<MAX_SPARSE_COMBINE; n++)
infill_config[n] = GCodePathConfig(&retraction_config, "FILL");
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(&retraction_config, "FILL");
}
};
class SliceDataStorage
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.)
Point3 model_size, model_min, model_max;
Polygons skirt;
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
std::vector<Polygons> oozeShield; //oozeShield per layer
std::vector<SliceMeshStorage> meshes;
RetractionConfig retraction_config;
GCodePathConfig skirt_config;
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;
SupportStorage support;
Polygons wipeTower;
Polygons skirt[MAX_EXTRUDERS]; //!< Skirt polygons per extruder, ordered from inner to outer polygons
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
int max_object_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder
PrimeTower primeTower;
std::vector<Polygons> oozeShield; //oozeShield per layer
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
Point wipePoint;
SliceDataStorage()
: skirt_config(&retraction_config, "SKIRT"), support_config(&retraction_config, "SUPPORT")
std::vector<RetractionConfig> initializeRetractionConfigs()
{
std::vector<RetractionConfig> ret;
ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
return ret;
}
std::vector<GCodePathConfig> initializeSkirtConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
}
return ret;
}
/*!
* \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()
{
}
/*!
* Get all outlines within a given layer.
*
* \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(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
+29 -22
Ver Arquivo
@@ -5,7 +5,7 @@
#include "utils/logoutput.h"
#include "slicer.h"
#include "polygonOptimizer.h"
#include "debug.h" // TODO remove
namespace cura {
@@ -13,6 +13,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
{
Polygons openPolygonList;
// connect line segments
for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
{
if (segmentList[startSegment].addedToPolygon)
@@ -30,20 +31,22 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
Point p0 = segmentList[segmentIndex].end;
poly.add(p0);
int nextIndex = -1;
MeshFace* face = &mesh->faces[segmentList[segmentIndex].faceIndex];
const MeshFace& face = mesh->faces[segmentList[segmentIndex].faceIndex];
for(unsigned int i=0;i<3;i++)
{
if (face->connected_face_index[i] > -1 && face_idx_to_segment_index.find(face->connected_face_index[i]) != face_idx_to_segment_index.end())
decltype(face_idx_to_segment_index.begin()) it;
if (face.connected_face_index[i] > -1 && (it = face_idx_to_segment_index.find(face.connected_face_index[i])) != face_idx_to_segment_index.end())
{
Point p1 = segmentList[face_idx_to_segment_index[face->connected_face_index[i]]].start;
int index = (*it).second;
Point p1 = segmentList[index].start;
Point diff = p0 - p1;
if (shorterThen(diff, MM2INT(0.01)))
{
if (face_idx_to_segment_index[face->connected_face_index[i]] == static_cast<int>(startSegment))
if (index == static_cast<int>(startSegment))
canClose = true;
if (segmentList[face_idx_to_segment_index[face->connected_face_index[i]]].addedToPolygon)
if (segmentList[index].addedToPolygon)
continue;
nextIndex = face_idx_to_segment_index[face->connected_face_index[i]];
nextIndex = index;
}
}
}
@@ -58,6 +61,8 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
}
//Clear the segmentList to save memory, it is no longer needed after this point.
segmentList.clear();
// TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons.
//Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
//First link up polygon ends that are within 2 microns.
@@ -88,9 +93,11 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
}
}
//Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
while(1)
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::NORMAL)
{
//Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
while(1)
{
int64_t bestScore = MM2INT(10.0) * MM2INT(10.0);
unsigned int bestA = -1;
unsigned int bestB = -1;
@@ -154,7 +161,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
}
}
}
}
if (extensive_stitching)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
@@ -166,7 +173,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
{
unsigned int bestA = -1;
unsigned int bestB = -1;
gapCloserResult bestResult;
GapCloserResult bestResult;
bestResult.len = POINT_MAX;
bestResult.polygonIdx = -1;
bestResult.pointIdxA = -1;
@@ -177,7 +184,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
if (openPolygonList[i].size() < 1) continue;
{
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
GapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
@@ -190,7 +197,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
{
if (openPolygonList[j].size() < 1 || i == j) continue;
gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
GapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
if (res.len > 0 && res.len < bestResult.len)
{
bestA = i;
@@ -275,7 +282,7 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
for(unsigned int i=0;i<openPolygonList.size();i++)
{
if (openPolygonList[i].size() > 0)
openPolygons.newPoly() = openPolygonList[i];
openPolylines.add(openPolygonList[i]);
}
//Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
@@ -298,9 +305,9 @@ void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
optimizePolygons(polygonList);
polygonList.simplify();
polygonList = polygonList.removeDegenerateVerts(); // remove verts connected to overlapping line segments
polygonList.removeDegenerateVerts(); // remove verts connected to overlapping line segments
int xy_offset = mesh->getSettingInMicrons("xy_offset");
if (xy_offset != 0)
@@ -321,9 +328,9 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool kee
layers[layer_nr].z = initial + thickness * layer_nr;
}
for(unsigned int i=0; i<mesh->faces.size(); i++)
for(unsigned int mesh_idx = 0; mesh_idx < mesh->faces.size(); mesh_idx++)
{
MeshFace& face = mesh->faces[i];
MeshFace& face = mesh->faces[mesh_idx];
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
@@ -333,8 +340,8 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool kee
if (p2.z < minZ) minZ = p2.z;
if (p1.z > maxZ) maxZ = p1.z;
if (p2.z > maxZ) maxZ = p2.z;
for(int32_t layer_nr = (minZ - initial) / thickness; layer_nr <= (maxZ - initial) / thickness; layer_nr++)
int32_t layer_max = (maxZ - initial) / thickness;
for(int32_t layer_nr = (minZ - initial) / thickness; layer_nr <= layer_max; layer_nr++)
{
int32_t z = layer_nr * thickness + initial;
if (z < minZ) continue;
@@ -361,8 +368,8 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool kee
// on the slice would create two segments
continue;
}
layers[layer_nr].face_idx_to_segment_index[i] = layers[layer_nr].segmentList.size();
s.faceIndex = i;
layers[layer_nr].face_idx_to_segment_index.insert(std::make_pair(mesh_idx, layers[layer_nr].segmentList.size()));
s.faceIndex = mesh_idx;
s.addedToPolygon = false;
layers[layer_nr].segmentList.push_back(s);
}
+10 -10
Ver Arquivo
@@ -18,7 +18,7 @@ public:
bool addedToPolygon;
};
class closePolygonResult
class ClosePolygonResult
{ //The result of trying to find a point on a closed polygon line. This gives back the point index, the polygon index, and the point of the connection.
//The line on which the point lays is between pointIdx-1 and pointIdx
public:
@@ -26,7 +26,7 @@ public:
int polygonIdx;
unsigned int pointIdx;
};
class gapCloserResult
class GapCloserResult
{
public:
int64_t len;
@@ -40,20 +40,20 @@ class SlicerLayer
{
public:
std::vector<SlicerSegment> segmentList;
std::map<int, int> face_idx_to_segment_index;
std::unordered_map<int, int> face_idx_to_segment_index; // topology
int z;
Polygons polygonList;
Polygons openPolygons;
Polygons openPolylines;
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
private:
gapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
{
gapCloserResult ret;
closePolygonResult c1 = findPolygonPointClosestTo(ip0);
closePolygonResult c2 = findPolygonPointClosestTo(ip1);
GapCloserResult ret;
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
@@ -102,9 +102,9 @@ private:
return ret;
}
closePolygonResult findPolygonPointClosestTo(Point input)
ClosePolygonResult findPolygonPointClosestTo(Point input)
{
closePolygonResult ret;
ClosePolygonResult ret;
for(unsigned int n=0; n<polygonList.size(); n++)
{
Point p0 = polygonList[n][polygonList[n].size()-1];
+200 -105
Ver Arquivo
@@ -3,9 +3,71 @@
#include <cmath> // sqrt
#include <utility> // pair
#include "Progress.h"
namespace cura
{
Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_this, int64_t supportJoinDistance, int64_t smoothing_distance, int min_smoothing_area, bool conical_support, int64_t conical_support_offset, int64_t conical_smallest_breadth)
{
Polygons joined;
if (conical_support)
{
Polygons insetted = supportLayer_up.offset(-conical_smallest_breadth/2);
Polygons small_parts = supportLayer_up.difference(insetted.offset(conical_smallest_breadth/2+20));
joined = supportLayer_this.unionPolygons(supportLayer_up.offset(conical_support_offset))
.unionPolygons(small_parts);
}
else
{
joined = supportLayer_this.unionPolygons(supportLayer_up);
}
// join different parts
if (supportJoinDistance > 0)
{
joined = joined.offset(supportJoinDistance)
.offset(-supportJoinDistance);
}
if (smoothing_distance > 0)
joined = joined.smooth(smoothing_distance, min_smoothing_area);
return joined;
}
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
{
// initialization of supportAreasPerLayer
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
storage.support.supportLayers.emplace_back();
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_idx, layer_count, supportAreas, commandSocket);
if (mesh.getSettingBoolean("support_roof_enable"))
{
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
}
else
{
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
{
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
}
}
}
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
{
storage.support.supportLayers[layer_idx].supportAreas = storage.support.supportLayers[layer_idx].supportAreas.unionPolygons();
}
storage.support.generated = true;
}
/*
* Algorithm:
@@ -18,40 +80,45 @@ namespace cura
*
* for support buildplate only: purge all support not connected to buildplate
*/
void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, int layer_count)
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->settings->getSettingAsSupportType("support_type");
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
storage.support.generated = false;
if (!object->settings->getSettingBoolean("support_enable"))
if (!mesh.getSettingBoolean("support_enable"))
return;
if (support_type == Support_None)
if (support_type == ESupportType::NONE)
return;
double supportAngle = object->settings->getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == Support_PlatformOnly;
int supportZDistance = object->settings->getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = object->settings->getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = object->settings->getSettingInMicrons("support_top_distance");
int supportJoinDistance = object->settings->getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = object->settings->getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = object->settings->getSettingInMicrons("support_area_smoothing");
int supportTowerDiameter = object->settings->getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = object->settings->getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = object->settings->getSettingInAngleRadians("support_tower_roof_angle");
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
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 = mesh.getSettingInMicrons("support_offset");
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->settings->getSettingInMicrons("layer_height");
int extrusionWidth = object->settings->getSettingInMicrons("wall_line_width_x"); // TODO check for layer0extrusionWidth!
int supportXYDistance = object->settings->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 = 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:
@@ -62,12 +129,22 @@ void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, i
int supportLayerThickness = layerThickness;
int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
int layerZdistanceBottom = supportZDistanceBottom / supportLayerThickness;
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged
int support_layer_count = layer_count;
int64_t conical_support_offset;
if (conical_support_angle > 0)
{
conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
}
else
{
conical_support_offset = -(tan(-conical_support_angle) - 0.01) * supportLayerThickness;
}
unsigned int support_layer_count = layer_count;
double tanTowerRoofAngle = tan(supportTowerRoofAngle);
int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle;
@@ -85,47 +162,57 @@ void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, i
// computation
std::vector<Polygons> joinedLayers; // join model layers of all meshes into polygons and store small areas which need tower support
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::joinMeshesAndDetectOverhangPoints(storage, joinedLayers, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
// initialization of supportAreasPerLayer
for (int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
storage.support.supportAreasPerLayer.emplace_back();
bool still_in_upper_empty_layers = true;
int overhang_points_pos = overhang_points.size() - 1;
Polygons supportLayer_last;
std::vector<Polygons> towerRoofs;
for (int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx >= 0 ; layer_idx--)
for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--)
{
Polygons overhang;
{
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
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);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
Polygons supportLayer_supportee = joinedLayers[layer_idx+layerZdistanceTop];
Polygons supportLayer_supported = joinedLayers[layer_idx-1+layerZdistanceTop].offset(maxDistFromLowerLayer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
support_extension = support_extension.intersection(supportLayer_supported);
support_extension = support_extension.intersection(supportLayer_supportee);
Polygons overhang = basic_overhang.unionPolygons(support_extension);
/* supported
* .................
* ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
// Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
//
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
// presumably the computation above is slower than the one below
Polygons overhang_extented = basic_overhang.offset(maxDistFromLowerLayer + 100); // +100 for easier joining with support from layer above
overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
/* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^ supporter
* ^^^^^^^^^^^^^^^^^ supported
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
}
Polygons& supportLayer_this = overhang;
supportLayer_this = supportLayer_this.simplify(50); // TODO: hardcoded value!
if (extension_offset)
{
supportLayer_this = supportLayer_this.offset(extension_offset);
}
supportLayer_this.simplify(50); // TODO: hardcoded value!
if (supportMinAreaSqrt > 0)
{
@@ -134,68 +221,58 @@ void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, i
// handle towers
AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower);
}
if (layer_idx+1 < support_layer_count)
{ // join with support from layer up
Polygons& supportLayer_up = supportLayer_last;
Polygons joined = supportLayer_this.unionPolygons(supportLayer_up);
// join different parts
if (supportJoinDistance > 0)
{
joined = joined.offset(supportJoinDistance);
joined = joined.offset(-supportJoinDistance);
}
if (smoothing_distance > 0)
joined = joined.smooth(smoothing_distance, min_smoothing_area);
// remove layer
Polygons insetted = joined.difference(joinedLayers[layer_idx]);
supportLayer_this = insetted;
{ // join with support from layer up
supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, min_smoothing_area, conical_support, conical_support_offset, conical_smallest_breadth);
}
supportLayer_last = supportLayer_this;
// inset using X/Y distance
if (supportLayer_this.size() > 0)
supportLayer_this = supportLayer_this.difference(joinedLayers[layer_idx].offset(supportXYDistance));
// move up from model
if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom)
{
int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1;
int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight;
supportLayer_this = supportLayer_this.difference(joinedLayers[bottomLayer]);
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false));
}
storage.support.supportAreasPerLayer[layer_idx] = supportLayer_this;
logProgress("support", support_layer_count - layer_idx, support_layer_count);
supportLayer_last = supportLayer_this;
// inset using X/Y distance
if (supportLayer_this.size() > 0)
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
supportAreas[layer_idx] = supportLayer_this;
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = layer_idx;
still_in_upper_empty_layers = false;
}
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
if (supportOnBuildplateOnly)
{
Polygons touching_buildplate = storage.support.supportAreasPerLayer[0];
for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportAreasPerLayer.size() ; layer_idx++)
Polygons touching_buildplate = supportAreas[0]; // TODO: not working for conical support!
for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportLayers.size() ; layer_idx++)
{
Polygons& supportLayer = storage.support.supportAreasPerLayer[layer_idx];
Polygons& supportLayer = supportAreas[layer_idx];
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
storage.support.supportAreasPerLayer[layer_idx] = touching_buildplate;
supportAreas[layer_idx] = touching_buildplate;
}
}
storage.support.generated = true;
}
void AreaSupport::joinMeshesAndDetectOverhangPoints(
void AreaSupport::detectOverhangPoints(
SliceDataStorage& storage,
std::vector<Polygons>& joinedLayers,
SliceMeshStorage& mesh,
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points, // stores overhang_points along with the layer index at which the overhang point occurs)
int layer_count,
int supportMinAreaSqrt,
@@ -204,30 +281,26 @@ void AreaSupport::joinMeshesAndDetectOverhangPoints(
{
for (int layer_idx = 0 ; layer_idx < layer_count ; layer_idx++)
{
joinedLayers.emplace_back();
for (SliceMeshStorage& mesh : storage.meshes)
SliceLayer& layer = mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
SliceLayer& layer = mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
if (part.outline.outerPolygon().area() < supportMinAreaSqrt * supportMinAreaSqrt)
{
Polygons part_poly_computed;
Polygons& part_poly = (part.insets.size() > 0)? part.insets[0] : part_poly_computed; // don't copy inset if its already computed
if (part.insets.size() == 0) { part_poly_computed = part.outline.offset(-extrusionWidth/2); }
if (part.outline[0].area() < supportMinAreaSqrt * supportMinAreaSqrt)
if (part_poly.size() > 0)
{
Polygons part_poly = part.outline.offset(-extrusionWidth/2);
if (part_poly.size() > 0)
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
overhang_points.back().second.push_back(part_poly);
else
{
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
overhang_points.back().second.push_back(part_poly);
else
{
std::vector<Polygons> small_part_polys;
small_part_polys.push_back(part_poly);
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
}
std::vector<Polygons> small_part_polys;
small_part_polys.push_back(part_poly);
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
}
}
joinedLayers.back() = joinedLayers.back().unionPolygons(part.outline);
}
}
@@ -336,6 +409,28 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
{
int roof_layer_count = support_roof_height / layerThickness;
std::vector<SupportLayer>& supportLayers = storage.support.supportLayers;
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
{
SupportLayer& layer = supportLayers[layer_idx];
if (layer_idx + roof_layer_count < supportLayers.size())
{
Polygons roofs = supportAreas[layer_idx].difference(supportAreas[layer_idx + roof_layer_count]);
roofs.removeSmallAreas(1.0);
layer.roofs.add(roofs);
layer.supportAreas.add(supportAreas[layer_idx].difference(layer.roofs));
}
else
{
layer.roofs.add(layer.supportAreas);
}
}
}
+54 -15
Ver Arquivo
@@ -3,13 +3,64 @@
#define SUPPORT_H
#include "sliceDataStorage.h"
#include "modelFile/modelFile.h"
#include "MeshGroup.h"
#include "commandSocket.h"
namespace cura {
class AreaSupport {
public:
/*!
* Generate the support areas and support roof areas for all models.
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
private:
/*!
* Generate support polygons over all layers for one object.
*
* 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 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, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
/*!
* Generate support roof areas and non-roof areas for a given mesh.
*
* \param storage Output storage: support area + support roof area output
* \param supportAreas The basic support areas for the current mesh
* \param commandSocket Socket over which to report the progress
* \param layerThickness The layer height
* \param support_roof_height The thickness of the hammock in z directiontt
*/
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
*
* \param supportLayer_up The support areas the layer above
* \param supportLayer_this The overhang areas of the current layer at hand
* \param supportJoinDistance The distance to be filled between two support areas
* \param smoothing_distance Maximal distance in the X/Y directions of a line segment which is to be smoothed out.
* \param min_smoothing_area minimal area for which to perform smoothing
* \param conical_support Whether the support should be conical instead of cylindrical
* \param conical_support_offset The offset determining the angle of the conical support
* \param conical_smallest_breadth The breadth of the smallest support area which is not to be redoces to a smaller size due to conical support.
*
* \return The joined support areas for this layer.
*/
static Polygons join(Polygons& supportLayer_up, Polygons& supportLayer_this, int64_t supportJoinDistance, int64_t smoothing_distance, int min_smoothing_area, bool conical_support, int64_t conical_support_offset, int64_t conical_smallest_breadth);
/*!
* Joins the layerpart outlines of all meshes and collects the overhang points (small areas).
* \param storage input layer outline information
@@ -18,9 +69,9 @@ public:
* \param supportMinAreaSqrt diameter of the minimal area which can be supported without a specialized strut
* \param extrusionWidth extrusionWidth
*/
static void joinMeshesAndDetectOverhangPoints(
static void detectOverhangPoints(
SliceDataStorage& storage,
std::vector<Polygons>& joinedLayers,
SliceMeshStorage& mesh,
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
int layer_count,
int supportMinAreaSqrt,
@@ -67,18 +118,6 @@ public:
);
};
/*!
* Generate support polygons over all layers.
*
* 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 and containing the output support storage per layer
* \param object The object for which to generate support areas
* \param layer_count total number of layers
*/
void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, int layer_count);
}//namespace cura
+14 -9
Ver Arquivo
@@ -7,10 +7,6 @@
#include "utils/polygonUtils.h"
using namespace cura;
/*
#include "utils/intpoint.h"
#include "utils/polygon.h"
@@ -166,6 +162,8 @@ void test_BucketGrid2D()
/*
#include <math.h>
#include "utils/gettime.h"
#include "utils/polygonUtils.h"
void test_findClosestConnection()
{
srand(1234);
@@ -267,6 +265,11 @@ void test_findClosestConnection()
}
*/
#include "utils/polygon.h"
namespace cura
{
void test_clipper()
{
Polygon p;
@@ -300,13 +303,15 @@ void test_clipper()
Polygons polys;
polys.add(p);
polys.debugOutputHTML("output/problem_test.html", true);
polys.offset(-400).debugOutputHTML("output/problem_test_offset.html", true);
polys = polys.removeDegenerateVerts();
polys.offset(-400).debugOutputHTML("output/problem_test_offset_solved.html", true);
// polys.debugOutputHTML("output/problem_test.html", true);
// polys.offset(-400).debugOutputHTML("output/problem_test_offset.html", true);
polys.removeDegenerateVerts();
// polys.offset(-400).debugOutputHTML("output/problem_test_offset_solved.html", true);
}
int main(int argc, char **argv)
{
// test_findClosestConnection();
test_clipper();
}
}
}//namespace cura
+19 -6
Ver Arquivo
@@ -4,6 +4,9 @@
#include <algorithm>
#include "timeEstimate.h"
namespace cura
{
#define MINIMUM_PLANNER_SPEED 0.05// (mm/sec)
const double max_feedrate[TimeEstimateCalculator::NUM_AXIS] = {600, 600, 40, 25};
@@ -21,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();
}
@@ -187,14 +197,15 @@ double TimeEstimateCalculator::calculate()
forward_pass();
recalculate_trapezoids();
double totalTime = 0;
double totalTime = extra_time;
for(unsigned int n=0; n<blocks.size(); n++)
{
double plateau_distance = blocks[n].decelerate_after - blocks[n].accelerate_until;
Block& block = blocks[n];
double plateau_distance = block.decelerate_after - block.accelerate_until;
totalTime += acceleration_time_from_distance(blocks[n].initial_feedrate, blocks[n].accelerate_until, blocks[n].acceleration);
totalTime += plateau_distance / blocks[n].nominal_feedrate;
totalTime += acceleration_time_from_distance(blocks[n].final_feedrate, (blocks[n].distance - blocks[n].decelerate_after), blocks[n].acceleration);
totalTime += acceleration_time_from_distance(block.initial_feedrate, block.accelerate_until, block.acceleration);
totalTime += plateau_distance / block.nominal_feedrate;
totalTime += acceleration_time_from_distance(block.final_feedrate, (block.distance - block.decelerate_after), block.acceleration);
}
return totalTime;
}
@@ -283,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];
@@ -305,3 +316,5 @@ void TimeEstimateCalculator::recalculate_trapezoids()
next->recalculate_flag = false;
}
}
}//namespace cura
+16 -4
Ver Arquivo
@@ -4,10 +4,13 @@
#include <stdint.h>
#include <vector>
/**
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.
*/
namespace cura
{
/*!
* 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
{
@@ -51,6 +54,8 @@ public:
};
private:
double extra_time;
Position previous_feedrate;
double previous_nominal_feedrate;
@@ -58,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();
@@ -73,4 +84,5 @@ private:
void planner_forward_pass_kernel(Block *previous, Block *current, Block *next);
};
}//namespace cura
#endif//TIME_ESTIMATE_H
+125
Ver Arquivo
@@ -0,0 +1,125 @@
#ifndef AABB_H
#define AABB_H
#include <limits>
#include "intpoint.h"
#include "polygon.h"
namespace cura
{
/* Axis aligned boundary box */
class AABB
{
public:
Point min, max;
AABB()
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
}
AABB(Point&min, Point& max)
: min(min), max(max)
{
}
AABB(Polygons& polys)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
calculate(polys);
}
void calculate(Polygons& polys)
{
min = Point(POINT_MAX, POINT_MAX);
max = Point(POINT_MIN, POINT_MIN);
for(unsigned int i=0; i<polys.size(); i++)
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
include(polys[i][j]);
}
}
}
bool hit(const AABB& other) const
{
if (max.X < other.min.X) return false;
if (min.X > other.max.X) return false;
if (max.Y < other.min.Y) return false;
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);
}
};
/*!
An Axis Aligned Bounding Box. Has a min and max vector, representing minimal and maximal coordinates in the three axes.
*/
struct AABB3D
{
Point3 min; //!< The minimal coordinates in x, y and z direction
Point3 max; //!< The maximal coordinates in x, y and z direction
/*!
* Create an AABB3D with coordinates at the numeric limits.
*/
AABB3D()
: min(std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max())
, max(std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::min())
{
}
/*!
* Expand the AABB3D to include the point \p p.
* \param p The point to include with the bounding box.
*/
void include(Point3 p)
{
min.x = std::min(min.x, p.x);
min.y = std::min(min.y, p.y);
min.z = std::min(min.z, p.z);
max.x = std::max(max.x, p.x);
max.y = std::max(max.y, p.y);
max.z = std::max(max.z, p.z);
}
/*!
* Offset the coordinates of the bounding box.
* \param offset The offset with which to offset the AABB3D.
*/
void offset(Point3 offset)
{
min += offset;
max += offset;
}
/*!
* Offset the coordinates of the bounding box.
* \param offset The offset with which to offset the AABB3D.
*/
void offset(Point offset)
{
min += offset;
max += offset;
}
};
}//namespace cura
#endif//AABB_H
+7 -7
Ver Arquivo
@@ -1,6 +1,6 @@
/** Copyright (C) 2015 Tim Kuipers- Released under terms of the AGPLv3 License */
#ifndef BUCKET_GRID_2D_H
#define BUCKET_GRID_2D_H
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_BUCKET_GRID_2D_H
#define UTILS_BUCKET_GRID_2D_H
#include "logoutput.h"
#include "intpoint.h"
@@ -46,7 +46,6 @@ private:
inline uint32_t pointHash_simple(const Point& p) const
{
return p.X ^ (p.Y << 8);
//return (p.X / 20000) ^ (p.Y / 20000) << 8;
}
/*!
@@ -200,8 +199,9 @@ public:
*/
void insert(Point& p, T& t)
{
typedef typename Map::iterator iter;
std::pair<iter, bool> emplaced = point2object.emplace(p, t);
// typedef typename Map::iterator iter;
// std::pair<iter, bool> emplaced =
point2object.emplace(p, t);
// if (! emplaced.second)
// logError("Error! BucketGrid2D couldn't insert object!");
};
@@ -212,5 +212,5 @@ public:
};
} // namespace cura
}//namespace cura
#endif//BUCKET_GRID_2D_H
+27
Ver Arquivo
@@ -0,0 +1,27 @@
#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.
*/
class NoCopy
{
protected:
NoCopy() {}
private:
/*!
* Private copy constructor.
* Cannot be called because it is private.
*/
NoCopy(const NoCopy&);
/*!
* Private assign operator.
* Cannot be called because it is private.
*/
NoCopy& operator =(const NoCopy&);
};
#endif // UTILS_NO_COPY_H
+279
Ver Arquivo
@@ -0,0 +1,279 @@
#ifndef SVG_H
#define SVG_H
#include <stdio.h> // for file output
#include "polygon.h"
#include "intpoint.h"
#include "AABB.h"
#include "logoutput.h"
namespace cura {
class SVG
{
public:
enum class Color {
BLACK,
WHITE,
GRAY,
RED,
BLUE,
GREEN,
YELLOW
};
private:
std::string toString(Color color)
{
switch (color)
{
case SVG::Color::BLACK: return "black";
case SVG::Color::WHITE: return "white";
case SVG::Color::GRAY: return "gray";
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";
}
}
FILE* out; // the output file
const AABB aabb; // the boundary box to display
const Point aabb_size;
const double scale;
public:
SVG(const char* filename, AABB aabb, Point canvas_size = Point(1024 * 4, 1024 * 4))
: aabb(aabb)
, aabb_size(aabb.max - aabb.min)
, 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.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'/>");
// fprintf(out, "</marker>");
}
~SVG()
{
fprintf(out, "</svg>\n");
fprintf(out, "</body></html>");
fclose(out);
}
/*!
* transform a point in real space to canvas space
*/
Point transform(const Point& p)
{
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + Point(10,10);
}
private:
// void _writeLines(PolygonRef polygon, Color color = Color::GRAY)
// {
// for(unsigned int n=0; n<polygon.size(); n++)
// {
// if (n == 0)
// fprintf(out, "M");
// else
// fprintf(out, "L");
// Point pf = transform(polygon[n]);
// fprintf(out, "%lli,%lli ", pf.X, pf.Y);
// }
// fprintf(out, "Z\n");
// }
public:
// void writeLines(Polygons& polygons, Color color = Color::GRAY, Color outline_color = Color::BLACK)
// {
// fprintf(out, "<g fill-rule='evenodd' style=\"fill: %s; stroke:%s;stroke-width:1\">\n", toString(color).c_str(), toString(outline_color).c_str());
// fprintf(out, "<path marker-mid='url(#MidMarker)' d=\"");
// for(PolygonRef poly : polygons)
// {
// _writeLines(poly, outline_color);
// }
// fprintf(out, "\"/>");
// fprintf(out, "</g>\n");
// }
// void writeLines(PolygonRef poly, Color color = Color::GRAY, Color outline_color = Color::BLACK)
// {
// fprintf(out, "<g fill-rule='evenodd' style=\"fill: %s; stroke:%s;stroke-width:1\">\n", toString(color).c_str(), toString(outline_color).c_str());
// fprintf(out, "<path marker-mid='url(#MidMarker)' d=\"");
// writeLines(poly, outline_color);
// fprintf(out, "\"/>");
// fprintf(out, "</g>\n");
// }
void writeAreas(Polygons& polygons, Color color = Color::GRAY, Color outline_color = Color::BLACK)
{
for(PolygonsPart& parts : polygons.splitIntoParts())
{
for(unsigned int j=0;j<parts.size();j++)
{
Polygon poly = parts[j];
fprintf(out, "<polygon points=\"");
for(Point& p : poly)
{
Point fp = transform(p);
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());
else
fprintf(out, "\" style=\"fill:white;stroke:%s;stroke-width:1\" />\n", toString(outline_color).c_str());
}
}
}
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.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.X, pf.Y, p.X, p.Y);
}
}
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
for (Point& p : poly)
{
writePoint(p, write_coords, size, color);
}
}
void writePoints(Polygons& polygons, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
for (PolygonRef poly : polygons)
{
writePoints(poly, write_coords, size, color);
}
}
/*!
* \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;
}
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)
{
fprintf(out, txt, args...);
}
/*
void Polygons::debugOutputHTML(const char* filename, bool dotTheVertices)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point modelSize = max() - min();
modelSize.X = std::max(modelSize.X, modelSize.Y);
modelSize.Y = std::max(modelSize.X, modelSize.Y);
Point modelMin = min();
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
for(PolygonsPart& parts : splitIntoParts())
{
for(unsigned int j=0;j<parts.size();j++)
{
Polygon poly = parts[j];
fprintf(out, "<polygon points=\"");
for(Point& p : poly)
{
fprintf(out, "%f,%f ", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
}
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
if (dotTheVertices)
for(Point& p : poly)
fprintf(out, "<circle cx=\"%f\" cy=\"%f\" r=\"2\" stroke=\"black\" stroke-width=\"3\" fill=\"black\" />", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
}
}
fprintf(out, "</svg>\n");
fprintf(out, "</body></html>");
fclose(out);
} */
};
} // namespace cura
#endif // SVG_H
+52 -48
Ver Arquivo
@@ -2,22 +2,25 @@
#ifndef FLOAT_POINT_H
#define FLOAT_POINT_H
/*
Floating point 3D points are used during model loading as 3D vectors.
They represent millimeters in 3D space.
*/
#include "intpoint.h"
#include <stdint.h>
#include <math.h>
namespace cura
{
/*
Floating point 3D points are used during model loading as 3D vectors.
They represent millimeters in 3D space.
*/
class FPoint3
{
public:
float x,y,z;
FPoint3() {}
FPoint3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {}
FPoint3(float _x, float _y, float _z): x(_x), y(_y), z(_z) {}
FPoint3(const Point3& p): x(p.x*.001), y(p.y*.001), z(p.z*.001) {}
FPoint3 operator+(const FPoint3& p) const { return FPoint3(x+p.x, y+p.y, z+p.z); }
@@ -25,9 +28,9 @@ public:
FPoint3 operator*(const float f) const { return FPoint3(x*f, y*f, z*f); }
FPoint3 operator/(const float f) const { return FPoint3(x/f, y/f, z/f); }
FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; }
FPoint3& operator += (const FPoint3& p) { x += p.x; y += p.y; z += p.z; return *this; }
FPoint3& operator -= (const FPoint3& p) { x -= p.x; y -= p.y; z -= p.z; return *this; }
FPoint3& operator *= (const float f) { x *= f; y *= f; z *= f; return *this; }
FPoint3& operator *= (const float f) { x *= f; y *= f; z *= f; return *this; }
bool operator==(FPoint3& p) const { return x==p.x&&y==p.y&&z==p.z; }
bool operator!=(FPoint3& p) const { return x!=p.x||y!=p.y||z!=p.z; }
@@ -52,48 +55,48 @@ public:
float vSize()
{
return sqrt(vSize2());
}
inline FPoint3 normalized()
{
return (*this)/vSize();
}
FPoint3 cross(const FPoint3& p)
{
return FPoint3(
y*p.z-z*p.y,
z*p.x-x*p.z,
x*p.y-y*p.x);
}
static FPoint3 cross(const Point3& a, const Point3& b)
{
return FPoint3(a).cross(FPoint3(b));
// FPoint3(
// a.y*b.z-a.z*b.y,
// a.z*b.x-a.x*b.z,
// a.x*b.y-a.y*b.x);
}
Point3 toPoint3()
{
return Point3(x*1000, y*1000, z*1000);
}
inline FPoint3 normalized()
{
return (*this)/vSize();
}
FPoint3 cross(const FPoint3& p)
{
return FPoint3(
y*p.z-z*p.y,
z*p.x-x*p.z,
x*p.y-y*p.x);
}
static FPoint3 cross(const Point3& a, const Point3& b)
{
return FPoint3(a).cross(FPoint3(b));
// FPoint3(
// a.y*b.z-a.z*b.y,
// a.z*b.x-a.x*b.z,
// a.x*b.y-a.y*b.x);
}
Point3 toPoint3()
{
return Point3(x*1000, y*1000, z*1000);
}
};
//inline FPoint3 operator+(FPoint3 lhs, const FPoint3& rhs) {
// lhs += rhs;
// return lhs;
//}
inline float operator*(FPoint3 lhs, const FPoint3& rhs) {
return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
}
//inline FPoint3 operator*(FPoint3 lhs, const float f) {
// lhs *= f;
// return lhs;
//}
//inline FPoint3 operator+(FPoint3 lhs, const FPoint3& rhs) {
// lhs += rhs;
// return lhs;
//}
inline float operator*(FPoint3 lhs, const FPoint3& rhs) {
return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
}
//inline FPoint3 operator*(FPoint3 lhs, const float f) {
// lhs *= f;
// return lhs;
//}
class FMatrix3x3
{
@@ -122,4 +125,5 @@ public:
}
};
}//namespace cura
#endif//INT_POINT_H
+5
Ver Arquivo
@@ -1,6 +1,9 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "gettime.h"
namespace cura
{
TimeKeeper::TimeKeeper()
{
restart();
@@ -12,3 +15,5 @@ double TimeKeeper::restart()
startTime = getTime();
return ret;
}
}//namespace cura
+3
Ver Arquivo
@@ -9,6 +9,8 @@
#include <stddef.h>
#endif
namespace cura
{
static inline double getTime()
{
#ifdef __WIN32
@@ -30,4 +32,5 @@ public:
double restart();
};
}//namespace cura
#endif//GETTIME_H
+45
Ver Arquivo
@@ -15,10 +15,15 @@ Integer points are used to avoid floating point rounding errors, and because Cli
#include <stdint.h>
#include <cmath>
#include <functional> // for hash function obkject
#include <iostream> // auto-serialization / auto-toString()
#define INT2MM(n) (double(n) / 1000.0)
#define INT2MM2(n) (double(n) / 1000000.0)
#define MM2INT(n) (int64_t((n) * 1000))
#define MM2_2INT(n) (int64_t((n) * 1000000))
#define MM3_2INT(n) (int64_t((n) * 1000000000))
#define INT2MICRON(n) ((n) / 1)
#define MICRON2INT(n) ((n) * 1)
@@ -37,6 +42,10 @@ Integer points are used to avoid floating point rounding errors, and because Cli
#define DEPRECATED(func) func
#endif
namespace cura
{
class Point3
{
public:
@@ -117,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;
}
@@ -136,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); }
@@ -143,6 +156,7 @@ INLINE Point operator-(const Point& p0, const Point& p1) { return Point(p0.X-p1.
INLINE Point operator*(const Point& p0, const int32_t i) { return Point(p0.X*i, p0.Y*i); }
INLINE Point operator*(const int32_t i, const Point& p0) { return p0 * i; }
INLINE Point operator/(const Point& p0, const int32_t i) { return Point(p0.X/i, p0.Y/i); }
INLINE Point operator/(const Point& p0, const Point& p1) { return Point(p0.X/p1.X, p0.Y/p1.Y); }
INLINE Point& operator += (Point& p0, const Point& p1) { p0.X += p1.X; p0.Y += p1.Y; return p0; }
INLINE Point& operator -= (Point& p0, const Point& p1) { p0.X -= p1.X; p0.Y -= p1.Y; return p0; }
@@ -204,6 +218,25 @@ INLINE int angle(const Point& p)
return angle;
}
}//namespace cura
namespace std {
template <>
struct hash<cura::Point> {
size_t operator()(const cura::Point & pp) const
{
static int prime = 31;
int result = 89;
result = result * prime + pp.X;
result = result * prime + pp.Y;
return result;
}
};
}
namespace cura
{
class PointMatrix
{
public:
@@ -252,6 +285,11 @@ public:
inline Point3 operator+(const Point3& p3, const Point& p2) {
return Point3(p3.x + p2.X, p3.y + p2.Y, p3.z);
}
inline Point3& operator+=(Point3& p3, const Point& p2) {
p3.x += p2.X;
p3.y += p2.Y;
return p3;
}
inline Point operator+(const Point& p2, const Point3& p3) {
return Point(p3.x + p2.X, p3.y + p2.Y);
@@ -261,9 +299,16 @@ inline Point operator+(const Point& p2, const Point3& p3) {
inline Point3 operator-(const Point3& p3, const Point& p2) {
return Point3(p3.x - p2.X, p3.y - p2.Y, p3.z);
}
inline Point3& operator-=(Point3& p3, const Point& p2) {
p3.x -= p2.X;
p3.y -= p2.Y;
return p3;
}
inline Point operator-(const Point& p2, const Point3& p3) {
return Point(p2.X - p3.x, p2.Y - p3.y);
}
}//namespace cura
#endif//INT_POINT_H
+180
Ver Arquivo
@@ -0,0 +1,180 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_LINEAR_ALG_2D_H
#define UTILS_LINEAR_ALG_2D_H
#include "intpoint.h"
namespace cura
{
class LinearAlg2D
{
public:
static short pointLiesOnTheRightOfLine(Point p, Point p0, Point p1)
{
// no tests unless the segment p0-p1 is at least partly at, or to right of, p.X
if ( std::max(p0.X, p1.X) >= p.X )
{
int64_t pdY = p1.Y-p0.Y;
if (pdY < 0) // p0->p1 is 'falling'
{
if ( p1.Y <= p.Y && p0.Y > p.Y ) // candidate
{
// dx > 0 if intersection is to right of p.X
int64_t dx = (p1.X - p0.X) * (p1.Y - p.Y) - (p1.X-p.X)*pdY;
if (dx == 0) // includes p == p1
{
return 0;
}
if (dx > 0)
{
return 1;
}
}
}
else if (p.Y >= p0.Y)
{
if (p.Y < p1.Y) // candidate for p0->p1 'rising' and includes p.Y
{
// dx > 0 if intersection is to right of p.X
int64_t dx = (p1.X - p0.X) * (p.Y - p0.Y) - (p.X-p0.X)*pdY;
if (dx == 0) // includes p == p0
{
return 0;
}
if (dx > 0)
{
return 1;
}
}
else if (p.Y == p1.Y)
{
// some special cases here, points on border:
// - p1 exactly matches p (might otherwise be missed)
// - p0->p1 exactly horizontal, and includes p.
// (we already tested std::max(p0.X,p1.X) >= p.X )
if (p.X == p1.X ||
(pdY==0 && std::min(p0.X,p1.X) <= p.X) )
{
return 0;
}
}
}
}
return -1;
}
/*!
* Find the point closest to \p from on the line from \p p0 to \p p1
*/
static Point getClosestOnLineSegment(Point from, Point p0, Point p1)
{
Point direction = p1 - p0;
Point toFrom = from-p0;
int64_t projected_x = dot(toFrom, direction) ;
int64_t x_p0 = 0;
int64_t x_p1 = vSize2(direction);
if (x_p1 == 0)
{
// std::cout << "warning! too small segment" << std::endl;
return p0;
}
if (projected_x <= x_p0)
{
return p0;
}
if (projected_x >= x_p1)
{
return p1;
}
else
{
Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction);
return ret ;
}
}
/*!
* 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(const Point& a, const Point& b, const Point& c, char* b_is_beyond_ac = nullptr)
{
/*
* a,
* /|
* / |
* b,/__|, x
* \ |
* \ |
* \|
* 'c
*
* x = b projected on ac
* ax = ab dot ac / vSize(ac)
* xb = ab - ax
* error = vSize(xb)
*/
Point ac = c - a;
int64_t ac_size = vSize(ac);
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;
}
return vSize2(ab);
}
if (ax_size > ac_size)
{// b is 'after' segment ac
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;
return vSize2(bx);
// return vSize2(ab) - ax_size*ax_size; // less accurate
}
};
}//namespace cura
#endif//UTILS_LINEAR_ALG_2D_H
+13 -4
Ver Arquivo
@@ -26,8 +26,17 @@ void logError(const char* fmt, ...)
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stderr);
}
}
void logWarning(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stderr);
}
void logCopyright(const char* fmt, ...)
{
va_list args;
@@ -48,12 +57,12 @@ void log(const char* fmt, ...)
va_end(args);
fflush(stderr);
}
void logProgress(const char* type, int value, int maxValue)
void logProgress(const char* type, int value, int maxValue, float percent)
{
if (!progressLogging)
return;
fprintf(stderr, "Progress:%s:%i:%i\n", type, value, maxValue);
fprintf(stderr, "Progress:%s:%i:%i \t%f%%\n", type, value, maxValue, percent);
fflush(stderr);
}
+4 -2
Ver Arquivo
@@ -9,13 +9,15 @@ void enableProgressLogging();
//Report an error message (always reported, independed of verbose level)
void logError(const char* fmt, ...);
//Report a warning message (always reported, independed of verbose level)
void logWarning(const char* fmt, ...);
//Report a message if the verbose level is 1 or higher. (defined as _log to prevent clash with log() function from <math.h>)
void log(const char* fmt, ...);
//Report an copyright message (always reported, independed of verbose level)
void logCopyright(const char* fmt, ...);
void logCopyright(const char* fmt, ...);
//Report engine progress to interface if any. Only if "enableProgressLogging()" has been called.
void logProgress(const char* type, int value, int maxValue);
void logProgress(const char* type, int value, int maxValue, float percent);
}//namespace cura
+333
Ver Arquivo
@@ -0,0 +1,333 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "polygon.h"
#include "linearAlg2D.h" // pointLiesOnTheRightOfLine
#include "../debug.h"
namespace cura
{
bool PolygonRef::inside(Point p, bool border_result)
{
PolygonRef thiss = *this;
if (size() < 1)
{
return false;
}
int crossings = 0;
Point p0 = back();
for(unsigned int n=0; n<size(); n++)
{
Point p1 = thiss[n];
// no tests unless the segment p0-p1 is at least partly at, or to right of, p.X
short comp = LinearAlg2D::pointLiesOnTheRightOfLine(p, p0, p1);
if (comp == 1)
{
crossings++;
}
else if (comp == 0)
{
return border_result;
}
p0 = p1;
}
return (crossings % 2) == 1;
}
bool Polygons::inside(Point p, bool border_result)
{
Polygons& thiss = *this;
if (size() < 1)
{
return false;
}
int crossings = 0;
for (PolygonRef poly : thiss)
{
Point p0 = poly.back();
for(Point& p1 : poly)
{
short comp = LinearAlg2D::pointLiesOnTheRightOfLine(p, p0, p1);
if (comp == 1)
{
crossings++;
}
else if (comp == 0)
{
return border_result;
}
p0 = p1;
}
}
return (crossings % 2) == 1;
}
unsigned int Polygons::findInside(Point p, bool border_result)
{
Polygons& thiss = *this;
if (size() < 1)
{
return false;
}
int64_t min_x[size()];
std::fill_n(min_x, size(), std::numeric_limits<int64_t>::max()); // initialize with int.max
int crossings[size()];
std::fill_n(crossings, size(), 0); // initialize with zeros
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
{
PolygonRef poly = thiss[poly_idx];
Point p0 = poly.back();
for(Point& p1 : poly)
{
short comp = LinearAlg2D::pointLiesOnTheRightOfLine(p, p0, p1);
if (comp == 1)
{
crossings[poly_idx]++;
int64_t x;
if (p1.Y == p0.Y)
{
x = p0.X;
}
else
{
x = p0.X + (p1.X-p0.X) * (p.Y-p0.Y) / (p1.Y-p0.Y);
}
if (x < min_x[poly_idx])
{
min_x[poly_idx] = x;
}
}
else if (border_result && comp == 0)
{
return poly_idx;
}
p0 = p1;
}
}
int64_t min_x_uneven = std::numeric_limits<int64_t>::max();
unsigned int ret = NO_INDEX;
unsigned int n_unevens = 0;
for (unsigned int array_idx = 0; array_idx < size(); array_idx++)
{
if (crossings[array_idx] % 2 == 1)
{
n_unevens++;
if (min_x[array_idx] < min_x_uneven)
{
min_x_uneven = min_x[array_idx];
ret = array_idx;
}
}
}
if (n_unevens % 2 == 0) { ret = NO_INDEX; }
return ret;
}
void PolygonRef::simplify(int smallest_line_segment_squared, int allowed_error_distance_squared){
PolygonRef& thiss = *this;
if (size() <= 2)
{
clear();
return;
}
{ // remove segments smaller than allowed_error_distance
// this is neccesary in order to avoid the case where a long segment is followed by a lot of small segments would get simplified to a long segment going to the wrong end point
// ....... _ _______
// | / |
// | would become / instead of |
// | / |
Point* last = &thiss.back();
unsigned int writing_idx = 0;
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
{
Point& here = thiss[poly_idx];
if (vSize2(*last - here) < smallest_line_segment_squared)
{
// don't add the point
}
else
{
thiss[writing_idx] = here;
writing_idx++;
last = &here;
}
}
polygon->erase(polygon->begin() + writing_idx , polygon->end());
}
Point* last = &thiss[0];
unsigned int writing_idx = 1;
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
{
Point& here = thiss[poly_idx];
if ( vSize2(here-*last) < allowed_error_distance_squared )
{
// don't add the point to the result
continue;
}
Point& next = thiss[(poly_idx+1) % size()];
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
} else
{
thiss[writing_idx] = here;
writing_idx++;
last = &here;
}
}
polygon->erase(polygon->begin() + writing_idx , polygon->end());
if (size() < 3)
{
clear();
return;
}
{ // handle the line segments spanning the vector end and begin
Point* last = &thiss.back();
Point& here = thiss[0];
if ( vSize2(here-*last) < allowed_error_distance_squared )
{
remove(0);
}
Point& next = thiss[1];
int64_t error2 = LinearAlg2D::getDist2FromLineSegment(*last, here, next);
if (error2 < allowed_error_distance_squared)
{
remove(0);
} else
{
// leave it in
}
}
if (size() < 3)
{
clear();
return;
}
}
std::vector<PolygonsPart> Polygons::splitIntoParts(bool unionAll) const
{
std::vector<PolygonsPart> ret;
ClipperLib::Clipper clipper(clipper_init);
ClipperLib::PolyTree resultPolyTree;
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
splitIntoParts_processPolyTreeNode(&resultPolyTree, ret);
return ret;
}
void Polygons::splitIntoParts_processPolyTreeNode(ClipperLib::PolyNode* node, std::vector<PolygonsPart>& ret) const
{
for(int n=0; n<node->ChildCount(); n++)
{
ClipperLib::PolyNode* child = node->Childs[n];
PolygonsPart part;
part.add(child->Contour);
for(int i=0; i<child->ChildCount(); i++)
{
part.add(child->Childs[i]->Contour);
splitIntoParts_processPolyTreeNode(child->Childs[i], ret);
}
ret.push_back(part);
}
}
unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
{
PartsView& partsView = *this;
for (unsigned int part_idx_now = 0; part_idx_now < partsView.size(); part_idx_now++)
{
std::vector<unsigned int>& partView = partsView[part_idx_now];
if (partView.size() == 0) { continue; }
std::vector<unsigned int>::iterator result = std::find(partView.begin(), partView.end(), poly_idx);
if (result != partView.end())
{
if (boundary_poly_idx) { *boundary_poly_idx = partView[0]; }
return part_idx_now;
}
}
return NO_INDEX;
}
PolygonsPart PartsView::assemblePart(unsigned int part_idx)
{
PartsView& partsView = *this;
PolygonsPart ret;
if (part_idx != NO_INDEX)
{
for (unsigned int poly_idx_ff : partsView[part_idx])
{
ret.add(polygons[poly_idx_ff]);
}
}
return ret;
}
PolygonsPart PartsView::assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
{
PolygonsPart ret;
unsigned int part_idx = getPartContaining(poly_idx, boundary_poly_idx);
if (part_idx != NO_INDEX)
{
return assemblePart(part_idx);
}
return ret;
}
PartsView Polygons::splitIntoPartsView(bool unionAll)
{
Polygons reordered;
PartsView partsView(*this);
ClipperLib::Clipper clipper(clipper_init);
ClipperLib::PolyTree resultPolyTree;
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
splitIntoPartsView_processPolyTreeNode(partsView, reordered, &resultPolyTree);
(*this) = reordered;
return partsView;
}
void Polygons::splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Polygons& reordered, ClipperLib::PolyNode* node)
{
for(int n=0; n<node->ChildCount(); n++)
{
ClipperLib::PolyNode* child = node->Childs[n];
partsView.emplace_back();
unsigned int pos = partsView.size() - 1;
partsView[pos].push_back(reordered.size());
reordered.add(child->Contour);
for(int i = 0; i < child->ChildCount(); i++)
{
partsView[pos].push_back(reordered.size());
reordered.add(child->Childs[i]->Contour);
splitIntoPartsView_processPolyTreeNode(partsView, reordered, child->Childs[i]);
}
}
}
}//namespace cura
+210 -262
Ver Arquivo
@@ -6,8 +6,9 @@
#include <float.h>
#include <clipper/clipper.hpp>
#include <algorithm> // std::reverse
#include <algorithm> // std::reverse, fill_n array
#include <cmath> // fabs
#include <limits> // int64_t.min
#include "intpoint.h"
@@ -29,9 +30,14 @@ enum PolygonType
SupportType,
SkirtType,
InfillType,
SupportInfillType
SupportInfillType,
MoveCombingType,
MoveRetractionType
};
class PartsView;
const static int clipper_init = (0);
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
@@ -45,6 +51,11 @@ public:
PolygonRef(ClipperLib::Path& polygon)
: polygon(&polygon)
{}
PolygonRef(const PolygonRef& other)
{
polygon = other.polygon;
}
unsigned int size() const
{
@@ -67,8 +78,12 @@ public:
polygon->push_back(p);
}
PolygonRef& operator=(const PolygonRef& other) { polygon = other.polygon; return *this; }
ClipperLib::Path& operator*() { return *polygon; }
template <typename... Args>
void emplace_back(Args... args)
void emplace_back(Args&&... args)
{
polygon->emplace_back(args...);
}
@@ -84,6 +99,11 @@ public:
polygon->clear();
}
/*!
* On Y-axis positive upward displays, Orientation will return true if the polygon's orientation is counter-clockwise.
*
* from http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/Orientation.htm
*/
bool orientation() const
{
return ClipperLib::Orientation(*polygon);
@@ -129,7 +149,6 @@ public:
return ret;
}
double area() const
{
return ClipperLib::Area(*polygon);
@@ -202,74 +221,15 @@ public:
* \param border_result What to return when the point is exactly on the border
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
*/
bool inside(Point p, bool border_result=false)
{
PolygonRef thiss = *this;
if (size() < 1)
{
return false;
}
int crossings = 0;
Point p0 = back();
for(unsigned int n=0; n<size(); n++)
{
Point p1 = thiss[n];
// no tests unless the segment p0-p1 is at least partly at, or to right of, p.X
if ( std::max(p0.X, p1.X) >= p.X )
{
int64_t pdY = p1.Y-p0.Y;
if (pdY < 0) // p0->p1 is 'falling'
{
if ( p1.Y <= p.Y && p0.Y > p.Y ) // candidate
{
// dx > 0 if intersection is to right of p.X
int64_t dx = (p1.X - p0.X) * (p1.Y - p.Y) - (p1.X-p.X)*pdY;
if (dx == 0) // includes p == p1
{
return border_result;
}
if (dx > 0)
{
crossings ++;
}
}
}
else if (p.Y >= p0.Y)
{
if (p.Y < p1.Y) // candidate for p0->p1 'rising' and includes p.Y
{
// dx > 0 if intersection is to right of p.X
int64_t dx = (p1.X - p0.X) * (p.Y - p0.Y) - (p.X-p0.X)*pdY;
if (dx == 0) // includes p == p0
{
return border_result;
}
if (dx > 0)
{
crossings ++;
}
}
else if (p.Y == p1.Y)
{
// some special cases here, points on border:
// - p1 exactly matches p (might otherwise be missed)
// - p0->p1 exactly horizontal, and includes p.
// (we already tested std::max(p0.X,p1.X) >= p.X )
if (p.X == p1.X ||
(pdY==0 && std::min(p0.X,p1.X) <= p.X) )
{
return border_result;
// otherwise, count no crossings
}
}
}
}
p0 = p1;
}
return (crossings % 2) == 1;
}
bool inside(Point p, bool border_result = false);
/*!
* Smooth out the polygon and store the result in \p result.
* Smoothing is performed by removing line segments smaller than \p remove_length
*
* \param remove_length The length of the largest segment removed
* \param result (output) The result polygon, assumed to be empty
*/
void smooth(int remove_length, PolygonRef result)
{
PolygonRef& thiss = *this;
@@ -287,68 +247,13 @@ public:
}
}
void simplify(int allowed_error_distance_squared, PolygonRef result) //!< removes consecutive line segments with same orientation
{
PolygonRef& thiss = *this;
ClipperLib::Path* poly = result.polygon;
if (size() < 4)
{
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
poly->push_back(thiss[poly_idx]);
return;
}
Point& last = thiss[0];
result.add(last);
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
{
/*
* /|
* c / | a
* /__|
* \ b|
* e \ | d
* \|
*
* b^2 = c^2 - a^2
* b^2 = e^2 - d^2
*
* approximately: (this is asymptotically true for d -> 0)
* a/d = c/e
* a/(a+d) = c/(c+e)
* a^2 / (a+d)^2 = c^2 / (c+e)^2
* a^2 = c^2 * (a+d)^2/ (c+e)^2
*
*/
if ( vSize2(thiss[poly_idx]-last) < allowed_error_distance_squared )
{
continue;
}
Point& next = thiss[(poly_idx+1) % size()];
auto square = [](double in) { return in*in; };
int64_t a2 = vSize2(next-thiss[poly_idx]) * vSize2(next-last) / static_cast<int64_t>(square(vSizeMM(next-last) + vSizeMM(thiss[poly_idx]-last))*1000*1000);
int64_t error2 = vSize2(next-thiss[poly_idx]) - a2;
if (error2 < allowed_error_distance_squared)
{
// don't add the point to the result
} else
{
poly->push_back(thiss[poly_idx]);
last = thiss[poly_idx];
}
}
if (result.size() < 3)
{
poly->clear();
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
poly->push_back(thiss[poly_idx]);
return;
}
}
/*!
* removes consecutive line segments with same orientation and changes this polygon
*
* \param smallest_line_segment_squared maximal squared length of removed line segments
* \param allowed_error_distance_squared The square of the distance of the middle point to the line segment of the consecutive and previous point for which the middle point is removed
*/
void simplify(int smallest_line_segment_squared = 100, int allowed_error_distance_squared = 25);
void pop_back()
{
@@ -400,9 +305,11 @@ public:
}
};
class PolygonsPart;
class Polygons
{
private:
protected:
ClipperLib::Paths polygons;
public:
unsigned int size() const
@@ -452,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
@@ -472,6 +380,13 @@ public:
clipper.Execute(ClipperLib::ctUnion, ret.polygons, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
return ret;
}
/*!
* Union all polygons with each other (When polygons.add(polygon) has been called for overlapping polygons)
*/
Polygons unionPolygons() const
{
return unionPolygons(Polygons());
}
Polygons intersection(const Polygons& other) const
{
Polygons ret;
@@ -501,6 +416,63 @@ public:
return ret;
}
Polygons offsetPolyLine(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter) const
{
Polygons ret;
double miterLimit = 1.2;
ClipperLib::ClipperOffset clipper(miterLimit, 10.0);
clipper.AddPaths(polygons, joinType, ClipperLib::etOpenSquare);
clipper.MiterLimit = miterLimit;
clipper.Execute(ret.polygons, distance);
return ret;
}
/*!
* Check if we are inside the polygon. We do this by tracing from the point towards the positive X direction,
* every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
* Care needs to be taken, if p.Y exactly matches a vertex to the right of p, then we need to count 1 intersect if the
* outline passes vertically past; and 0 (or 2) intersections if that point on the outline is a 'top' or 'bottom' vertex.
* The easiest way to do this is to break out two cases for increasing and decreasing Y ( from p0 to p1 ).
* A segment is tested if pa.Y <= p.Y < pb.Y, where pa and pb are the points (from p0,p1) with smallest & largest Y.
* When both have the same Y, no intersections are counted but there is a special test to see if the point falls
* exactly on the line.
*
* Returns false if outside, true if inside; if the point lies exactly on the border, will return \p border_result.
*
* \param p The point for which to check if it is inside this polygon
* \param border_result What to return when the point is exactly on the border
* \return Whether the point \p p is inside this polygon (or \p border_result when it is on the border)
*/
bool inside(Point p, bool border_result = false);
/*!
* Find the polygon inside which point \p p resides.
*
* We do this by tracing from the point towards the positive X direction,
* every line we cross increments the crossings counter. If we have an even number of crossings then we are not inside the polygon.
* We then find the polygon with an uneven number of crossings which is closest to \p p.
*
* If \p border_result, we return the first polygon which is exactly on \p p.
*
* \param p The point for which to check in which polygon it is.
* \param border_result Whether a point exactly on a polygon counts as inside
* \return The index of the polygon inside which the point \p p resides
*/
unsigned int findInside(Point p, bool border_result = false);
/*!
* Approximates the convex hull of the polygons.
* \p extra_outset Extra offset outward
* \return the convex hull (approximately)
*
*/
Polygons convexHull(int extra_outset = 0)
{
int overshoot = 100000; // 10 cm (hardcoded value)
return offset(overshoot, ClipperLib::jtRound).offset(-overshoot+extra_outset, ClipperLib::jtRound);
}
Polygons smooth(int remove_length, int min_area) //!< removes points connected to small lines
{
Polygons ret;
@@ -525,35 +497,46 @@ public:
return ret;
}
Polygons simplify(int allowed_error_distance) //!< removes points connected to similarly oriented lines
/*!
* removes points connected to similarly oriented lines
*
* \param smallest_line_segment_squared maximal squared length of removed line segments
* \param allowed_error_distance_squared The square of the distance of the middle point to the line segment of the consecutive and previous point for which the middle point is removed
*/
void simplify(int smallest_line_segment = 10, int allowed_error_distance = 5)
{
int allowed_error_distance_squared = allowed_error_distance * allowed_error_distance;
Polygons ret;
int smallest_line_segment_squared = smallest_line_segment * smallest_line_segment;
Polygons& thiss = *this;
for (unsigned int p = 0; p < size(); p++)
{
thiss[p].simplify(allowed_error_distance_squared, ret.newPoly());
thiss[p].simplify(smallest_line_segment_squared, allowed_error_distance_squared);
if (thiss[p].size() < 3)
{
remove(p);
p--;
}
}
return ret;
}
/*!
* Split up the polygons into groups according to the even-odd rule.
* Each polygons in the result has an outline as first polygon, whereas the rest are holes.
* Each PolygonsPart in the result has an outline as first polygon, whereas the rest are holes.
*/
std::vector<Polygons> splitIntoParts(bool unionAll = false) const
{
std::vector<Polygons> ret;
ClipperLib::Clipper clipper(clipper_init);
ClipperLib::PolyTree resultPolyTree;
clipper.AddPaths(polygons, ClipperLib::ptSubject, true);
if (unionAll)
clipper.Execute(ClipperLib::ctUnion, resultPolyTree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
else
clipper.Execute(ClipperLib::ctUnion, resultPolyTree);
_processPolyTreeNode(&resultPolyTree, ret);
return ret;
}
std::vector<PolygonsPart> splitIntoParts(bool unionAll = false) const;
private:
void splitIntoParts_processPolyTreeNode(ClipperLib::PolyNode* node, std::vector<PolygonsPart>& ret) const;
public:
/*!
* Split up the polygons into groups according to the even-odd rule.
* Each vector in the result has the index to an outline as first index, whereas the rest are indices to holes.
*
* \warning Note that this function reorders the polygons!
*/
PartsView splitIntoPartsView(bool unionAll = false);
private:
void splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Polygons& reordered, ClipperLib::PolyNode* node);
public:
/*!
* Removes polygons with area smaller than \p minAreaSize (note that minAreaSize is in mm^2, not in micron^2).
*/
@@ -573,11 +556,12 @@ public:
/*!
* Removes overlapping consecutive line segments which don't delimit a positive area.
*/
Polygons removeDegenerateVerts()
void removeDegenerateVerts()
{
Polygons ret;
for (PolygonRef poly : *this)
Polygons& thiss = *this;
for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++)
{
PolygonRef poly = thiss[poly_idx];
Polygon result;
auto isDegenerate = [](Point& last, Point& now, Point& next)
@@ -586,7 +570,7 @@ public:
Point next_line = next - now;
return dot(last_line, next_line) == -1 * vSize(last_line) * vSize(next_line);
};
bool isChanged = false;
for (unsigned int idx = 0; idx < poly.size(); idx++)
{
Point& last = (result.size() == 0) ? poly.back() : result.back();
@@ -595,6 +579,7 @@ public:
if ( isDegenerate(last, poly[idx], next) )
{ // lines are in the opposite direction
// don't add vert to the result
isChanged = true;
while (result.size() > 1 && isDegenerate(result[result.size()-2], result.back(), next) )
{
result.pop_back();
@@ -606,10 +591,19 @@ public:
}
}
if (result.size() > 2) { ret.add(result); }
if (isChanged)
{
if (result.size() > 2)
{
*poly = *result;
}
else
{
thiss.remove(poly_idx);
poly_idx--; // effectively the next iteration has the same poly_idx (referring to a new poly which is not yet processed)
}
}
}
return ret;
}
/*!
* Removes the same polygons from this set (and also empty polygons).
@@ -666,24 +660,7 @@ public:
}
return result;
}
private:
void _processPolyTreeNode(ClipperLib::PolyNode* node, std::vector<Polygons>& ret) const
{
for(int n=0; n<node->ChildCount(); n++)
{
ClipperLib::PolyNode* child = node->Childs[n];
Polygons polygons;
polygons.add(child->Contour);
for(int i=0; i<child->ChildCount(); i++)
{
polygons.add(child->Childs[i]->Contour);
_processPolyTreeNode(child->Childs[i], ret);
}
ret.push_back(polygons);
}
}
public:
Polygons processEvenOdd() const
{
Polygons ret;
@@ -737,6 +714,32 @@ public:
return ret;
}
void applyMatrix(const PointMatrix& matrix)
{
for(unsigned int i=0; i<polygons.size(); i++)
{
for(unsigned int j=0; j<polygons[i].size(); j++)
{
polygons[i][j] = matrix.apply(polygons[i][j]);
}
}
}
};
/*!
* A single area with holes. The first polygon is the outline, while the rest are holes within this outline.
*
* This class has little more functionality than Polygons, but serves to show that a specific instance is ordered such that the first Polygon is the outline and the rest are holes.
*/
class PolygonsPart : public Polygons
{
public:
PolygonRef outerPolygon()
{
Polygons& thiss = *this;
return thiss[0];
}
bool inside(Point p)
{
if (size() < 1)
@@ -750,94 +753,39 @@ public:
}
return true;
}
void applyMatrix(const PointMatrix& matrix)
{
for(unsigned int i=0; i<polygons.size(); i++)
{
for(unsigned int j=0; j<polygons[i].size(); j++)
{
polygons[i][j] = matrix.apply(polygons[i][j]);
}
}
}
void debugOutputHTML(const char* filename, bool dotTheVertices = false)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point modelSize = max() - min();
modelSize.X = std::max(modelSize.X, modelSize.Y);
modelSize.Y = std::max(modelSize.X, modelSize.Y);
Point modelMin = min();
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
for(Polygons& parts : splitIntoParts())
{
for(unsigned int j=0;j<parts.size();j++)
{
Polygon poly = parts[j];
fprintf(out, "<polygon points=\"");
for(Point& p : poly)
{
fprintf(out, "%f,%f ", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
}
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
if (dotTheVertices)
for(Point& p : poly)
fprintf(out, "<circle cx=\"%f\" cy=\"%f\" r=\"2\" stroke=\"black\" stroke-width=\"3\" fill=\"black\" />", float(p.X - modelMin.X)/modelSize.X*500, float(p.Y - modelMin.Y)/modelSize.Y*500);
}
}
fprintf(out, "</svg>\n");
fprintf(out, "</body></html>");
fclose(out);
}
};
/* Axis aligned boundary box */
class AABB
/*!
* Extension of vector<vector<unsigned int>> which is similar to a vector of PolygonParts, except the base of the container is indices to polygons into the original Polygons, instead of the polygons themselves
*/
class PartsView : public std::vector<std::vector<unsigned int>>
{
public:
Point min, max;
AABB()
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
{
}
AABB(Polygons polys)
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
{
calculate(polys);
}
void calculate(Polygons polys)
{
min = Point(POINT_MAX, POINT_MAX);
max = Point(POINT_MIN, POINT_MIN);
for(unsigned int i=0; i<polys.size(); i++)
{
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;
}
}
}
bool hit(const AABB& other) const
{
if (max.X < other.min.X) return false;
if (min.X > other.max.X) return false;
if (max.Y < other.min.Y) return false;
if (min.Y > other.max.Y) return false;
return true;
}
Polygons& polygons;
PartsView(Polygons& polygons) : polygons(polygons) { }
/*!
* Get the index of the PolygonsPart of which the polygon with index \p poly_idx is part.
*
* \param poly_idx The index of the polygon in \p polygons
* \param boundary_poly_idx Optional output parameter: The index of the boundary polygon of the part in \p polygons
* \return The PolygonsPart containing the polygon with index \p poly_idx
*/
unsigned int getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr);
/*!
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
*
* \param poly_idx The index of the polygon in \p polygons
* \param boundary_poly_idx Optional output parameter: The index of the boundary polygon of the part in \p polygons
* \return The PolygonsPart containing the polygon with index \p poly_idx
*/
PolygonsPart assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr);
/*!
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
*
* \param part_idx The index of the part
* \return The PolygonsPart with index \p poly_idx
*/
PolygonsPart assemblePart(unsigned int part_idx);
};
}//namespace cura
+230 -87
Ver Arquivo
@@ -1,52 +1,198 @@
/** Copyright (C) 2015 Tim Kuipers - Released under terms of the AGPLv3 License */
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "polygonUtils.h"
#include <list>
#include "linearAlg2D.h"
#include "../debug.h"
namespace cura
{
void offsetExtrusionWidth(Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
int64_t offset_safe_allowance = 20; // make all offset safe operations a bit less safe to allow for small variations in walls which are supposed to be exactly x perimeters thick
int64_t in_between_min_dist_half = 10;
void PolygonUtils::offsetExtrusionWidth(const Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool removeOverlappingPerimeters)
{
int direction = (inward)? -1 : 1;
int distance = (inward)? -extrusionWidth : extrusionWidth;
if (!avoidOverlappingPerimeters)
if (!removeOverlappingPerimeters)
{
result = poly.offset(distance);
return;
}
else
{
result = poly.offset(distance*3/2).offset(-distance/2); // overshoot by half the extrusionWidth
result = poly.offset(distance*3/2 - direction*offset_safe_allowance).offset(-distance/2 + direction*offset_safe_allowance); // overshoot by half the extrusionWidth
if (in_between) // if a pointer for in_between is given
in_between->add(poly.offset(distance/2).difference(result.offset(-distance/2)));
in_between->add(poly.offset(distance/2 + direction*in_between_min_dist_half).difference(result.offset(-distance/2 - direction*in_between_min_dist_half)));
}
}
void offsetSafe(Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool avoidOverlappingPerimeters)
void PolygonUtils::offsetSafe(const Polygons& poly, int distance, int offset_first_boundary, int extrusion_width, Polygons& result, Polygons* in_between, bool removeOverlappingPerimeters)
{
int direction = (distance > 0)? 1 : -1;
if (!avoidOverlappingPerimeters)
if (!removeOverlappingPerimeters)
{
result = poly.offset(distance);
return;
}
else
{
result = poly.offset(distance + direction*extrusionWidth/2).offset(-direction * extrusionWidth/2);
result = poly.offset(distance + direction*extrusion_width / 2 - direction*offset_safe_allowance).offset(-direction*extrusion_width/2 + direction*offset_safe_allowance); // overshoot by half the extrusionWidth
if (in_between) // if a pointer for in_between is given
in_between->add(poly.offset(offset_first_boundary + direction*in_between_min_dist_half).difference(result.offset(-direction * extrusion_width/2 - direction*in_between_min_dist_half)));
}
}
void removeOverlapping(Polygons& poly, int extrusionWidth, Polygons& result)
void PolygonUtils::offsetSafe(const Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool removeOverlappingPerimeters)
{
int direction = (distance > 0)? 1 : -1;
if (!removeOverlappingPerimeters)
{
result = poly.offset(distance);
return;
}
else
{
result = poly.offset(distance + direction*extrusionWidth/2 - direction*offset_safe_allowance).offset(-direction * extrusionWidth/2 + direction*offset_safe_allowance);
}
}
void PolygonUtils::removeOverlapping(const Polygons& poly, int extrusionWidth, Polygons& result)
{
result = poly.offset(extrusionWidth/2).offset(-extrusionWidth).offset(extrusionWidth/2);
}
Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int point_idx, int64_t offset)
{
Point p0 = poly[(point_idx > 0) ? (point_idx - 1) : (poly.size() - 1)];
Point p1 = poly[point_idx];
Point p2 = poly[(point_idx < (poly.size() - 1)) ? (point_idx + 1) : 0];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0))); // 1.0 for some precision
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0))); // 1.0 for some precision
Point n = normal(off0 + off1, -offset);
return p1 + n;
}
/*
* Implementation assumes moving inside, but moving outside should just as well be possible.
*/
unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int distance, int64_t maxDist2)
{
Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
unsigned int bestPoly = NO_INDEX;
bool is_inside = false;
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
PolygonRef poly = polygons[poly_idx];
if (poly.size() < 2)
continue;
Point p0 = poly[poly.size()-2];
Point p1 = poly.back();
bool projected_p_beyond_prev_segment = dot(p1 - p0, from - p0) > vSize2(p1 - p0);
for(Point& p2 : poly)
{
// X = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
// X = P projected on AB
Point& a = p1;
Point& b = p2;
Point& p = from;
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 (projected_p_beyond_prev_segment)
{ // case which looks like: > .
projected_p_beyond_prev_segment = false;
Point& x = p1;
int64_t dist2 = vSize2(x - p);
if (dist2 < bestDist2)
{
bestDist2 = dist2;
bestPoly = poly_idx;
if (distance == 0) { ret = x; }
else
{
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;
}
}
}
else
{
projected_p_beyond_prev_segment = false;
p0 = p1;
p1 = p2;
continue;
}
}
else if (ax_length >= ab_length) // x is projected to beyond ab
{
projected_p_beyond_prev_segment = true;
p0 = p1;
p1 = p2;
continue;
}
else
{
projected_p_beyond_prev_segment = false;
Point x = a + ab * ax_length / ab_length;
int64_t dist2 = vSize2(p - x);
if (dist2 < bestDist2)
{
bestDist2 = dist2;
bestPoly = poly_idx;
if (distance == 0) { ret = x; }
else
{
Point inward_dir = crossZ(normal(ab, distance));
ret = x + inward_dir;
is_inside = dot(inward_dir, p - x) >= 0;
}
}
}
p0 = p1;
p1 = p2;
}
}
if (is_inside)
{
if (bestDist2 < distance * distance)
{
from = ret;
}
else
{
// from = from; // original point stays unaltered. It is already inside by enough distance
}
return bestPoly;
}
else if (bestDist2 < maxDist2)
{
from = ret;
return bestPoly;
}
return NO_INDEX;
}
void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
{
PolygonRef poly1 = poly1_result.poly;
PolygonRef poly2 = poly2_result.poly;
@@ -76,40 +222,7 @@ void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoi
walkToNearestSmallestConnection(poly1_result, poly2_result);
}
void findSmallestConnection_OLD(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
{
PolygonRef poly1 = poly1_result.poly;
PolygonRef poly2 = poly2_result.poly;
if (poly1.size() == 0 || poly2.size() == 0)
{
return;
}
int bestDist2 = -1;
int step1 = std::max<unsigned int>(2, poly1.size() / sample_size);
int step2 = std::max<unsigned int>(2, poly2.size() / sample_size);
for (unsigned int i = 0; i < poly1.size(); i += step1)
{
for (unsigned int j = 0; j < poly2.size(); j += step2)
{
ClosestPolygonPoint here1(i, poly1);
ClosestPolygonPoint here2(j, poly2);
walkToNearestSmallestConnection(here1, here2);
int dist2 = vSize2(here1.location - here2.location);
if (bestDist2 == -1 || dist2 < bestDist2)
{
bestDist2 = dist2;
poly1_result = here1;
poly2_result = here2;
}
}
}
}
void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
{
PolygonRef poly1 = poly1_result.poly;
PolygonRef poly2 = poly2_result.poly;
@@ -133,7 +246,7 @@ void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestP
}
}
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx)
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx)
{
ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1);
ClosestPolygonPoint back = findNearestClosest(from, polygon, start_idx, -1);
@@ -147,7 +260,7 @@ ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start
}
}
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction)
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction)
{
if (polygon.size() == 0)
{
@@ -166,7 +279,7 @@ ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start
Point& p1 = polygon[p1_idx];
Point& p2 = polygon[p2_idx];
Point closestHere = getClosestOnLine(from, p1 ,p2);
Point closestHere = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
int64_t dist = vSize2(from - closestHere);
if (dist < closestDist)
{
@@ -183,7 +296,7 @@ ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start
return ClosestPolygonPoint(best, bestPos, polygon);
}
ClosestPolygonPoint findClosest(Point from, Polygons& polygons)
ClosestPolygonPoint PolygonUtils::findClosest(Point from, Polygons& polygons)
{
Polygon emptyPoly;
@@ -215,7 +328,7 @@ ClosestPolygonPoint findClosest(Point from, Polygons& polygons)
return best;
}
ClosestPolygonPoint findClosest(Point from, PolygonRef polygon)
ClosestPolygonPoint PolygonUtils::findClosest(Point from, PolygonRef polygon)
{
if (polygon.size() == 0)
{
@@ -235,7 +348,7 @@ ClosestPolygonPoint findClosest(Point from, PolygonRef polygon)
if (p2_idx >= polygon.size()) p2_idx = 0;
Point& p2 = polygon[p2_idx];
Point closestHere = getClosestOnLine(from, p1 ,p2);
Point closestHere = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
int64_t dist = vSize2(from - closestHere);
if (dist < closestDist)
{
@@ -249,35 +362,6 @@ ClosestPolygonPoint findClosest(Point from, PolygonRef polygon)
}
Point getClosestOnLine(Point from, Point p0, Point p1)
{
Point direction = p1 - p0;
Point toFrom = from-p0;
int64_t projected_x = dot(toFrom, direction) ;
int64_t x_p0 = 0;
int64_t x_p1 = vSize2(direction);
if (projected_x <= x_p0)
{
return p0;
}
if (projected_x >= x_p1)
{
return p1;
}
else
{
if (vSize2(direction) == 0)
{
std::cout << "warning! too small segment" << std::endl;
return p0;
}
Point ret = p0 + projected_x / vSize(direction) * direction / vSize(direction);
return ret ;
}
}
@@ -287,12 +371,7 @@ Point getClosestOnLine(Point from, Point p0, Point p1)
bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
{
Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()];
@@ -369,4 +448,68 @@ bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, i
bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
{
Point p0 = transformation_matrix.apply(poly.back());
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))
{
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)
return true;
}
p0 = p1;
}
return false;
}
bool PolygonUtils::polygonCollidesWithlineSegment(PolygonRef poly, Point& startPoint, Point& endPoint)
{
Point diff = endPoint - startPoint;
PointMatrix transformation_matrix = PointMatrix(diff);
Point transformed_startPoint = transformation_matrix.apply(startPoint);
Point transformed_endPoint = transformation_matrix.apply(endPoint);
return PolygonUtils::polygonCollidesWithlineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix);
}
bool PolygonUtils::polygonCollidesWithlineSegment(Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
{
for(PolygonRef poly : polys)
{
if (poly.size() == 0) { continue; }
if (PolygonUtils::polygonCollidesWithlineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix))
{
return true;
}
}
return false;
}
bool PolygonUtils::polygonCollidesWithlineSegment(Polygons& polys, Point& startPoint, Point& endPoint)
{
Point diff = endPoint - startPoint;
PointMatrix transformation_matrix = PointMatrix(diff);
Point transformed_startPoint = transformation_matrix.apply(startPoint);
Point transformed_endPoint = transformation_matrix.apply(endPoint);
return polygonCollidesWithlineSegment(polys, transformed_startPoint, transformed_endPoint, transformation_matrix);
}
}//namespace cura
+184 -73
Ver Arquivo
@@ -1,20 +1,11 @@
/** Copyright (C) 2015 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef POLYGON_UTILS_H
#define POLYGON_UTILS_H
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_POLYGON_UTILS_H
#define UTILS_POLYGON_UTILS_H
#include "polygon.h"
namespace cura
{
//! performs an offset compared to an adjacent inset/outset and also computes the area created by gaps between the two consecutive insets/outsets
void offsetExtrusionWidth(Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters);
//! performs an offset and makes sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
void offsetSafe(Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool avoidOverlappingPerimeters);
//! performs offsets to make sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
void removeOverlapping(Polygons& poly, int extrusionWidth, Polygons& result);
/*!
* Result of finding the closest point to a given within a set of polygons, with extra information on where the point is.
@@ -37,69 +28,189 @@ struct GivenDistPoint
Point location; //!< Result location
int pos; //!< Index to the first point in the polygon of the line segment on which the result was found
};
/*!
* Find the two points in two polygons with the smallest distance.
*
* \warning The ClosestPolygonPoint::poly fields output parameters should be initialized with the polygons for which to find the smallest connection.
*
* \param poly1_result Output parameter: the point at the one end of the smallest connection between its poly and \p poly2_result.poly.
* \param poly2_result Output parameter: the point at the other end of the smallest connection between its poly and \p poly1_result.poly.
* \param sample_size The number of points on each polygon to start the hill climbing search from.
*/
void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size);
/*!
*
* \warning Assumes \p poly1_result and \p poly2_result have their pos and poly fields initialized!
*/
void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result);
/*!
* Find the nearest closest point on a polygon from a given index.
*
* \param from The point from which to get the smallest distance.
* \param polygon The polygon on which to find the point with the smallest distance.
* \param start_idx The index of the point in the polygon from which to start looking.
* \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from.
*/
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx);
/*!
* Find the nearest closest point on a polygon from a given index walking in one direction along the polygon.
*
* \param from The point from which to get the smallest distance.
* \param polygon The polygon on which to find the point with the smallest distance.
* \param start_idx The index of the point in the polygon from which to start looking.
* \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction
* \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from.
*/
ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction);
/*!
* Find the point closest to \p from in all polygons in \p polygons.
*/
ClosestPolygonPoint findClosest(Point from, Polygons& polygons);
/*!
* Find the point closest to \p from in the polygon \p polygon.
*/
ClosestPolygonPoint findClosest(Point from, PolygonRef polygon);
/*!
* Find the point closest to \p from on the line from \p p0 to \p p1
*/
Point getClosestOnLine(Point from, Point p0, Point p1);
class PolygonUtils
{
public:
//! performs an offset compared to an adjacent inset/outset and also computes the area created by gaps between the two consecutive insets/outsets
static void offsetExtrusionWidth(const Polygons& poly, bool inward, int extrusionWidth, Polygons& result, Polygons* in_between, bool removeOverlappingPerimeters);
/*!
* Find the next point (going along the direction of the polygon) with a distance \p dist from the point \p from within the \p poly.
* Returns whether another point could be found within the \p poly which can be found before encountering the point at index \p start_idx.
* The point \p from and the polygon \p poly are assumed to lie on the same plane.
*
* \param start_idx the index of the prev poly point on the poly.
* \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result.
*/
bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
/*!
* performs an offset compared to an adjacent inset/outset and also computes the area created by gaps between the two consecutive insets/outsets.
* This function allows for different extrusion widths between the two insets.
*/
static void offsetSafe(const Polygons& poly, int distance, int offset_first_boundary, int extrusion_width, Polygons& result, Polygons* in_between, bool removeOverlappingPerimeters);
//! performs an offset and makes sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
static void offsetSafe(const Polygons& poly, int distance, int extrusionWidth, Polygons& result, bool removeOverlappingPerimeters);
//! performs offsets to make sure the lines don't overlap (ignores any area between the original poly and the resulting poly)
static void removeOverlapping(const Polygons& poly, int extrusionWidth, Polygons& result);
/*!
* Get a point from the \p poly with a given \p offset.
*
* \param poly The polygon.
* \param point_idx The index of the point in the polygon.
* \param offset The distance the point has to be moved outward from the polygon.
* \return A point at the given distance inward from the point on the boundary polygon.
*/
static Point getBoundaryPointWithOffset(PolygonRef poly, unsigned int point_idx, int64_t offset);
/*!
* Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within \p distance.
* Given a \p distance more than zero, the point will end up inside, and conversely outside.
* When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned.
* When the point is in/outside by less than \p distance, \p from is moved to the correct place.
*
* \param polygons The polygons onto which to move the point
* \param from The point to move.
* \param distance The distance by which to move the point.
* \param maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
* \return The index to the polygon onto which we have moved the point.
*/
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max());
/*!
* Find the two points in two polygons with the smallest distance.
*
* Note: The amount of preliminary distance checks is quadratic in \p sample_size : `O(sample_size ^2)`.
* Further convergence time depends on polygon size and shape.
*
* \warning The ClosestPolygonPoint::poly fields output parameters should be initialized with the polygons for which to find the smallest connection.
*
* \param poly1_result Output parameter: the point at the one end of the smallest connection between its poly and \p poly2_result.poly.
* \param poly2_result Output parameter: the point at the other end of the smallest connection between its poly and \p poly1_result.poly.
* \param sample_size The number of points on each polygon to start the hill climbing search from.
*/
static void findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size);
/*!
*
* \warning Assumes \p poly1_result and \p poly2_result have their pos and poly fields initialized!
*/
static void walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result);
/*!
* Find the nearest closest point on a polygon from a given index.
*
* \param from The point from which to get the smallest distance.
* \param polygon The polygon on which to find the point with the smallest distance.
* \param start_idx The index of the point in the polygon from which to start looking.
* \return The nearest point from \p start_idx going along the \p polygon (in both directions) with a locally minimal distance to \p from.
*/
static ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx);
/*!
* Find the nearest closest point on a polygon from a given index walking in one direction along the polygon.
*
* \param from The point from which to get the smallest distance.
* \param polygon The polygon on which to find the point with the smallest distance.
* \param start_idx The index of the point in the polygon from which to start looking.
* \param direction The direction to walk: 1 for walking along the \p polygon, -1 for walking in opposite direction
* \return The nearest point from \p start_idx going along the \p polygon with a locally minimal distance to \p from.
*/
static ClosestPolygonPoint findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction);
/*!
* Find the point closest to \p from in all polygons in \p polygons.
*/
static ClosestPolygonPoint findClosest(Point from, Polygons& polygons);
/*!
* Find the point closest to \p from in the polygon \p polygon.
*/
static ClosestPolygonPoint findClosest(Point from, PolygonRef polygon);
/*!
* Find the next point (going along the direction of the polygon) with a distance \p dist from the point \p from within the \p poly.
* Returns whether another point could be found within the \p poly which can be found before encountering the point at index \p start_idx.
* The point \p from and the polygon \p poly are assumed to lie on the same plane.
*
* \param from The point from whitch to find a point on the polygon satisfying the conditions
* \param start_idx the index of the prev poly point on the poly.
* \param poly_start_idx The index of the point in the polygon which is to be handled as the start of the polygon. No point further than this point will be the result.
*/
static bool getNextPointWithDistance(Point from, int64_t dist, const PolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
/*!
* 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).
*
* 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.
*
* 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).
*
* 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);
};
}//namespace cura

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