Comparar commits

...

480 Commits

Autor SHA1 Mensagem Data
Tim Kuipers 5a48d86bd7 refactor: handle layer plans outside of buffer (CURA-3339)
allocation is handled outside buffer
writing gcode is handled outside buffer
buffer processing is handled separately from adding to the buffer
2017-02-17 11:44:53 +01:00
Tim Kuipers 79cb7850e9 refactor: simplified processLayer calls (CURA-3339) 2017-02-17 11:44:53 +01:00
Tim Kuipers 9e4a786f3d refactor: clean up GCodePathConfig (CURA-3339)
have one constructor rather than a constructor, init, setLayerHeight and smoothSpeed
2017-02-17 11:44:53 +01:00
Tim Kuipers fe49157ca8 refactor: PathConfigs ==> PathConfigStorage (CURA-3339)
because the structure follows the structure of SliceDataStorage.
2017-02-17 11:44:53 +01:00
Tim Kuipers 889c2e4b13 fix: GCodePathConfig extrusion_per_mm was calculated too early (CURA-3339)
this resulted in all extrusion lines being replaced by travel paths.
This problem was introduced a couple of commits ago.
The underlying problem, that there needs to be a definite order between init and setLayerHeight, will be solved in a commit soon to come.
2017-02-17 11:44:53 +01:00
Tim Kuipers ca3848f79a fix: const correctness (except some const_cast instances!) (CURA-3339) 2017-02-17 11:44:53 +01:00
Tim Kuipers 77f1aebef9 fix: bundle state from gcodePlanner into class (CURA-3339)
this helps in making GCodePlanner multithreaded
The state of the gcodePlanner which is carried over to the next layer is bundled in a nice package, which is to be dealt with soon in a coming commit
2017-02-17 11:44:53 +01:00
Tim Kuipers 046c34b31b lil gitignore 2017-02-17 11:44:53 +01:00
Tim Kuipers 066a0d7318 fix: move writeGcode configs out of sliceDataStorage (CURA-3339)
This contributes to making sliceDataStorage const after FffPolygonGenerator has been called
2017-02-17 11:44:53 +01:00
Tim Kuipers 989317a293 fix: const correctness in combing (CURA-3339) 2017-02-17 11:43:45 +01:00
Tim Kuipers cf3d18eb75 fix: const correctness in prime tower (CURA-3339) 2017-02-17 11:43:45 +01:00
Tim Kuipers c9096424c1 fix: lil const correctnes MergeInfillLines (CURA-3339) 2017-02-17 11:43:45 +01:00
Tim Kuipers d489143fcb fix: const correctness in polygon (CURA-3339) 2017-02-17 11:43:45 +01:00
Tim Kuipers 8ac3b49549 refactor: introduced ConstPolygonRef (CURA-3339)
Adapted from 9712301aa8
From @Johan3DV 'Improve const correctness and remove const_cast'
2017-02-17 11:43:45 +01:00
Tim Kuipers bdb788e3c4 don't generate towers when support_use_towers is disabled (CURA-3288) 2017-02-15 12:48:46 +01:00
Tim Kuipers 5b00c21a5f Merge branch 'feature_omp_settings' 2017-02-15 12:19:21 +01:00
Tim Kuipers b546f66bbe Merge branch 'feature_omp_skin_infill' 2017-02-15 12:16:37 +01:00
Tim Kuipers 2af8320cc9 Merge branch 'feature_omp_slicing' 2017-02-15 12:16:11 +01:00
Tim Kuipers 5fa5c89935 fix: const correctness of getSetting functions (CURA-3372) 2017-02-15 12:05:14 +01:00
Tim Kuipers 5c40f74c68 feat: small optimization in setting value retrieval (CURA-3372) 2017-02-15 12:03:11 +01:00
Tim Kuipers 60cebc86eb fix: error on unknown unset setting, rather than warn (CURA-3372)
this als oremoves a const_cast :)
2017-02-15 12:02:36 +01:00
Tim Kuipers 1bb4e03b36 fix: remove superfluous const_cast (CURA-3372) 2017-02-15 11:46:01 +01:00
Tim Kuipers 677857a186 fix: lil (CURA-3331) 2017-02-09 21:13:26 +01:00
Tim Kuipers 156674e452 fix: bs include (CURA-3331) 2017-02-09 17:44:23 +01:00
Tim Kuipers 903b31d381 Merge branch 'feature_omp_skin_infill' of github.com:Ultimaker/CuraEngine into feature_omp_skin_infill 2017-02-09 17:41:49 +01:00
Tim Kuipers 734978230f lil (CURA-3330) 2017-02-09 17:41:45 +01:00
Tim Kuipers fa02006a5d fix: logging of OpenMP threads in main (CURA-3330) 2017-02-09 15:30:45 +01:00
Ghostkeeper 6861020c6b Remove irrelevant documentation of different function
This documentation of processInsets reports on one of the generateInsets functions. Probably a copy-paste mistake.

Contributes to issue CURA-3330.
2017-02-08 16:43:13 +01:00
Ghostkeeper 2d9a3c83ef Code style: Space after for keyword
Contributes to issue CURA-3330.
2017-02-08 16:29:02 +01:00
Ghostkeeper ab7e035f6e Don't continue with compiling when function has error
This would give an exception which breaks the script and makes a very unclear chained error message.

Contributes to issue CURA-2572.
2017-02-03 17:05:28 +01:00
Ghostkeeper d0a149cf26 Fix error handling when setting has a parse error
The code was an integer. It needs to be converted to a string in order to be added to other strings.

Contributes to issue CURA-2572.
2017-02-03 17:05:28 +01:00
Tim Kuipers 4d35735aa8 Merge pull request #451 from smartavionics/mb-spiralize-bug
Fix spiralize args bug - untested but this just looked wrong to me.
2017-02-02 13:12:49 +01:00
Mark Burton 5e831f99e9 Fix spiralize args bug - untested but this just looked wrong to me. 2017-02-02 08:08:50 +00:00
Tim Kuipers 04766f663b Add OpenMP parallel execution createLayerParts (CURA-3331) 2017-02-01 13:26:51 +01:00
Tim Kuipers 42684e6368 feat: Add OpenMP parallel execution of slice make polygons (CURA-3331) 2017-01-31 11:41:34 +01:00
Ghostkeeper aa14682087 Remove 'l' formatting character and simplify+speed-up int2mm formatting
The 'l' formatting character in combination with 'd' was not allowed with MinGW on Windows. Simply formatting with 'd' is sufficient since our coordinates are allowed to be cast to integers at this stage: We won't need to square them any more or anything.

Contributes to issue CURA-3274.
2017-01-31 10:35:28 +01:00
Tim Kuipers 4c5a959c4e fix: handle multithreaded progress messages (CURA-781) 2017-01-30 17:41:33 +01:00
Johan Kristensen 21746022c4 Add OpenMP parallel execution of processInsets
CURA-541
2017-01-30 17:36:12 +01:00
Johan Kristensen d6bf9be54e Add OpenMP parallel execution of processSkinsAndInfill
CURA-541
2017-01-30 17:35:59 +01:00
Johan K fff1164042 Add cmake option to enable OpenMP
CURA-541
2017-01-30 17:30:44 +01:00
Ghostkeeper c7e621eeb5 Merge branch '2.4' 2017-01-25 17:46:07 +01:00
Ghostkeeper 9c3170b277 Print debug message upon sending layer data
This should help a bit with debugging the slice loop. I hope.

Contributes to issue CURA-3274.
2017-01-25 17:45:34 +01:00
Ghostkeeper e2f89778e2 Remove p1 after inserting point next to it
Because this insertion requires dereferencing p1. If it has been removed from the list, dereferencing it would remove it from memory so that would be an invalid pointer reference.

Contributes to debugging issue CURA-3274.
2017-01-25 14:40:11 +01:00
Ghostkeeper 8f74952aca Replace last long in gcodeExport with int64_t
Previously we had a crash only on windows because the long was not long enough. Perhaps this fixes something with the slicing loop.

Contributes to issue CURA-3274.
2017-01-25 13:36:14 +01:00
Ghostkeeper 5b6f1db59d Merge branch 'mb-min-infill-area' of https://github.com/smartavionics/CuraEngine into smartavionics-mb-min-infill-area 2017-01-24 11:40:03 +01:00
Tim Kuipers 17463e1139 quick fix: accidental const (CURA-3309) 2017-01-23 17:37:17 +01:00
Tim Kuipers 8df26ae916 fix: equalize flow still equalizes after halving the line width for perimeter gaps (CURA-3309) 2017-01-23 17:36:24 +01:00
Tim Kuipers 9ac9d1dd59 Merge branch '2.4' 2017-01-23 14:54:16 +01:00
Tim Kuipers c2aa1d59bc fix: add perimeter_gaps config with line width of half the inner wall line width (CURA-3309)
also the perimeter gaps of the skin walls now have to be generated separately
2017-01-23 14:53:57 +01:00
Tim Kuipers 3d476f114b Merge branch '2.4' 2017-01-23 10:22:54 +01:00
Tim Kuipers 152f6e89a8 fix: more safety against empty layers (CURA-3290) 2017-01-20 16:56:01 +01:00
Tim Kuipers 113202cd34 fix: types in CubicSubdiv were wrong (CURA-3196)
the sphere_slice_radius2 was a long rather than a coord_t (long long)
2017-01-20 15:23:34 +01:00
Tim Kuipers 4bc706d618 fix: perimeter gaps were introduced for the overlap between infill and skin (CURA-3179)
this caused extra overextrusion where infill and skin meet
2017-01-20 15:01:51 +01:00
Tim Kuipers 5766e2db11 fix: don't fill gaps for outer wall when spiralize is enabled (CURA-3152) 2017-01-20 14:21:14 +01:00
Tim Kuipers 671ebccdbb fix: indentation only (CURA-3271) 2017-01-20 14:08:56 +01:00
Tim Kuipers 2516165c86 fix: there were no support bottoms on top of the first model in support of the second model (CURA-3271)
All support areas are now generated before generating the interface. They are stored in supportLayer.supportAreas right after generation of the areas.
generateSupportInterface then operates on the merged supportAreas.

Note that this has a performace impact: while evaluating support interface, the support of all models is considered, rather than only the support it introduced itself.
2017-01-20 14:08:29 +01:00
Tim Kuipers 0315aaf404 fix: support top distance was one layer too high (CURA-3269) 2017-01-20 13:26:39 +01:00
Mark Burton b43e4df3aa Remove areas of infill smaller than min_infill_area.
When you have a small feature above an outer layer (e.g. raised text), the
area under the feature will be infill rather than skin and so the skin is
composed of multiple segments around the feature. Better surface quality is
obtained if the area under the raised feature is treated as skin thus allowing
more of the outer layer to be printed as a single segment. This setting
specifies the minimum area (in mm^2) of a filled region. Areas smaller than
this will be filled with skin rather than infill.
2017-01-19 11:44:22 +00:00
Ghostkeeper 405c49133b Merge branch 'first-layer-skin-pattern' of https://github.com/14bitVoid/CuraEngine into 14bitVoid-first-layer-skin-pattern 2017-01-03 10:42:31 +01:00
Tim Kuipers 49f09ed204 fix: (anti) support meshes could f*ck up the printZ of each layer (CURA-3198)
those meshes didn't call createLayerParts and so their printZ was never set.
To determine the print height of a layer an arbitrary mesh was used to see the printZ.
2017-01-02 23:03:40 +01:00
14bitVoid e717404055 Use different skin pattern on first layer 2017-01-02 22:30:41 +01:00
Ghostkeeper 3e7d623c86 Make total bounding box use head coordinates
Instead of nozzle coordinates.
2016-12-20 15:49:37 +01:00
Ghostkeeper d694bff227 Remove superfluous comment
A bit witty, perhaps, but unnecessary.

Contributes to issue CURA-3137.
2016-12-15 12:03:28 +01:00
Ghostkeeper 05be030c45 Document range check for pre-computed cubes
Contributes to issue CURA-3137.
2016-12-14 15:15:20 +01:00
Ghostkeeper 3c5e745f83 Reintroduce assert for coordinates exceeding maximum int
Because we might still test with models of 2km size.

Contributes to issue CURA-3137.
2016-12-14 14:09:16 +01:00
Ghostkeeper 2e6cd36f20 Make unused parameters unnamed
This way they cannot be used any more.

Shuts the compiler up for issue CURA-3137.
2016-12-14 12:02:00 +01:00
Ghostkeeper 9fc4a427cd Removed checks of unsigned ints being >= 0
Because that's always true. Even the compiler says that.

Contributes sorta to issue CURA-3137.
2016-12-14 12:00:39 +01:00
Ghostkeeper 5729908023 Remove unused path parameter
According to the compiler, the path parameter of handleChildren was not unused, but that was only because it went recursively to subcalls to handleChildren, so it was actually also unused.

Contributes to issue CURA-3137.
2016-12-14 11:55:40 +01:00
Ghostkeeper c5b90b0ad9 Put check for empty extruder trains before size check
Could save a miniscule amount of time.

Contributes to issue CURA-3137.
2016-12-14 11:50:45 +01:00
Ghostkeeper 382343e558 Make recursion depth unsigned
It can't be negative anyway. And it helps with the signed-unsigned comparison checks.

Contributes to issue CURA-3137.
2016-12-14 11:48:46 +01:00
Ghostkeeper 68b293b880 Make subdivided cubic infill robust to 0% infill
Otherwise it would crash or loop forever.

Contributes to issue CURA-3137.
2016-12-14 11:44:34 +01:00
Ghostkeeper f867c0f53a Prevent crashing when given no extruder trains
Instead we return -1. A bit more robust.

Contributes to issue CURA-3137.
2016-12-14 11:05:05 +01:00
Ghostkeeper ee7e83d138 Merge branch 'mb-layer-height-comment' of https://github.com/smartavionics/CuraEngine into smartavionics-mb-layer-height-comment 2016-12-14 09:29:53 +01:00
Ghostkeeper 50df40c6c6 Revert "Re-use extruder train variable"
This reverts commit c9217d4738.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 4d924fd33d Revert "Add park distance information to retraction config"
This reverts commit 23ef513cce.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper c953a726cb Revert "Add function to move filament to park position"
This reverts commit ae00cbe497.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 7c8c0b2417 Revert "Correct speed of retraction"
This reverts commit 6925f39426.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper a834754d64 Revert "Remove some annoying compile warnings"
This reverts commit d75882a707.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 9957a0c733 Revert "Fix updating current E value"
This reverts commit 80f8760f68.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper f6ce0b4141 Revert "Put filament in parking position when we never use it any more"
This reverts commit 3cb3e5de45.

Contributes to issue CURA-2795.
2016-12-13 15:35:47 +01:00
Ghostkeeper 120a9c440c Revert "Park filament even if printer doesn't support retraction"
This reverts commit 837f992f69.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Ghostkeeper 59f72bdd98 Revert "Expand documentation of writePark"
This reverts commit 4f3b4f429d.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Ghostkeeper abc43302ac Revert "Correct retraction speed"
This reverts commit 36eb3471df.

Contributes to issue CURA-2795.
2016-12-13 15:35:46 +01:00
Mark Burton c2725bdf83 Move layer_height out of the GCodeExport class - it doesn't need to be a member. 2016-12-13 13:47:39 +00:00
Tim Kuipers fb761dfd9d fix: don't generate perimeter_gaps between inner wall and infill/skin when there is no infill (CURA-3108) 2016-12-13 13:53:05 +01:00
Mark Burton 90727a0578 Merge branch 'master' into mb-layer-height-comment 2016-12-13 08:05:31 +00:00
Mark Burton fd7d1a4bd4 Add comment that reports layer height in RepRap flavour gcode. 2016-12-12 16:54:13 +00:00
Ghostkeeper 2b1266c647 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 14:04:32 +01:00
Ghostkeeper 901bf47610 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 13:55:47 +01:00
Ghostkeeper 80a6115537 Move implementation to CPP file
Contributes to issue CURA-3006.
2016-12-12 13:44:23 +01:00
Ghostkeeper f94ca645bd Make writeRetraction accept const ref instead of const pointer
This change was already made for CURA-2795, and upon this change was built by other commits for other issues. I had to revert CURA-2795, so that would undo the changes that the other commits depend on. Instead though, I un-reverted the change because it's nice and doesn't have anything inherently to do with CURA-2795.

Contributes to issue CURA-2795.
2016-12-12 13:11:03 +01:00
Ghostkeeper 2067644d30 Revert "Update prime speed corresponding to park retraction"
This reverts commit 6c986e6cfe.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 0285e2f025 Revert "Add additional safety check for extruder switching off in later layers"
This reverts commit bc82fd98c9.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 38a1ee4270 Revert "Allow going to stand-by temperature if nozzle is later used"
This reverts commit 3c0d3f1b2d.

Contributes to issue CURA-2795.
2016-12-12 12:57:15 +01:00
Ghostkeeper 63459d5cd4 Revert "Correct documentation on why the temperature is switched off later"
This reverts commit 6972101d7e.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 138691436e Revert "Correct technicality in documentation"
This reverts commit ab2c2eed4b.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper b9c5b4593b Revert "Remove unused parameter"
This reverts commit b7e9f72023.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper a0a3a24dc1 Revert "Restore park distance exception for BFB g-code"
This reverts commit b6355b69e7.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5959b41132 Revert "Use filament park distance setting instead of direct heat & cool zones"
This reverts commit b0487fa4c7.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 3d7229c9f2 Revert "Turn extruder temperature off only if it parked the filament"
This reverts commit 84d3381be6.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper e1cfc3d93b Revert "Break retraction in two"
This reverts commit a0625aa735.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5834540bec Revert "Rename writeRetractionMove to writeMoveFilament"
This reverts commit 7c39b6b26a.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper f191d23e17 Revert "Pass retraction configs by reference instead of pointer"
This reverts commit 7a8be50b8f.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 5114ab4218 Revert "Use writeMoveFilament to retract to parking distance"
This reverts commit cb7b7bf22a.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 9f5645ecac Revert "Protect writeMoveFilament"
This reverts commit 52220ccab8.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper 66befe5827 Revert "Correct documentation of extruder_switch parameter"
This reverts commit 4263801d16.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper c9de64f946 Revert "Use optional to denote uninitiated previous extruder temperature"
This reverts commit 460c52ea6c.

Contributes to issue CURA-2795.
2016-12-12 12:57:14 +01:00
Ghostkeeper dbfa3a0f4b Revert "Move park distance data to ExtruderTrainAttributes"
This reverts commit 7777668b86.

Contributes to issue CURA-2795.
2016-12-12 12:54:56 +01:00
Ghostkeeper 02268eb7e8 Revert "Park via E-moves instead of via a retraction"
This reverts commit 6350f47cd6.

Contributes to issue CURA-2795.
2016-12-12 12:54:56 +01:00
Tim Kuipers d2b8c8bf17 Merge pull request #417 from smartavionics/mb-fix-total-filament-report
Reinstate filament used comment for RepRap flavour gcode.
2016-12-12 11:32:58 +01:00
Tim Kuipers d00f5efa77 lil fix: assert used unset extruder_count 2016-12-11 23:12:20 +01:00
Tim Kuipers b8d7162daf lil cast warning fix 2016-12-11 11:56:51 +01:00
Tim Kuipers b0293e2d6a refactor: processSkin ==> processSkinAndPerimeterGaps (CURA-3108) 2016-12-11 11:50:07 +01:00
Tim Kuipers e998eb8899 fix: perform perimeter gaps rounding safety offset only from one side (CURA-3108)
saves computation time
2016-12-11 11:24:01 +01:00
Tim Kuipers 731a69ff86 fix: perimeter gaps offset wasn't used (CURA-3108) 2016-12-11 11:04:49 +01:00
Tim Kuipers d2d34f89d5 lil doc pf planPrime (CURA-3006) 2016-12-10 14:40:58 +01:00
Tim Kuipers 0ad7bd747c fix: extruder was switched without pre-switch code (CURA-3006) 2016-12-10 14:36:13 +01:00
Tim Kuipers 3ad4dbd62e fix: plan a prime at a specified location (CURA-3006) 2016-12-10 14:26:30 +01:00
Tim Kuipers b5c8f95713 safety: assert in Meshgroup::getExtruderTrain (URA-3006) 2016-12-10 13:57:51 +01:00
Tim Kuipers 1590c18041 refactor: make explicit why only GRIFFIN needs to have priming on the initial layer (CURA-3006) 2016-12-10 13:54:25 +01:00
Tim Kuipers 405e8bdd0e fix: properly determine the starting extruder everywhere (CURA-3006) 2016-12-10 13:14:51 +01:00
Tim Kuipers 06c74c3151 lil refactor: initialize FffGcodeWriter members in initializer list (CURA-3006) 2016-12-10 12:40:24 +01:00
Tim Kuipers 28304d1aa9 refactor: compute starting extruder outside of processStartingCode (CURA-3006) 2016-12-10 12:28:27 +01:00
Tim Kuipers a18d61ccb3 refactor: reduced code duplication in Preheat (CURA-3006) 2016-12-10 12:12:51 +01:00
Ghostkeeper 6350f47cd6 Park via E-moves instead of via a retraction
While parking the filament is essentially a retraction, it should not be called this way and therefore we should make an exception for park moves that they do not allow the printer to decide for itself how far to retract the filament.

Contributes to issue CURA-2795.
2016-12-09 16:04:01 +01:00
Ghostkeeper e25a0d1305 Rename near to nearby
Because 'near' is a built-in keyword in the MSVC compiler. You can't use that as a variable name.

Contributes to issue CURA-1112.
2016-12-09 14:06:37 +01:00
Ghostkeeper 31179cab5c Revert "Use std::next instead of incrementing .begin()"
This reverts commit c85146457e.

Contributes to issue CURA-1112.
2016-12-09 14:03:19 +01:00
Ghostkeeper c85146457e Use std::next instead of incrementing .begin()
Hopefully that is allowed by MSVC. Let's see.

Contributes to issue CURA-1112.
2016-12-09 13:41:27 +01:00
Tim Kuipers f1442ba7a5 fix: also add perimeter gaps between the innermost wall and infill/skin (CURA-3108) 2016-12-08 20:24:33 +01:00
Tim Kuipers 33e50f7b05 fix: also do preheating for layers with unretracted moves only (CURA-3006) 2016-12-08 19:35:59 +01:00
Tim Kuipers 865aacbfb1 fix: always prime nozzles on the first layer (CURA-3006) 2016-12-08 19:35:14 +01:00
Tim Kuipers 2bc7ee5df0 feat: prime all nozzles at the end of the first layer (CURA-3006) 2016-12-08 19:34:50 +01:00
Ghostkeeper 7777668b86 Move park distance data to ExtruderTrainAttributes
It was deemed to be more logical there.

Contributes to issue CURA-2795.
2016-12-08 15:35:00 +01:00
Tim Kuipers 96e8c7a73d lil fix: forward declaration of ExtruderPlan for TimeMaterialEstimates (CURA-3006) 2016-12-08 15:30:19 +01:00
Tim Kuipers 89446b8e32 refactor: factor out GCodePath into its own header file (CURA-3006) 2016-12-08 15:27:07 +01:00
Tim Kuipers b2fa8d3be1 refactor: factor out TimeMaterialEstimates into its own .h and .cpp files (CURA-3006) 2016-12-08 15:21:59 +01:00
Tim Kuipers d8e0e84fcc refactor: factor out NozzleTempInsert into its own header file (CURA-3006) 2016-12-08 15:15:30 +01:00
Tim Kuipers 443c630997 Merge branch 'feature_mesh_order' 2016-12-08 15:06:54 +01:00
Tim Kuipers 9831ef3580 lil fix: allow negative bed temperatures (CURA-2781) 2016-12-08 15:05:07 +01:00
Ghostkeeper 460c52ea6c Use optional to denote uninitiated previous extruder temperature
NaN is also a very common pattern, but std::optional is more explicit.

Contributes to issue CURA-2795.
2016-12-08 14:55:15 +01:00
Tim Kuipers 3d1e79c670 lil comment (CURA-2907) 2016-12-08 11:56:21 +01:00
Ghostkeeper 879b86cab9 Also enforce z-distance from the top
This entails a bit slower slicing. How much slower will be determined by a performance test.

Contributes to issue CURA-2907.
2016-12-08 10:17:37 +01:00
Mark Burton 941b1ff1ac Reinstate filament used comment for RepRap flavour gcode. 2016-12-07 14:55:54 +00:00
Ghostkeeper 4263801d16 Correct documentation of extruder_switch parameter
This parameter is only used for firmware-retracting printers.

Contributes to issue CURA-2795.
2016-12-07 15:44:11 +01:00
Ghostkeeper 52220ccab8 Protect writeMoveFilament
It should be used as a helper function only.

Contributes to issue CURA-2795.
2016-12-07 15:41:53 +01:00
Ghostkeeper cb7b7bf22a Use writeMoveFilament to retract to parking distance
This re-uses a bit of code.

Contributes to issue CURA-2795.
2016-12-07 15:40:03 +01:00
Ghostkeeper 7a8be50b8f Pass retraction configs by reference instead of pointer
This makes things a bit safer. It's a const reference, so it should be very easy.

Contributes to issue CURA-2795.
2016-12-07 15:36:52 +01:00
Ghostkeeper 7c39b6b26a Rename writeRetractionMove to writeMoveFilament
I think that is more indicative of what it can do, since it doesn't only concern retractions.

Contributes to issue CURA-2795.
2016-12-07 15:31:40 +01:00
Ghostkeeper a0625aa735 Break retraction in two
One starting point for normal retractions and one to directly move the filament to a certain position.

Contributes to issue CURA-2795.
2016-12-07 15:29:28 +01:00
Ghostkeeper 84d3381be6 Turn extruder temperature off only if it parked the filament
The two had slightly different conditions if the stand-by temperature was zero.

Contributes to issue CURA-2795.
2016-12-07 12:50:49 +01:00
Ghostkeeper 8b01098dd1 Cache prime tower outside polygon
Saves a few polygon unions, since this is the same for each layer anyway.

Contributes to issue CURA-2684.
2016-12-07 11:54:12 +01:00
Ghostkeeper 1bfe4e74d2 Correct height of prime tower to subtract from support
The prime tower has one additional layer on top of the last extruder switch.

Contributes to issue CURA-2684.
2016-12-07 11:49:11 +01:00
Tim Kuipers bcde7f50ee Revert "fix: don't output print temperature in starting code if set to zero (CURA-3101)"
This reverts commit 5db20ee68d.
2016-12-06 16:39:33 +01:00
Tim Kuipers c7cadc132b Revert "fix: don't output print temperature in starting code if set to zero (CURA-3101)"
This reverts commit abfb41006b.
2016-12-06 16:39:12 +01:00
Tim Kuipers abfb41006b fix: don't output print temperature in starting code if set to zero (CURA-3101) 2016-12-06 16:37:35 +01:00
Tim Kuipers 5db20ee68d fix: don't output print temperature in starting code if set to zero (CURA-3101) 2016-12-06 16:36:32 +01:00
Ghostkeeper b0487fa4c7 Use filament park distance setting instead of direct heat & cool zones
The sum of the heat and cool zone lengths is now computed via inheritance in the front-end.

Contributes to issue CURA-2795.
2016-12-06 15:57:39 +01:00
Ghostkeeper b6355b69e7 Restore park distance exception for BFB g-code
Turns out that BFB flavour doesn't have an e-axis, but it still doesn't count as a firmware retract.

Contributes to issue CURA-2795.
2016-12-06 14:58:26 +01:00
Tim Kuipers 9597ee5645 fix: concentric infill always acted as if alternate_extra_perimeter was enabled (CURA-2772) 2016-12-06 14:09:28 +01:00
Tim Kuipers 5ba1cb674e fix: first walls of concentrid 3D infill didn't get added 2016-12-06 13:55:37 +01:00
Tim Kuipers f336d5ffc0 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-12-06 13:03:19 +01:00
Tim Kuipers 1d0ac8528b fix: only ensure unprimed extruders are primed (CURA-3006) 2016-12-06 13:02:35 +01:00
Tim Kuipers 5c06c48c87 small fix: only perform anti-overhang when there is anti-overhang (CURA-3006, CURA-2077) 2016-12-06 13:01:26 +01:00
Tim Kuipers 837d4e7fba feat: tracking of extruder_prime_is_planned (CURA-3006) 2016-12-06 12:59:50 +01:00
Ghostkeeper 7ded01a110 Remove unused prev_extruder parameter
It doesn't need to know which extruder it came from. Just with which extruder to draw the lines. The new_extruder parameter was renamed because there is no need to disambiguate any more.

Contributes to issue CURA-2684.
2016-12-06 11:20:51 +01:00
Ghostkeeper 3f3b526ebd Remove unused storage parameter
It doesn't need to know any settings and the documentation was outdated.

Contributes to issue CURA-2684.
2016-12-06 11:17:28 +01:00
Ghostkeeper b29607b8a9 Remove unused gcode parameter
The results are stored in the gcodeLayer parameter. Has been this way for a while. This was generating compiler warnings. The documentation was already updated, it seems.

Contributes to issue CURA-2684.
2016-12-06 11:15:30 +01:00
Ghostkeeper e641a25aab Subtract prime tower from support after determining prime tower height
There is a circular dependency between prime tower height and support areas. Prime tower height is determined by the highest extruder switch. The highest extruder switch is dependent on the height of support if support is printed with a different exturder. The height of support is dependent on the support areas and the support areas are dependent on the prime tower height. Previously we resolved this by printing no support in the prime tower area and no prime tower, possibly violating the support horizontal expansion in an edge case. Now it is resolved by printing a prime tower there even if there are no extruder switches.

Contributes to issue CURA-2684.
2016-12-06 11:07:10 +01:00
Ghostkeeper b7e9f72023 Remove unused parameter
One of the many. Seems to me that the documentation is outdated if an essential parameter like this one is unused.

Contributes to issue CURA-2795.
2016-12-06 09:44:18 +01:00
Ghostkeeper ab2c2eed4b Correct technicality in documentation
Contributes to issue CURA-2795.
2016-12-06 09:41:40 +01:00
Ghostkeeper 6972101d7e Correct documentation on why the temperature is switched off later
This old code needs to be after the switch, but it was not clear to me why. I tried to make it clear and failed.

Contributes to issue CURA-2795.
2016-12-06 09:40:02 +01:00
Ghostkeeper 3c0d3f1b2d Allow going to stand-by temperature if nozzle is later used
Contributes to issue CURA-2795.
2016-12-06 09:38:08 +01:00
Ghostkeeper bc82fd98c9 Add additional safety check for extruder switching off in later layers
This is never used, but it can't hurt.

Contributes to issue CURA-2795.
2016-12-06 09:32:34 +01:00
Ghostkeeper 6c986e6cfe Update prime speed corresponding to park retraction
We have some double data here, which could lead to asynchronicity. As is evident in my mistake.

Contributes to issue CURA-2795.
2016-12-06 09:30:57 +01:00
Ghostkeeper 36eb3471df Correct retraction speed
Contributes to issue CURA-2795.
2016-12-06 09:24:33 +01:00
Ghostkeeper 4f3b4f429d Expand documentation of writePark
It wasn't clear what retraction limit this function was not adhering to. It is not adhering to the only limit on retraction we currently have: The number of retractions.

Contributes to issue CURA-2795.
2016-12-06 09:10:34 +01:00
Ghostkeeper 837f992f69 Park filament even if printer doesn't support retraction
Let's hope they do support non-increasing E values.

Contributes to issue CURA-2795.
2016-12-06 09:04:07 +01:00
Tim Kuipers ad76736d26 fix: specify starting extruder when adhesion_type==none (CURA-3006, CURA-759) 2016-12-05 17:01:21 +01:00
Tim Kuipers 3fe5a000f2 fix: set extruder back after priming in the first raft layer (CURA-3006) 2016-12-05 17:00:25 +01:00
Ghostkeeper d5a7e2f62e Add documentation of why this union is necessary
Contributes to issue CURA-3017.
2016-12-05 14:23:41 +01:00
Ghostkeeper 6e5315f754 Revert "Also union layer polygons before combing"
This reverts commit 4aff5a1488.
2016-12-05 14:17:22 +01:00
Ghostkeeper 3cb3e5de45 Put filament in parking position when we never use it any more
It should retract before the nozzle switch, of course, so it can't be retracting at the same time as when it's setting the extruder temperature to 0.

Contributes to issue CURA-2795.
2016-12-05 12:04:12 +01:00
Ghostkeeper 80f8760f68 Fix updating current E value
It should be updated before we use it to write the retraction. Also we should subtract the retraction because it is the amount to retract, not the amount to prime.

Contributes to issue CURA-2795.
2016-12-05 12:02:33 +01:00
Ghostkeeper d75882a707 Remove some annoying compile warnings
They are by far not all, but these remove some of the 'unused variable' warnings.

Contributes to issue CURA-2795.
2016-12-05 11:54:51 +01:00
Ghostkeeper 6925f39426 Correct speed of retraction
It should be in millimetres per minute, not millimetres per second. Also, always use the retraction speed.

Contributes to issue CURA-2795.
2016-12-05 11:34:59 +01:00
Tim Kuipers d6cdfefb58 fix: use start_layers_at_same_position to see with which mesh to start a layer (CURA-1112) 2016-12-03 12:43:43 +01:00
Tim Kuipers dfa23feb31 lil optimization vector.reserve (CURA-1112) 2016-12-03 12:26:12 +01:00
Tim Kuipers b0888e4424 refactor: compute mesh order globally (CURA-1112) 2016-12-03 12:25:53 +01:00
Tim Kuipers fd06e10646 fix: calculateMeshOrder now uses a smart path (CURA-1112)
but doesn't yet use the Z seam setting and starts at an arbitrary point in the smart cycle
2016-12-03 12:14:55 +01:00
Tim Kuipers 6e051e0bdc refactor: let OrderOptimizer contain objects of arbitrary type rather than always pointers (CURA-1112) 2016-12-03 12:11:19 +01:00
Ghostkeeper ae00cbe497 Add function to move filament to park position
If the g-code has no control over the retraction distances via G1, it performs a normal retraction instead.

Contributes to issue CURA-2795.
2016-12-02 16:58:13 +01:00
Ghostkeeper 23ef513cce Add park distance information to retraction config
This is the location of the filament when the extruder is parked.

Contributes to issue CURA-2795.
2016-12-02 16:34:19 +01:00
Tim Kuipers 3aa29d898f feat: orderOptimizer (CURA-1112) 2016-12-02 15:22:31 +01:00
Ghostkeeper c9217d4738 Re-use extruder train variable
I moved the definition of the 'train' variable a bit earlier so it can be re-used a bit more often.

Contributes to issue CURA-2795.
2016-12-02 15:05:51 +01:00
Ghostkeeper 16d4ab923f Make support attach directly to prime tower
No X/Y offset is happening. This makes both support and prime tower sturdier, and prevents having to do an offset of the prime tower so it would make the support a bit faster in theory (but the difference is not noticable).

Contributes to issue CURA-2684.
2016-12-02 12:39:05 +01:00
Ghostkeeper 60d4a3108d Don't print support inside hollow prime towers
It only takes the outside of the prime tower ground polygon.

Contributes to issue CURA-2684.
2016-12-02 12:34:59 +01:00
Tim Kuipers 7ad5eee176 indent change only (CURA-1112) 2016-12-02 11:58:38 +01:00
Tim Kuipers 209e9fa50f refactor: separate out FffGcodeWriter::addMeshPartToGCode (CURA-1112) 2016-12-02 11:58:11 +01:00
Ghostkeeper 4aff5a1488 Also union layer polygons before combing
If two polygons overlap, clipperlib can interpret them as one being the inside polygon with the even odd rule. If they overlap exactly, it produces no output. However if you union the polygons it will properly avoid all objects regardless of whether they overlap with other objects.

Contributes to issue CURA-3017.
2016-12-02 11:21:16 +01:00
Ghostkeeper 4f786d5533 Subtract prime tower area from support
At this point we don't know for certain if there will be a prime tower, but that is impossible since there is a circular dependency between prime tower and support then.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Ghostkeeper b4fd72f92f Re-use layer outlines variable
We already had it stored in a variable and the polygons don't change in between, so we can safely re-use this.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Ghostkeeper df892030c5 Only include prime tower if it is enabled
We always generate a ground polygon now, so we can't rely on the ground polygon being empty if it is disabled due to there being no layer switches.

Contributes to issue CURA-2684.
2016-12-01 13:10:49 +01:00
Tim Kuipers e31a516205 fix: ooze/draft shield now processed again (CURA-3077) 2016-12-01 11:57:13 +01:00
Ghostkeeper c77afe06e2 Generate prime tower polygon at construction
This way we can always request the prime tower ground polygon throughout the entire process. The polygon will be empty if prime tower is disabled.

Contributes to issue CURA-2684.
2016-11-30 17:41:41 +01:00
Ghostkeeper 921ffbe659 Merge branch 'feature_z_seam_location' 2016-11-30 16:34:31 +01:00
Ghostkeeper 96ac0758b9 Don't use sqrt in a constexpr
sqrt doesn't return a constexpr. GCC allows it, but Clang doesn't. Instead we just use a preprocessor definition.

Contributes to issue CURA-2602.
2016-11-30 10:44:19 +01:00
Ghostkeeper dd8639b1f5 Use std::abs instead of abs
The clang compiler doesn't define abs outside of the std library.
2016-11-30 10:27:30 +01:00
Ghostkeeper 2572349938 Remove debug prints
These should never have been committed.

Contributes to issue CURA-3017.
2016-11-30 10:09:09 +01:00
Tim Kuipers ac9a053f5b Revert "lil fix: don't compute overhang on first layer"
This reverts commit bec51eb0bd.
2016-11-29 17:19:15 +01:00
Tim Kuipers bec51eb0bd lil fix: don't compute overhang on first layer 2016-11-29 17:17:35 +01:00
Jaime van Kessel 12ccb3512f Merge branch 'feature_recursive_infill_code_review' of github.com:Ultimaker/CuraEngine 2016-11-29 15:42:02 +01:00
Tim Kuipers 87a3a30bc5 fix: open polylines bug
turned back a mistake which was made in a refactor: fa203bd976
2016-11-29 14:32:01 +01:00
Tim Kuipers 6ae41a5e86 fix: max feedrate is in mm/s in both frontend and firmware (CURA-2955)
I've verified that M203 Sxx is interpreted as mm/s in Marlin, Sprinter and Repetier-Firmware.
Other firmware types we support, like MakerBot, BFB and MACH weren't checked.
2016-11-29 13:15:22 +01:00
Ghostkeeper 8ce016064f Fix typo in documentation
The variable name is _dest_point, it seems.

Contributes to issue CURA-2229.
2016-11-28 17:05:51 +01:00
Ghostkeeper 4e16b4313f Fix typo in assert message
Contributes to issue CURA-3008.
2016-11-28 16:55:24 +01:00
Ghostkeeper 5b493c17dc Union first layer polygons for brim too
For skirt it does this union by taking the convex hull. For brim it did no such thing, so when polygons overlapped it would get complex polygons which didn't work out well.

Contributes to issue CURA-3017.
2016-11-28 16:37:28 +01:00
Tim Kuipers 4b45726ada Revert "feat: PolygonPointer as distinct from PolygonRef (CURA-2602)"
This reverts commit a2a8604c72.
2016-11-28 14:42:18 +01:00
Tim Kuipers 6653f5a557 Merge branch 'master' into feature_recursive_infill_code_review 2016-11-28 14:39:53 +01:00
Tim Kuipers 4da8c6d8e7 fix: no more aabb hit check fails (CURA-2992) 2016-11-28 14:35:13 +01:00
Tim Kuipers c5ce425924 fix: no more use of deleted memory by optional (CURA-3008) 2016-11-28 14:34:45 +01:00
Tim Kuipers c74d4f7550 Revert "feat: PolygonPointer as distinct from PolygonRef (CURA-3008)"
This reverts commit 20cd4275fc.
2016-11-28 13:43:03 +01:00
Tim Kuipers 19f093e0cc safety: assert on optional::instance (CURA-3008) 2016-11-28 13:42:36 +01:00
Tim Kuipers 2a4cca0402 fix: use ClosestPolygonPoint::isValid where neccesary (CURA-3008) 2016-11-28 13:40:54 +01:00
Tim Kuipers 32804c102c fix: use optional<PolygonRef> instead of PolygonPointer (CURA-3008) 2016-11-28 13:40:02 +01:00
Tim Kuipers 776f56fc37 lil fix: no more un/signed int comaprison 2016-11-28 12:18:13 +01:00
Tim Kuipers 866e911739 fix: make z_seam_pos settable per mesh (CURA-1461) 2016-11-25 16:43:31 +01:00
Tim Kuipers a92cd23e62 safety: extra assert for too many nozzle switches in a layer 2016-11-25 16:10:10 +01:00
Tim Kuipers 4725001564 fix: remove printing_temperature_command if precooling goes further back (CURA-1932)
If the precooling command will precede the printing temeprature command to heat to the temp of the next layer, then don't use that temp;
we should already be cooling down, rather than heating toward the temp of the next layer.
2016-11-25 14:00:26 +01:00
Tim Kuipers a18595877f safety: extra assert on temp in NozzleTempInsert (CURA-1932) 2016-11-25 14:00:26 +01:00
Tim Kuipers 40e7c450d5 fix: getCoolDownPointAfterWarmUp outer_temp was switched (CURA-1932)
This caused the timing of the precool command to be off
2016-11-25 14:00:26 +01:00
Tim Kuipers 13a0b11d68 fix: don't use material_print_temperature_layer_0 if it's zero (CURA-1932) 2016-11-25 14:00:26 +01:00
Ghostkeeper 4fc69f608a Fix max extruder height if only using bed adhesion
If an extruder only prints bed adhesion, the maximum extruder height for that extruder is now correct.

Contributes to issue CURA-2993.
2016-11-25 11:21:59 +01:00
Ghostkeeper deb577d559 Make extruder numbers const and unsigned
This is in line with the first one, and more correct.

Contributes to issue CURA-2993.
2016-11-25 11:17:45 +01:00
Ghostkeeper c8161da3eb Remove spaces within brackets
As per our code style.

Contributes to issue CURA-2993.
2016-11-25 11:16:30 +01:00
Ghostkeeper 87733834d1 Expand documentation for computePrintHeightStatistics
Should make it more overseeable.

Contributes to issue CURA-2993.
2016-11-25 11:08:29 +01:00
Ghostkeeper b9aeea425f Correct documentation of computePrintHeightStatistics
It's initialised, not uninitialised (or unitialised).

Contributes to issue CURA-2993.
2016-11-25 11:01:51 +01:00
Tim Kuipers 44b3039db6 optimization: use already computed ClosestPolygonPoint for ensureInsideOrOutside during combing crossing calculation (CURA-2229) 2016-11-24 15:09:41 +01:00
Tim Kuipers 6476270cf6 fix: combing start crossing inside location was sometimes on the destination part, rather than the starting part (CURA-2229) 2016-11-24 13:56:57 +01:00
Ghostkeeper e532f3ddda Merge branch 'feature_anti_support' 2016-11-22 16:36:52 +01:00
Tim Kuipers 7aaab151e7 fix: better handling of edge cases for Preheat::getCoolDownPointAfterWarmUp and getWarmUpPointAfterCoolDown (CURA-3006) 2016-11-22 14:54:59 +01:00
Tim Kuipers 0353433919 fix: also ensure prime after first layer of raft (CURA-3006) 2016-11-22 14:52:21 +01:00
Tim Kuipers 40a6c495c7 refactor: move code from FffGcodeWriter::processLayer into new FffGcodeWriter::ensureAllExtrudersArePrimed (CURA-3006) 2016-11-22 14:51:48 +01:00
Ghostkeeper cfa6758911 Merge branch 'fix_support_order'
Conflicts:
	src/FffGcodeWriter.cpp
	src/PrimeTower.cpp
2016-11-22 13:03:38 +01:00
Tim Kuipers f6b29b1d8a Merge branch 'feature_anti_support' of github.com:Ultimaker/CuraEngine into feature_anti_support 2016-11-22 10:21:44 +01:00
Ghostkeeper bd79a8468e Merge branch 'feature_hollow_prime_tower' 2016-11-22 09:19:16 +01:00
Tim Kuipers 83a1a09772 fix: merge fix for PathOrderOptimizer in FffGcodeWriter::processSkin (CURA-1461) 2016-11-21 18:48:10 +01:00
Tim Kuipers 2c425421ec Merge branch 'master' into feature_z_seam_location 2016-11-21 18:45:10 +01:00
Ghostkeeper 35dcc6906f Merge branch 'feature_precool' 2016-11-21 17:35:40 +01:00
Tim Kuipers db7bc279ee fix: removeEmptyFirstLayers can still remove even when support was generated (but removed by anti support meshes (CURA-2077) 2016-11-21 16:50:26 +01:00
Tim Kuipers a7ea623266 fix: removeEmptyFirstLayers can still remove even when support was generated (but removed by anti support meshes (CURA-2077) 2016-11-21 16:34:55 +01:00
Tim Kuipers 724589c13a fix: support towers went all the way to the bed (CURA-2077) 2016-11-21 16:31:52 +01:00
Tim Kuipers db1fa098ad Merge branch 'master' into feature_anti_support 2016-11-21 16:12:57 +01:00
Tim Kuipers be99db30c0 fix: combing referenced element zero of a polygons (CURA-3008) 2016-11-21 15:54:07 +01:00
Tim Kuipers 9456592dd7 fix: use PolygonPointer in ClosestPolygonPoint (CURA-3008)
sometimes functions cannot return a ClosestPolygonPoint, or we start with an empty one untill we find any point

cherry-picked from 4634338c7d
2016-11-21 15:45:27 +01:00
Tim Kuipers 20cd4275fc feat: PolygonPointer as distinct from PolygonRef (CURA-3008)
It was a bad idea to change PolygonRef into PolygonPointer, because then we'd have to check whether it is a nullptr all over the engine...

cherry-picked from a2a8604c72
2016-11-21 15:38:12 +01:00
Tim Kuipers 67697d5258 fix: handle modulo operations for negative layer numbers (CURA-2789) 2016-11-21 14:18:39 +01:00
Tim Kuipers 7d8e4de7ba fix: prime tower didn't get added in filler layers (CURA-2789)
PrimeTower always received layer_nr=0 for those layers, so i had to propagate the negative layer numbers to addSupportToGcode and to PrimeTower
2016-11-21 14:17:32 +01:00
Tim Kuipers 2486120a38 fix: don't send polygons over command socket during planning phase (CURA-2789)
The polygons of the prime tower were also sent on the wrong layer / height, causing it to be visualized within raft layers
2016-11-21 14:13:56 +01:00
Tim Kuipers 43c8f2a913 fix: print prime tower directly after platform adhesion on layer 0 (CURA-2789) 2016-11-21 13:19:25 +01:00
Tim Kuipers baf6410a1e fix: PolygonUtils::findClosest failed when only the first polygon was empty (CURA-2602) 2016-11-18 16:33:28 +01:00
Tim Kuipers 4634338c7d fix: use PolygonPointer in ClosestPolygonPoint (CURA-2602)
sometimes functions cannot return a ClosestPolygonPoint, or we start with an empty one untill we find any point
2016-11-18 16:32:56 +01:00
Tim Kuipers a2a8604c72 feat: PolygonPointer as distinct from PolygonRef (CURA-2602)
It was a bad idea to change PolygonRef into PolygonPointer, because then we'd have to check whether it is a nullptr all over the engine...
2016-11-18 16:30:34 +01:00
Ghostkeeper dd8b57b666 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-11-18 15:51:10 +01:00
Jaime van Kessel 72ed492a61 Merge branch 'feature_alternate_carving' of github.com:Ultimaker/CuraEngine 2016-11-18 15:14:12 +01:00
Ghostkeeper c02482590f Merge branch 'feature_fill_small_gaps' 2016-11-18 15:05:58 +01:00
Tim Kuipers f32e2d9554 Merge branch 'master' into feature_anti_support 2016-11-18 14:07:29 +01:00
Tim Kuipers 40a6075022 Merge branch 'master' into feature_recursive_infill_code_review 2016-11-18 13:55:06 +01:00
Jaime van Kessel bf61814849 Merge branch 'feature_fan_speed_0' of github.com:Ultimaker/CuraEngine 2016-11-18 10:43:56 +01:00
Jaime van Kessel 5741e79ade Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-11-18 10:42:17 +01:00
Jaime van Kessel 09419fd6be Merge branch 'feature_bed_adhesion_none' of github.com:Ultimaker/CuraEngine 2016-11-18 10:35:29 +01:00
Tim Kuipers a002f4b3b2 feat: alternate_carve_order (CURA-2992) 2016-11-17 19:58:31 +01:00
Tim Kuipers 25c7ccb0d3 fix: outside combing put starting and ending points of basic comb path on the wrong side (CURA-2988)
basic comb paths started on the wrong side, then followed the polygon on the right side and ended on the wrong side again
2016-11-17 17:22:53 +01:00
Tim Kuipers 58a99a403b fix: createLocToLineGrid didn't create any cells and copied the hashmap (CURA-2988)
std::bind doesn't allow for binding by reference
2016-11-17 17:01:57 +01:00
Tim Kuipers dd8594b200 fix: inside combing put starting and ending points of basic comb path on the wrong side (CURA-2988)
basic comb paths started on the wrong side, then followed the polygon on the right side and ended on the wrong side again
2016-11-17 17:00:23 +01:00
Ghostkeeper 6df5368cb9 Update variable names in documentation
The variable names were changed but changed incorrectly here.

Contributes to issue CURA-1932.
2016-11-17 14:49:29 +01:00
Ghostkeeper 3a038a2cd2 Put correct bounding box in g-code header
This is according to the Griffin header specification.

Contributes to issue CURA-2625.
2016-11-17 13:35:59 +01:00
Tim Kuipers 9613e186a3 docs: fixed docs location and included precool diagram in LayerPlanBuffer (CURA-1932) 2016-11-16 17:54:11 +01:00
Tim Kuipers 82b2362b2d lil compiler warning removal (CURA-1932) 2016-11-16 16:52:54 +01:00
Tim Kuipers a6ee34602c refactor: rename rediculous timeBeforeEndToInsertPreheatCommand_coolDownWarmUp ==> sensible getWarmUpPointAfterCoolDown and vice versa (CURA-1932) 2016-11-16 16:50:05 +01:00
Tim Kuipers 05d29eabcd refactor: timeBeforeEndToInsertPreheatCommand arguments better names (CURA-1932) 2016-11-16 16:41:18 +01:00
Tim Kuipers f7e3534a79 fix: update outdated function signature (add const and better naming) (CURA-1932) 2016-11-16 16:30:56 +01:00
Tim Kuipers bd4972e466 refactor: rename function argument to better match the intent (CURA-1932) 2016-11-16 16:27:44 +01:00
Tim Kuipers 0acf3beec2 fix: insertFinalPrintTempCommand: compute weighted average_print_temp by taking time into account (CURA-1932)
note that the print temp statistic doesn't really matter that much
2016-11-16 16:20:51 +01:00
Tim Kuipers 081055be46 fix: better documentation for LayerPlanBuffer::insertFinalPrintTempCommand (CURA-1932) 2016-11-16 16:17:42 +01:00
Tim Kuipers c57ab7d090 fix: getSettingInMicrons should return coord_t, rather than int! (CURA-2602) 2016-11-16 15:25:12 +01:00
Tim Kuipers bd5a82dd86 refactor: split up SubDivCube::generateSubdivisionLines into two functions (CURA-2602)
one public base class function, one private recursive function.
Instead of using a heap-allocated array of pointers to Polygons, just use a stack-allocated array of Polygons
2016-11-16 15:14:16 +01:00
Tim Kuipers 942d409d72 lil refactor: moved constant expression inside function where it's used (CURA-2602) 2016-11-16 14:56:48 +01:00
Tim Kuipers a17d3017b5 lil simplification and reduction of rounding errors (CURA-2602) 2016-11-16 14:35:35 +01:00
Tim Kuipers b79f605ce2 fix: made Polygons::remove be O(1) (CURA-2602)
The order of polygons is never important, except in PolygonsPart where the first polygon is the outer one, but there it should be impossible to remove the first element anyway.
2016-11-16 14:22:45 +01:00
Tim Kuipers ea1c9b80e9 refactor: make private function of SubDivCube::addLineAndCombine (CURA-2602) 2016-11-16 14:21:10 +01:00
Tim Kuipers 1d86f54a8b lil refactor 2016-11-16 13:59:14 +01:00
Tim Kuipers fc909ff3ad fix: subdivcube: max_side_length based on machine dimensions (CURA-2602) 2016-11-16 13:56:41 +01:00
Tim Kuipers 7f54037246 refactor: merged cube properties into one vector of ew struct (CURA-2602) 2016-11-16 13:36:08 +01:00
Tim Kuipers ec7164004e refactor: factored addLine into Polygons class (CURA-2602) 2016-11-16 13:22:28 +01:00
Tim Kuipers a6314a9f80 lil improvements (CURA-2602) 2016-11-16 12:57:09 +01:00
Ghostkeeper 560d23d3f2 Simplify skipping current extruder in calculateExtruderOrder
These are equivalent, but the code is now simpler.

Contributes to issue CURA-2789.
2016-11-16 12:51:50 +01:00
Tim Kuipers 1207291950 rename: src/infill/subDivCube.cpp ==> src/infill/SubDivCube.cpp (CURA-2602) 2016-11-16 12:47:47 +01:00
Tim Kuipers 44e7b0e0be fix: apply outer wall line width offset for interperimeter gaps (CURA-2306) 2016-11-16 10:18:40 +01:00
Tim Kuipers 8c48d1ce82 fix: material_extrusion_cool_down_speed handled correctly (CURA-1932)
it was handled as if the inverse (in s/*c) was the modifier rather than the original (in *C/s)
2016-11-15 17:53:38 +01:00
Tim Kuipers 8432e9ed9f fix: first start of extruder, require print temp rather than initial print temp (CURA-1932) 2016-11-15 17:52:07 +01:00
Tim Kuipers 1b014199c9 refactor: store initial_printing_temperature in ExtruderPlan (CURA-1932) 2016-11-15 17:46:47 +01:00
Tim Kuipers 7e0c5c6323 refactor: replaced timeBeforeEndToInsertPreheatCommand_warmUp by getTimeToGoFromTempToTemp (CURA-1932)
both functions actually tried to do the same thing
2016-11-15 17:42:37 +01:00
Tim Kuipers 9ada0901a6 refactor: insertPreheatCommands ==> insertTempCommands (CURA-1932) 2016-11-15 17:38:26 +01:00
Tim Kuipers d60d32a5d8 fix: exposed less internal functions of LayerPlanBuffer (CURA-1932) 2016-11-15 14:38:15 +01:00
Tim Kuipers 667c00aa5a lil assert (CURA-1932) 2016-11-15 14:37:32 +01:00
Tim Kuipers cf0ca05843 fix: insert initial/final print temp commands for groups of extruder plans rather than each (CURA-1932)
before this we would get precool and preheat during a layer change even when the last extruder of the previous layer is the first extruder of the next...
2016-11-15 14:37:07 +01:00
Tim Kuipers 3bcabacef4 fix: wrong check whether initial print temp was unused (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers b5801ea847 fix: warmUpCoolDown computed cool_down_time as if it was warm_up_time (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers 1bb90a2f03 fix: use more accurate coolDownWarmUp time estimations (CURA-1932)
with the initial and final printing temperature the old assumption didn't hold any more:
'Assumes from_temp is approximately the same as @p temp'

It uses the same logic as timeBeforeEndToInsertPreheatCommand_warmUpCoolDown(.)
2016-11-14 17:50:08 +01:00
Tim Kuipers cf55aef52b fix: for warmUpCoolDown compute max temp accurately (CURA-1932)
Didn't take extra heating time into account
2016-11-14 17:50:08 +01:00
Tim Kuipers c0f7538fdb fix: take care of during_printing for heatup/cooldown timings (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers 395ab9b7cd fix: warmup and cooldown times were incorrect (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers 80ecabb618 refactor: move Preheat functions to cpp file (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers ff291cc4d1 fix: insert temp commands for preheat and precool (CURA-1932)
insert heat command from initial_print_temp to print_temp at the first extrusion in a layer plan
insert cool command from print_temp to final_print_temp during the last extrusion moves in a layer plan
2016-11-14 17:50:08 +01:00
Tim Kuipers 4183835d2b fix: use initial_print_temp where print_temp used to be used (CURA-1932)
heating up is now done toward initial_print_temp
and the required temp at the start of a layer is initial_print_temp
2016-11-14 17:50:08 +01:00
Tim Kuipers 0e24d8db47 cleanup: no more boolean trappiness (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers f7bde54869 feat: some handy Preheat functions (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers bf8f027a97 setting: material_initial_print_temperature, material_final_print_temperature (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers 60625ea4bf refactor: ExtruderPlan::required_temp ==> printing_temperature (CURA-1932) 2016-11-14 17:50:08 +01:00
Tim Kuipers a9f6ae1943 refactor: rewrote logic in FffGcodeWriter::calculateExtruderOrder (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers cd01d7051b refactor: support_skin ==> support_interface (CURA-2789) 2016-11-14 17:49:16 +01:00
Ghostkeeper bd126bb841 Fix typo in assert message
Contributes to issue CURA-2789.
2016-11-14 17:49:16 +01:00
Tim Kuipers f1b3fb3cd6 refactor/fix: plan extruders independently of meshes and plan support per extruder plan (CURA-2789)
When printing dual color models with dual support (different support infill extruder than support skin extruder)
all support would either be printed before all models or after, leading to two extruder switches in one layer.

Now the extruder_order is calculated independently of the mesh_order and the support is added per extruder plan.

The support is always added first, so that:
- in single extrusion we never print in order support-support-model-model which would lead to model being printed on top of model which hasn't cooled enough
- in dual color, dual support we always print E1-support-model-support-model-E2-support-model-support-model so that oozle is automatically wiped on support
2016-11-14 17:49:16 +01:00
Tim Kuipers d48f06db0e refactor/fix: return whether support was added to extruder plan and only switch extruder if plans were added (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers 5567b06ed4 cleanup: remove unused parameter to FffGcodeWriter::addSupportToGCode (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers e87b11179f fix: getExtrudersUsed only include platform adhesion for first layer(s) (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers a17fef9d4b refactor: separate handleSupportBeforeModels out of FffGcodeWriter::addSupportToGCode (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers 4c49bf7894 cleanup: const-correctness for SliceDataStorage::getExtrudersUsed (CURA-2789) 2016-11-14 17:49:16 +01:00
Tim Kuipers bcb0ded784 fix: print support consecutive instead of always at end of layer (CURA-2789)
also support_interface_extruder_nr mattered even if the interface was turned off
2016-11-14 17:49:16 +01:00
jack 54ba25e7f5 Merge pull request #411 from Ultimaker/bugfix_prime_tower_too_high
CURA-2633 fix: limit prime tower height when support is touching buildplate onl…
2016-11-14 14:57:13 +01:00
Tim Kuipers 84a7f401a2 lil indent fix only (CURA-759) 2016-11-14 12:41:58 +01:00
Tim Kuipers 6afdf19ce4 feat: adhesion_type none (CURA-759) 2016-11-14 12:41:29 +01:00
Tim Kuipers 4aa1cc47f4 fix: don't start output decimal numbers with the decimal dot 2016-11-11 17:23:53 +01:00
Tim Kuipers 166473596a fix: compute perimeter_gaps for zigzag skin infill (CURA-2306) 2016-11-11 16:00:35 +01:00
Tim Kuipers feb21b67d1 fix: turn off gap filling if fill_perimeter_gaps is set to nowhere (CURA-2306) 2016-11-11 14:58:59 +01:00
Tim Kuipers dae7ec184f removal: remove skin option from fill_perimeter_gaps mode (CURA-2306) 2016-11-11 14:44:48 +01:00
Tim Kuipers cd170cae99 Revert "cleanup: removed fill_perimeter_gaps setting (CURA-996)"
This reverts commit 9c47644e55.

This reintroduces the fill_perimeter_gaps setting (CURA-2306)
2016-11-11 14:41:33 +01:00
Tim Kuipers d47d0a2e46 fix: handle perimeter_gaps for concentric skin infill (CURA-2306) 2016-11-11 14:37:51 +01:00
Tim Kuipers f0d59db203 refactor: optionally pass down perimeter_gaps to Infill constructor (CURA-2306) 2016-11-11 14:37:14 +01:00
Tim Kuipers f34a4e566b feat: handle perimeter_gaps between skin walls (CURA-2306) 2016-11-11 14:35:43 +01:00
Tim Kuipers 9c1ef177d1 feat: perimeter gaps for normal walls (CURA-2306) 2016-11-11 14:33:45 +01:00
Tim Kuipers e1c7e86b66 fix: optimize skin part order (CURA-2306) 2016-11-11 14:29:32 +01:00
Tim Kuipers 384071aaeb feat: z seam position (CURA-1461) 2016-11-11 12:02:46 +01:00
Tim Kuipers 79d1074d47 feat: cool_fan_speed_0 (CURA-2182) 2016-11-10 16:41:42 +01:00
Tim Kuipers 348ca93dcb fix: add more special casing to ignore (anti)support meshes (CURA-2077) 2016-11-09 17:27:59 +01:00
Tim Kuipers e70b3c099f Merge branch 'fix_support_order' of github.com:Ultimaker/CuraEngine into fix_support_order 2016-11-09 16:40:50 +01:00
Tim Kuipers 8c9858a14c refactor: rewrote logic in FffGcodeWriter::calculateExtruderOrder (CURA-2789) 2016-11-09 16:38:14 +01:00
Tim Kuipers 6e56cb3416 refactor: support_skin ==> support_interface (CURA-2789) 2016-11-09 16:31:43 +01:00
Ghostkeeper 8761ae1e53 Merge branch 'feature_retract_at_layer_change' 2016-11-09 13:37:48 +01:00
Ghostkeeper d32be27b50 Fix typo in assert message
Contributes to issue CURA-2789.
2016-11-09 11:58:48 +01:00
Tim Kuipers 11b33215a2 fix: set extruder count (CURA-2325) 2016-11-09 10:24:31 +01:00
Tim Kuipers e8a31f1380 fix: remove GCodePlanner::makeLastPathZhopped (CURA-2325) 2016-11-09 10:24:31 +01:00
Ghostkeeper 7cbfb22f1f )
'I have to sort my books' she cried,
With self-indulgent glee;
With senseless, narcissistic pride:
'I'm just so OCD.'

'How random guys,' I smiled and said,
Then left without a peep -
And washed my hands until they bled,
And cried myself to sleep.
2016-11-09 10:24:24 +01:00
Tim Kuipers 40e6aac22b fix: only wipe in the middle of the prime tower if it's hollow (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers 33cc601a1b fix: only wipe on inside of prime tower if z hops are already being performed (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers d50b0c9c2f fix: go down on middle of hollow wipe tower (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers 77f415be61 fix: made hollow prime tower robust against zero wall thickness (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers 1aa784e521 doc update prime tower (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers ef8258da2f fix: make move to prime location z-hopped (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers 79692200b0 fix: make move toward hollow prime tower always z hopped (CURA-2325) 2016-11-09 10:24:24 +01:00
Tim Kuipers 966912ccc5 fix: more prime positions cause inside of hollow prime tower is quite big (CURA-2325) 2016-11-09 10:19:15 +01:00
Tim Kuipers 1513dcad5c fix: add brim on inside of hollow prime tower (CURA-2325)
even when we have selected to generate brim only for outside polygons
2016-11-09 10:19:15 +01:00
Tim Kuipers 41050ac835 fix: wipe on middle of prime tower (CURA-2325) 2016-11-09 10:19:15 +01:00
Tim Kuipers fb8625756f fix: return *this for operator= (CURA-2325) 2016-11-09 10:19:15 +01:00
Tim Kuipers 886bab29f7 refactor: cache prime tower walls as well as infill (CURA-2325) 2016-11-09 10:19:15 +01:00
Tim Kuipers 5bdf538d35 lil cleanup prime tower (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers 7d5040e283 feat: hollow prime tower (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers a49ed9a90e cleanup: removed unused var in PrimeTower (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers 7a4e732f3b refactor: pass along new_extruder to wipe tower generator functions (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers 653ce82255 fix: make wipe settings settable per extruder (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers 0b1df81945 feat: separate setting for pre-wipe (CURA-2325) 2016-11-09 10:19:14 +01:00
Tim Kuipers f65993c5b6 fix: put prime_tower_wipe_enabled back to old functionality (CURA-2325) 2016-11-09 10:19:14 +01:00
Ghostkeeper df91b3d8aa Merge branch 'feature_extruder_temp_layer_0' 2016-11-08 14:46:45 +01:00
Tim Kuipers 4b58ab0ad9 refactor/fix: plan extruders independently of meshes and plan support per extruder plan (CURA-2789)
When printing dual color models with dual support (different support infill extruder than support skin extruder)
all support would either be printed before all models or after, leading to two extruder switches in one layer.

Now the extruder_order is calculated independently of the mesh_order and the support is added per extruder plan.

The support is always added first, so that:
- in single extrusion we never print in order support-support-model-model which would lead to model being printed on top of model which hasn't cooled enough
- in dual color, dual support we always print E1-support-model-support-model-E2-support-model-support-model so that oozle is automatically wiped on support
2016-11-08 11:36:28 +01:00
Tim Kuipers 9cbdfd2152 refactor/fix: return whether support was added to extruder plan and only switch extruder if plans were added (CURA-2789) 2016-11-08 11:30:16 +01:00
Tim Kuipers 4a0a0088fe cleanup: remove unused parameter to FffGcodeWriter::addSupportToGCode (CURA-2789) 2016-11-07 17:49:35 +01:00
Ghostkeeper dc37384ffd Merge branch 'bugfix_support_retractions' 2016-11-07 15:40:08 +01:00
Tim Kuipers 2a21e6c348 fix: don't write temperature gcode commands for the UM2 family (CURA-2781, CURA-2736)
they are fixed by the firmware
2016-11-07 15:16:43 +01:00
Tim Kuipers 30011a5285 fix: insert preheat commands even in single extruder mode (CURA-2736) 2016-11-07 13:56:22 +01:00
Tim Kuipers 965b28e009 fix: compiler warnings for unsigned layer_nr and extruder_nr (CURA-2736) 2016-11-07 13:55:22 +01:00
Tim Kuipers 1b37007003 feat: initial layer extruder temp (CURA-2736) 2016-11-07 13:48:48 +01:00
Tim Kuipers 5cf0bcd399 feat: add is_initial_layer to layerplan and extruderplan (CURA-2736) 2016-11-07 13:48:48 +01:00
Tim Kuipers 0930d61dad feat: material_bed_temperature_layer_0 (CURA-2781)
set bed temeprature to normal at first layer
set bed temp to initial layer bed temp the first layer of each next meshgroup (one-at-a-time mode)
wait for initial bed temp to be reached
don't wait for second layer bed temp
2016-11-07 13:48:48 +01:00
Tim Kuipers a181977ce9 refactor: moved command line settings to fdmprinter.def.json (CURA-566)
also fixed some problems:
- type of one setting was string rather than str
- there were some old unused settings in there (prime_tower_outward_dir, machine_prinbt_temp_wait)
2016-11-07 11:59:13 +01:00
Ghostkeeper 3ab19f2f29 Merge branch 'feature_wall_wipe' 2016-11-07 11:37:18 +01:00
mboerwinkle 4b6f672e56 Merge pull request #2 from Ultimaker/feature_recursive_infill_BagelOrb
Some small optimizations and cleanup
2016-11-06 18:23:13 -06:00
Ghostkeeper c4a3830838 Merge branch 'bugfix_support_brim_round' 2016-11-04 15:23:47 +01:00
Jaime van Kessel d546c27462 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-11-04 14:59:57 +01:00
Jaime van Kessel 7664e81aa2 Merge branch 'feature_concentric_3d_infill' of github.com:Ultimaker/CuraEngine 2016-11-04 14:54:07 +01:00
Tim Kuipers aa2ce7c770 refactor: retrieve print_layer_count from SliceDataStore rather than passing it to all functions (CURA-2077) 2016-11-04 14:23:27 +01:00
Tim Kuipers f63fc1ed74 fix: compute layer_count once and for all (CURA-2077) 2016-11-04 14:18:47 +01:00
Tim Kuipers ac183f4e4b refactor: don't pass down total_layers, but use mesh.layers.size instead (CURA-2077) 2016-11-04 13:15:03 +01:00
Tim Kuipers 4f3c337cc7 fix: resize layers vector in all meshes beforehand (CURA-2077)
A lot of (outdated) code depends on there being the same amount of layers in each mesh.
This was not the case for anti-overhang meshes and support meshes
Now each mesh starts out with the same amount of empty layers
2016-11-04 13:14:23 +01:00
Ghostkeeper 1e24769061 Merge branch 'feature_start_layer_far_away' 2016-11-04 11:36:48 +01:00
Tim Kuipers 02134eb822 fix: don't retract while performing the outer wall wipe (CURA-1698) 2016-11-03 20:18:45 +01:00
Tim Kuipers b436da841a fix2: don't access protected member from within lambda function (CURA-2704) 2016-11-03 14:45:58 +01:00
Tim Kuipers fa95e56b76 fix: don't access protected member from within lambda function (CURA-2704) 2016-11-03 14:13:52 +01:00
Ghostkeeper 6bed19c295 Merge branch 'feature_infill_support' 2016-11-03 13:37:07 +01:00
Tim Kuipers 2a20853b92 fix: getExtrudersUsed only include platform adhesion for first layer(s) (CURA-2789) 2016-11-02 17:50:30 +01:00
Tim Kuipers 09ad6301e7 refactor: separate handleSupportBeforeModels out of FffGcodeWriter::addSupportToGCode (CURA-2789) 2016-11-02 16:55:34 +01:00
Tim Kuipers d8fa29e979 cleanup: const-correctness for SliceDataStorage::getExtrudersUsed (CURA-2789) 2016-11-02 16:54:50 +01:00
Tim Kuipers ab91cc2def lil refactor: p0 ==> b_from_transformed (CURA-2704)
to conform to the other parameters such as a_from_transformed
2016-11-02 16:39:31 +01:00
Tim Kuipers cd4e2e29a3 lil refactor: transformed_startPoint ==> transformed_from (CURA-2704) 2016-11-02 16:37:12 +01:00
Tim Kuipers 0f454e897b refactor: polygonCollidesWithlineSegment ==> polygonCollidesWithLineSegment (l==>L) (CURA-2704) 2016-11-02 16:35:33 +01:00
Tim Kuipers c1d53811f3 lil (no unneccesary type cast (CURA-2077) 2016-11-02 16:05:36 +01:00
Tim Kuipers dbe6269262 Revert "refactor: support_mesh ==> overhang_mesh (CURA-2077)"
This reverts commit 00041da2df.
2016-11-02 16:01:08 +01:00
Tim Kuipers 0c395c5bfa fix: wall wipe didn't update last point (CURA-1698) 2016-11-02 15:35:34 +01:00
Tim Kuipers f312f15813 Merge branch 'master' into feature_infill_support 2016-11-01 11:04:13 +01:00
Ghostkeeper 406ea7155c Merge pull request #408 from thopiekar/master-gcc6-fix
Adding <numeric> for GCC 6.x
2016-11-01 10:21:16 +01:00
Thomas Karl Pietrowski f0f23ed732 Adding <numeric> for GCC 6.x
Otherwise you will get something like:

In file included from /<<PKGBUILDDIR>>/src/FffPolygonGenerator.cpp:7:0:
/<<PKGBUILDDIR>>/src/utils/algorithm.h: In function ‘std::vector<long unsigned int> cura::order(const std::vector<_Tp>&)’:
/<<PKGBUILDDIR>>/src/utils/algorithm.h:30:5: error: ‘iota’ is not a member of ‘std’
     std::iota(order.begin(), order.end(), 0); // fill vector with 1, 2, 3,.. etc
     ^~~
2016-11-01 09:48:46 +01:00
Ghostkeeper 0a59a059f4 Merge branch 'feature_nozzle_off_after_use' 2016-10-31 13:31:37 +01:00
Tim Kuipers e902fb3fc9 lil (CURA-2704)
makes for better debugging of the scenario of doom (isinside =/= inside)
2016-10-27 22:55:20 +02:00
Tim Kuipers c7be93e850 test: isinside error I couldn't reproduce (CURA-2704) 2016-10-27 22:53:51 +02:00
Tim Kuipers fe8207fb1b fix: user clipper functions for Polygons::inside (CURA-2704) 2016-10-27 22:53:15 +02:00
Tim Kuipers d1fbe96d6f feat: use LocToLineGrid for moveInside-like functions (CURA-2704) 2016-10-27 17:17:07 +02:00
Tim Kuipers ccc89e6263 refactor: typedef SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator> LocToLineGrid (CURA-2704) 2016-10-27 16:28:41 +02:00
Tim Kuipers d235de6ca2 fix: early stopping for SparseGrid for_each-like functions (CURA-2704) 2016-10-27 15:06:54 +02:00
Tim Kuipers 4d98e07eb4 fix: safety for polygon operator[] (CURA-2704) 2016-10-27 12:22:50 +02:00
Tim Kuipers 7c3f69a5bf bugfixes: combing edge cases (CURA-2704) 2016-10-27 12:22:13 +02:00
Tim Kuipers 846bb1109a fix: combing: create inside_loc_to_line grid after partsView (CURA-2704) 2016-10-27 12:02:48 +02:00
Tim Kuipers 8c5ba49068 fix: more lenient assertions due to rounding errors (CURA-2704) 2016-10-26 18:33:14 +02:00
Tim Kuipers 8e18139ae9 fix: use loc_to_line_grid for optimized combpath optimization (CURA-2704) 2016-10-26 18:32:05 +02:00
Tim Kuipers 410d42ccb3 fix: use inside_loc_to_line grid for checking whether a small move crosses the boundary (CURA-2704) 2016-10-26 18:02:10 +02:00
Tim Kuipers 2c35ba595c refactor: use LazyInitialization for Comb::outside_loc_to_line (CURA-2704) 2016-10-26 17:53:34 +02:00
Tim Kuipers 8f04afbff9 refactor/fix: LazyInitialization: from bind to lambdas (CURA-2704)
also fixes the problem of temporary rvalues being used by the constructor function
2016-10-26 17:51:57 +02:00
Tim Kuipers 1fd5355290 fix: combing: compute whether a small move is cross-boundary (CURA-2704)
That is: whether the move is (party) over the in_between area
2016-10-26 15:43:59 +02:00
Tim Kuipers 65b6c48391 refactor: use LazyInitialization for Comb::boundary_outside (CURA-2704) 2016-10-26 15:41:16 +02:00
Tim Kuipers 34c0960cc2 fix: LazyInitialization constructor with function object (CURA-2704) 2016-10-26 15:39:41 +02:00
Tim Kuipers d6175e8269 feat: LazyInitialization (CURA-2704) 2016-10-26 15:38:42 +02:00
Thomas Karl Pietrowski 47984afb5f Fixing formatting in README.md 2016-10-25 20:08:49 +02:00
Tim Kuipers c86823033d feat: PolygonUtils::polygonCollidesWithlineSegment(.) for a loc_to_line_grid (CURA-2704) 2016-10-25 17:59:19 +02:00
Tim Kuipers 6c157b4e1f fix: made function parameters const in SparseGrid (CURA-2704) 2016-10-25 17:52:32 +02:00
Tim Kuipers 97b8d63547 fix: LinearAlg2D::lineSegmentsCollide now properly handles colinear cases (CURA-2704) 2016-10-25 16:58:28 +02:00
Tim Kuipers f0536be401 refactor: move part of LinearAlg2D::lineSegmentsCollide to LinearAlg2D (CURA-2704) 2016-10-25 16:56:57 +02:00
Tim Kuipers 2c983ce39c feat: SparseGrid::processLine (CURA-2704) 2016-10-25 16:04:38 +02:00
Tim Kuipers a3eb8ebb2d refactor: made SparseGrid function templates into std::function (CURA-2704) 2016-10-25 16:04:01 +02:00
Tim Kuipers bb98cb983e refactor: abstract SparseLineGrid::insert to SparseGrid::processLine (CURA-2704) 2016-10-25 11:56:17 +02:00
Tim Kuipers a67f7465c1 fix: print support consecutive instead of always at end of layer (CURA-2789)
also support_interface_extruder_nr mattered even if the interface was turned off
2016-10-22 16:34:02 +02:00
Tim Kuipers 9b8ef3981a feat: retract_at_layer_change (CURA-2780) 2016-10-22 16:21:20 +02:00
Tim Kuipers 24a22eec82 feat: start_layers_at_same_position (CURA-1112) 2016-10-21 17:45:44 +02:00
Ghostkeeper 4d34cbc66b Merge branch 'bugfix_double_to_stream' 2016-10-21 17:28:58 +02:00
Tim Kuipers 405a2b2a5f fix: allow outer wall wipe to wrap around as many times as requested (CURA-1698) 2016-10-21 17:08:09 +02:00
Tim Kuipers 17260b0272 feat: wall_0_wipe (CURA-1698) 2016-10-21 17:01:40 +02:00
Tim Kuipers 2785baf7be refactor: allow wall_0_wipe distance as parameter to GCodePlanner::addPolygon (CURA-1698) 2016-10-21 17:01:25 +02:00
Tim Kuipers c4bc6b8d19 cleanup: codestyle in GCodePlanner::addPolygon (CURA-1698) 2016-10-21 17:00:24 +02:00
Tim Kuipers a29af7f791 Revert "Revert "feat: setting for carveMultipleVolumes (CURA-2712)""
This reverts commit d50f67e583.

Reapplies b2d837efde
2016-10-21 16:22:45 +02:00
Tim Kuipers f304c09db4 Merge branch '2.3' 2016-10-21 16:22:30 +02:00
Tim Kuipers d50f67e583 Revert "feat: setting for carveMultipleVolumes (CURA-2712)"
This reverts commit b2d837efde.
2016-10-21 16:22:16 +02:00
Tim Kuipers b2d837efde feat: setting for carveMultipleVolumes (CURA-2712) 2016-10-21 16:05:15 +02:00
Ghostkeeper 5c680b312b Indent broken-up lines
So that you can see that the line is broken up into multiple lines.

Contributes to issue CURA-1103.
2016-10-21 15:52:22 +02:00
Ghostkeeper 0a683ff05e Transform order algorithm to our code style
Lukasz needs to obey the rulesz!

Contributes to issue CURA-1103.
2016-10-21 15:39:19 +02:00
Tim Kuipers 0dc6dcbb34 fix: concentric infill doesn't start right next to the inner wall any more (CURA-2772) 2016-10-20 23:22:07 +02:00
Tim Kuipers dbb48c82bf fix: concentric infill doesn't oscilate because of alternate extra wall (CURA-2772)
because the innermost wall was different every layer, the concentric infill was also shifted every layer
2016-10-20 23:13:57 +02:00
Tim Kuipers 2a1e4da930 feat: concentric 3d infill (CURA-2772) 2016-10-20 22:57:31 +02:00
Tim Kuipers dd481bcc2e fix: tetrahedral infill line distance is now the average line distance (CURA-2772)
Because the lines of tetrahedral infill sway toward and away from each other, we need to speak of an average line distance.
However, the line distance when you skip one line is constant; I call that the period.
2016-10-20 22:56:51 +02:00
Tim Kuipers f68d1a4e2d fix: concentric infill copied instead of pass by reference (CURA-2772) 2016-10-20 22:52:51 +02:00
Tim Kuipers 9d56baba41 fix: keep SliceMeshStorage aligned with Mesh (CURA-2077) 2016-10-20 09:38:25 +02:00
Tim Kuipers 4e96d9cbe6 feat: infill_hollow (CURA-2748) 2016-10-19 18:13:38 +02:00
Tim Kuipers 00041da2df refactor: support_mesh ==> overhang_mesh (CURA-2077) 2016-10-19 16:42:50 +02:00
Tim Kuipers 478bd31d02 Merge branch 'bugfix_double_to_stream' of github.com:Ultimaker/CuraEngine into bugfix_double_to_stream 2016-10-18 11:02:37 +02:00
Tim Kuipers faab907bab fix: int64_t and double formatting (CURA-2627) 2016-10-17 17:51:38 +02:00
Tim Kuipers dff554863c fix: nozzle was never turned off (CURA-1103) 2016-10-17 17:25:18 +02:00
Tim Kuipers f2222f97fd fix: PrimeTower::extruder_count wasn't set (CUTA-1103) 2016-10-17 17:20:25 +02:00
Tim Kuipers aa18e7bd08 feat: turn nozzle off after last layer switch (CUTA-1103) 2016-10-17 17:12:15 +02:00
Tim Kuipers 07dc53765a fix: removeEmptyFirstLayers invalidated mesh.layer_nr_max_filled_layer (CURA-1103) 2016-10-17 17:11:37 +02:00
Tim Kuipers 559deb8914 refactor: moved max_print_height_second_to_last_extruder to FffPolygonGenerator (CURA-1103) 2016-10-17 17:10:57 +02:00
Tim Kuipers 421ff6d818 feat: prime tower max print height calculation more generic (CURA-1103)
Also fix for platform adhesion in registering the max height an extruder is used.
2016-10-17 15:32:26 +02:00
Tim Kuipers e85a1004cd feat: algorithm::order function (CURA-1103) 2016-10-17 14:46:07 +02:00
Tim Kuipers 627848bc41 refactor: max_object_height_second_to_last_extruder ==> max_print_height_second_to_last_extruder (CURA-1103) 2016-10-17 11:30:53 +02:00
Ghostkeeper eccc62cf1d Add whitespace around binary operators
As per our code style.

Contributes to issue CURA-2627.
2016-10-17 11:06:22 +02:00
Tim Kuipers 1d251fef70 fix: limit prime tower height when support is touching buildplate only (CURA-2633) 2016-10-15 16:15:18 +02:00
Tim Kuipers 16a0cf0fd5 fix: make first layer brim under support round (CURA-2686) 2016-10-15 15:59:48 +02:00
Tim Kuipers 3c7c352bfc fix: add support during generation, causing all support settings to be applied to support meshes (CURA-2077)
This has the disadvantage that support meshes are re-evaluated for each normal mesh.
However, otherwise support meshes could overlap with each other mesh..
2016-10-14 16:44:50 +02:00
Tim Kuipers 66e6375942 fix: lil typo in AreaSupport::generateSupportAreas for support meshes (CURA-2077) 2016-10-14 16:43:26 +02:00
Tim Kuipers 8d3f66c2cb fix: don't switch extruder if support interface is too small to be printed (CURA-2077) 2016-10-14 16:41:15 +02:00
Tim Kuipers 1fd540c231 fix: also print support of support meshes above normal support (CURA-2077) 2016-10-14 16:00:45 +02:00
Tim Kuipers ac799dd00e fix: don't do multi volumes functions on support meshes (CURA-2077) 2016-10-14 16:00:22 +02:00
Tim Kuipers 699406b044 refactor: anti_support_mesh ==> anti_overhang_mesh (CURA-2077) 2016-10-14 15:31:53 +02:00
Tim Kuipers 2f3333e87c feat: anti support meshes (CURA-2077) 2016-10-14 15:30:19 +02:00
Tim Kuipers 58e2e1a4e1 fix: better buffer for double to stream and more tests (CURA-2627) 2016-10-13 17:39:59 +02:00
106 arquivos alterados com 4925 adições e 2374 exclusões
+6
Ver Arquivo
@@ -7,8 +7,14 @@
NUL
*.gcode
## Directories used for other stuff
Trash/*
output/*
callgrind/*
## Building result.
build/*
debug_build/*
*.pyc
*.exe
*.a
+16 -1
Ver Arquivo
@@ -42,6 +42,16 @@ if(NOT APPLE AND NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
endif()
option (ENABLE_OPENMP
"Use OpenMP for parallel code" ON)
if (ENABLE_OPENMP)
FIND_PACKAGE( OpenMP )
if( OPENMP_FOUND )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" )
endif()
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
add_library(clipper STATIC libs/clipper/clipper.cpp)
@@ -66,6 +76,7 @@ set(engine_SRCS # Except main.cpp.
src/MeshGroup.cpp
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/Preheat.cpp
src/PrimeTower.cpp
src/raft.cpp
src/skin.cpp
@@ -84,14 +95,18 @@ set(engine_SRCS # Except main.cpp.
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/infill/subDivCube.cpp
src/infill/SubDivCube.cpp
src/pathPlanning/Comb.cpp
src/pathPlanning/GCodePath.cpp
src/pathPlanning/LinePolygonsCrossings.cpp
src/pathPlanning/NozzleTempInsert.cpp
src/pathPlanning/TimeMaterialEstimates.cpp
src/progress/Progress.cpp
src/progress/ProgressStageEstimator.cpp
src/settings/PathConfigStorage.cpp
src/settings/SettingConfig.cpp
src/settings/SettingContainer.cpp
src/settings/SettingRegistry.cpp
+1 -1
Ver Arquivo
@@ -832,7 +832,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = documentation/assets
IMAGE_PATH = docs/assets
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
+1
Ver Arquivo
@@ -31,6 +31,7 @@ CMake compilation:
4. ```$ make```
Project files generation:
1. Navigate to the CuraEngine directory and execute the following commands
2. ```cmake . -G "CodeBlocks - Unix Makefiles"```
3. (for a list of supported IDE's see http://www.cmake.org/Wiki/CMake_Generator_Specific_Information#Code::Blocks_Generator)

Antes

Largura:  |  Altura:  |  Tamanho: 18 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 18 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 70 KiB

Antes

Largura:  |  Altura:  |  Tamanho: 20 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 20 KiB

-1
Ver Arquivo
@@ -1 +0,0 @@
html/index.html
-58
Ver Arquivo
@@ -1,58 +0,0 @@
{
"version": 2,
"name": "Command line setting defaults CuraEngine",
"metadata":
{
"author": "Ultimaker B.V."
},
"settings": {
"command_line_settings": {
"label": "Command Line Settings",
"type": "category",
"children": {
"center_object": {
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
"type": "bool",
"label": "Center object",
"default_value": true
},
"machine_print_temp_wait": {
"description": "Whether to wait for the nozzle temperature to be reached when preheating the nozzles at the start of the gcode.",
"type": "bool",
"label": "Machine print temp wait",
"default_value": true
},
"mesh_position_x": {
"description": "Offset applied to the object in the x direction.",
"type": "float",
"label": "Mesh position x",
"default_value": 0
},
"mesh_position_y": {
"description": "Offset applied to the object in the y direction.",
"type": "float",
"label": "Mesh position y",
"default_value": 0
},
"mesh_position_z": {
"description": "Offset applied to the object in the z direction. With this you can perform what was used to call 'Object Sink'.",
"type": "float",
"label": "Mesh position z",
"default_value": 0
},
"mesh_rotation_matrix": {
"label": "Mesh Rotation Matrix",
"description": "Transformation matrix to be applied to the model when loading it from file.",
"type": "string",
"default_value": "[[1,0,0], [0,1,0], [0,0,1]]"
},
"prime_tower_dir_outward": {
"description": "Whether to start printing in the middle of the prime tower and end up at the perimeter, or the other way around. This is only used for certain types of prime tower.",
"type": "bool",
"label": "Prime tower direction outward",
"default_value": false
}
}
}
}
}
+1
Ver Arquivo
@@ -11,6 +11,7 @@ struct FanSpeedLayerTimeSettings
public:
double cool_min_layer_time;
double cool_min_layer_time_fan_speed_max;
double cool_fan_speed_0;
double cool_fan_speed_min;
double cool_fan_speed_max;
double cool_min_speed;
+542 -323
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+109 -49
Ver Arquivo
@@ -65,20 +65,29 @@ private:
*/
bool skirt_brim_is_processed[MAX_EXTRUDERS];
std::vector<std::vector<unsigned int>> mesh_order_per_extruder; //!< For each extruder, the cyclic order of the meshes (the first element is not the starting element per se)
/*!
* For each extruder whether priming has already been planned
*/
bool extruder_prime_is_planned[MAX_EXTRUDERS];
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder; //!< The settings used relating to minimal layer time and fan speeds. Configured for each extruder.
Point last_position_planned; //!< The position of the head before planning the next layer
int current_extruder_planned; //!< The extruder train in use before planning the next layer
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
GCodePlanner::PlanningState planner_state;
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, max_object_height(0)
, layer_plan_buffer(this, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // changed somewhere early in FffGcodeWriter::writeGCode
, is_inside_mesh_layer_part(false)
, extruder_prime_is_planned {} // initialize all values in array with [false]
, planner_state{ no_point
, 0 // changed somewhere early in FffGcodeWriter::writeGCode
, false
}
{
max_object_height = 0;
}
/*!
@@ -164,33 +173,33 @@ private:
* \param[out] storage The data storage to which to save the configurations
*/
void setConfigRetraction(SliceDataStorage& storage);
/*!
* Initialize the GcodePathConfig config parameters which don't change over
* all layers, for each feature.
* Get the extruder with which to start the print.
*
* The features are: skirt or brim, support and for each mesh: outer wall,
* inner walls, skin, infill (and combined infill).
* Generally this is the adhesion_extruder_nr, but in case the platform adhesion type is none,
* the extruder with lowest number which is used on the first layer is used as initial extruder.
*
* \param[out] storage The data storage to which to save the configurations.
* \param[in] storage where to get settings from.
*/
void initConfigs(SliceDataStorage& storage);
unsigned int getStartExtruder(const SliceDataStorage& storage);
/*!
* Set temperatures and perform initial priming.
*
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
*
* \param[in] storage where the slice data is stored.
* \param[in] start_extruder_nr The extruder with which to start the print.
*/
void processStartingCode(SliceDataStorage& storage);
void processStartingCode(const SliceDataStorage& storage, const unsigned int start_extruder_nr);
/*!
* Move up and over the already printed meshgroups to print the next meshgroup.
*
* \param[in] storage where the slice data is stored.
*/
void processNextMeshGroupCode(SliceDataStorage& storage);
void processNextMeshGroupCode(const SliceDataStorage& storage);
/*!
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
@@ -198,7 +207,7 @@ private:
* \param[in,out] storage where the slice data is stored.
* \param total_layers The total number of layers.
*/
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
void processRaft(const SliceDataStorage& storage, unsigned int total_layers);
/*!
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
@@ -209,9 +218,25 @@ private:
* \param[in] storage 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.
* \return The layer plans
*/
void processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers);
GCodePlanner& processLayer(const SliceDataStorage& storage, int layer_nr, unsigned int total_layers) const;
/*!
* Whether the extruders need to be primed separately just before they are used.
*
* \return whether the extruders need to be primed separately just before they are used
*/
bool getExtrudersNeedPrimeDuringFirstLayer() const;
/*!
* Plan priming of all used extruders which haven't been primed yet
* \param[in] storage where the slice data is stored.
* \param layer_plan The initial planning of the g-code of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void ensureAllExtrudersArePrimed(const SliceDataStorage& storage, GCodePlanner& layer_plan, const int layer_nr) const;
/*!
* Add the skirt or the brim to the layer plan \p gcodeLayer.
*
@@ -220,7 +245,7 @@ private:
* \param extruder_nr The extruder train for which to process the skirt or
* brim.
*/
void processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
void processSkirtBrim(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr) const;
/*!
* Adds the ooze shield to the layer plan \p gcodeLayer.
@@ -229,7 +254,7 @@ private:
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
void processOozeShield(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr) const;
/*!
* Adds the draft protection screen to the layer plan \p gcodeLayer.
@@ -238,120 +263,155 @@ private:
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
void processDraftShield(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr) const;
/*!
* Calculate in which order to print the meshes.
* Calculate in which order to plan the extruders
*
* \param[in] storage where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of mesh indices ordered on print order.
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
std::vector<int> calculateExtruderOrder(const SliceDataStorage& storage, int current_extruder) const;
/*!
* Calculate in which order to plan the meshes of a specific extruder
*
* \param[in] storage where the slice data is stored.
* \param extruder_nr The extruder for which to determine the order
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<unsigned int> calculateMeshOrder(const SliceDataStorage& storage, int extruder_nr) const;
/*!
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \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);
void addMeshLayerToGCode_meshSurfaceMode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcodeLayer, int layer_nr) const;
/*!
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \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);
void addMeshOpenPolyLinesToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const;
/*!
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer.
* Add a single layer from a single mesh-volume to the layer plan \p gcode_layer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param mesh_config the line config with which to print a print feature
* \param gcode_layer 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);
void addMeshLayerToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, GCodePlanner& gcode_layer, int layer_nr) const;
/*!
* Add a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param mesh_config the line config with which to print a print feature
* \param part The part to add
* \param gcode_layer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshPartToGCode(const SliceDataStorage& storage, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr) const;
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \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 distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
void processMultiLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle) const;
/*!
* 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 layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \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 distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
void processSingleLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle) const;
/*!
* 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 layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \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
* \param z_seam_pos The location near where to start the outer inset in case \p z_seam_type is 'back'
*/
void processInsets(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type);
void processInsets(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos) const;
/*!
* Add the gcode of the top/bottom skin of the given part.
* Add the gcode of the top/bottom skin of the given part and of the perimeter gaps.
*
* Perimter gaps are generated for skin outlines and printed while the skin fill of the skin part is printed.
* Perimeter gaps between the walls are added to the gcode afterwards.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param mesh_config the line config with which to print a print feature
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param skin_overlap The distance by which the skin overlaps with the wall insets.
* \param skin_overlap The distance by which the skin overlaps with the wall insets and the distance by which the perimeter gaps overlap with adjacent print features.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
void processSkinAndPerimeterGaps(GCodePlanner& gcode_layer, const SliceMeshStorage* mesh, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle) const;
/*!
* Add the support to the layer plan \p gcodeLayer of the current layer.
* Add the support to the layer plan \p gcodeLayer of the current layer for all support parts with the given \p extruder_nr.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, or after.
* \return whether any support was added to the layer plan
*/
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
bool addSupportToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr) const;
/*!
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support infill was added to the layer plan
*/
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
bool addSupportInfillToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr) const;
/*!
* Add the support skins to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support skin was added to the layer plan
*/
void addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
bool addSupportRoofsToGCode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr) const;
/*!
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
@@ -363,7 +423,7 @@ private:
* \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);
void setExtruder_addPrime(const SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr) const;
/*!
* Add the prime tower gcode for the current layer.
@@ -372,7 +432,7 @@ private:
* \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);
void addPrimeTower(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder) const;
/*!
* Add the end gcode and set all temperatures to zero.
+221 -56
Ver Arquivo
@@ -2,8 +2,10 @@
#include <algorithm>
#include <map> // multimap (ordered map allowing duplicate keys)
#include <omp.h>
#include "utils/math.h"
#include "utils/algorithm.h"
#include "slicer.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
@@ -52,7 +54,7 @@ unsigned int FffPolygonGenerator::getDraftShieldLayerCount(const unsigned int to
case DraftShieldHeightLimitation::FULL:
return total_layers;
case DraftShieldHeightLimitation::LIMITED:
return std::max(0, (getSettingInMicrons("draft_shield_height") - getSettingInMicrons("layer_height_0")) / getSettingInMicrons("layer_height") + 1);
return std::max((coord_t)0, (getSettingInMicrons("draft_shield_height") - getSettingInMicrons("layer_height_0")) / getSettingInMicrons("layer_height") + 1);
}
}
@@ -107,7 +109,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("conical_overhang_enabled"))
if (mesh.getSettingBoolean("conical_overhang_enabled") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
}
@@ -115,16 +117,62 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
carveMultipleVolumes(slicerList);
if (storage.getSettingBoolean("carve_multiple_volumes"))
{
carveMultipleVolumes(slicerList, storage.getSettingBoolean("alternate_carve_order"));
}
generateMultipleVolumesOverlap(slicerList);
storage.print_layer_count = 0;
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
Slicer* slicer = slicerList[meshIdx];
if (!mesh.getSettingBoolean("anti_overhang_mesh") && !mesh.getSettingBoolean("infill_mesh"))
{
storage.print_layer_count = std::max(storage.print_layer_count, (unsigned int)slicer->layers.size());
}
}
storage.support.supportLayers.resize(storage.print_layer_count);
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++)
{
Slicer* slicer = slicerList[meshIdx];
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("anti_overhang_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.anti_overhang = support_layer.anti_overhang.unionPolygons(slicer_layer.polygons);
meshStorage.layers[layer_nr].printZ =
slicer_layer.z
+ getSettingInMicrons("layer_height_0")
- initial_slice_z;
}
continue;
}
if (mesh.getSettingBoolean("support_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.support_mesh.add(slicer_layer.polygons);
meshStorage.layers[layer_nr].printZ =
slicer_layer.z
+ getSettingInMicrons("layer_height_0")
- initial_slice_z;
}
continue;
}
createLayerParts(meshStorage, slicer, mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
@@ -168,7 +216,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
unsigned int slice_layer_count = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
slice_layer_count = std::max<unsigned int>(slice_layer_count, mesh.layers.size());
}
@@ -197,11 +245,10 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
}
for (unsigned int mesh_order_idx(0); mesh_order_idx < mesh_order.size(); ++mesh_order_idx)
{
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, slice_layer_count, inset_skin_progress_estimate);
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_order_idx + 1, storage.meshes.size());
}
unsigned int print_layer_count = 0;
for (unsigned int layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
{
SliceLayer* layer = nullptr;
@@ -211,7 +258,6 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
if (int(layer_nr) <= mesh.layer_nr_max_filled_layer)
{
layer = &mesh.layers[layer_nr];
print_layer_count = layer_nr + 1;
break;
}
}
@@ -224,21 +270,21 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
}
}
log("Layer count: %i\n", print_layer_count);
log("Layer count: %i\n", storage.print_layer_count);
//layerparts2HTML(storage, "output/output.html");
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, print_layer_count);
AreaSupport::generateSupportAreas(storage, storage.print_layer_count);
// we need to remove empty layers after we have procesed the insets
// processInsets might throw away parts if they have no wall at all (cause it doesn't fit)
// brim depends on the first layer not being empty
// only remove empty layers if we haven't generate support, because then support was added underneath the model.
// for some materials it's better to print on support than on the buildplate.
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), print_layer_count); // changes total_layers!
if (print_layer_count == 0)
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), storage.print_layer_count); // changes storage.print_layer_count!
if (storage.print_layer_count == 0)
{
log("Stopping process because there are no non-empty layers.\n");
return;
@@ -247,7 +293,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
for (unsigned int layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr"));
@@ -256,15 +302,17 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
}
*/
computePrintHeightStatistics(storage);
// handle helpers
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, print_layer_count);
storage.primeTower.generatePaths(storage);
storage.primeTower.subtractFromSupport(storage);
logDebug("Processing ooze shield\n");
processOozeShield(storage);
logDebug("Processing draft shield\n");
processDraftShield(storage, print_layer_count);
processDraftShield(storage);
logDebug("Processing platform adhesion\n");
processPlatformAdhesion(storage);
@@ -272,17 +320,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
processDerivedWallsSkinInfill(mesh, print_layer_count);
processDerivedWallsSkinInfill(mesh);
}
}
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate)
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
size_t mesh_layer_count = mesh.layers.size();
if (mesh.getSettingBoolean("infill_mesh"))
{
processInfillMesh(storage, mesh_order_idx, mesh_order, total_layers);
processInfillMesh(storage, mesh_order_idx, mesh_order);
}
// TODO: make progress more accurate!!
@@ -292,20 +341,32 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
inset_skin_progress_estimate.nextStage(mesh_inset_skin_progress_estimator); // the stage of this function call
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(total_layers);
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(inset_estimator);
// walls
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
unsigned int processed_layer_count = 0;
#pragma omp parallel for default(none) shared(mesh_layer_count, mesh, inset_skin_progress_estimate, processed_layer_count) schedule(dynamic)
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
logDebug("Processing insets for layer %i of %i\n", layer_number, total_layers);
logDebug("Processing insets for layer %i of %i\n", layer_number, mesh_layer_count);
processInsets(mesh, layer_number);
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
#ifdef _OPENMP
if (omp_get_thread_num() == 0)
#endif
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
int _processed_layer_count;
#pragma omp atomic read
_processed_layer_count = processed_layer_count;
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
#pragma omp atomic
processed_layer_count++;
}
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(total_layers);
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
bool process_infill = mesh.getSettingInMicrons("infill_line_distance") > 0;
@@ -333,19 +394,36 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
{
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++)
processed_layer_count = 0;
#pragma omp parallel default(none) shared(mesh_layer_count, mesh, mesh_max_bottom_layer_count, process_infill, inset_skin_progress_estimate, processed_layer_count)
{
logDebug("Processing skins and infill layer %i of %i\n", layer_number, total_layers);
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
#pragma omp for schedule(dynamic)
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
processSkinsAndInfill(mesh, layer_number, process_infill);
logDebug("Processing skins and infill layer %i of %i\n", layer_number, mesh_layer_count);
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(mesh, layer_number, process_infill);
}
#ifdef _OPENMP
if (omp_get_thread_num() == 0)
#endif
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
int _processed_layer_count;
#pragma omp atomic read
_processed_layer_count = processed_layer_count;
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
#pragma omp atomic
processed_layer_count++;
}
}
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
}
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
@@ -416,18 +494,19 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers)
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
//SubDivCube Pre-compute Octree
if(mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV){
if (mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV)
{
SubDivCube::precomputeOctree(mesh);
}
// combine infill
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), 1))); //How many infill layers to combine to obtain the requested sparse thickness.
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1))); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
// fuzzy skin
@@ -437,18 +516,26 @@ void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh,
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* processInsets only reads and writes data for the current layer
*/
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && ((layer_nr % 2) + 2) % 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;
{
inset_count += ((layer_nr % 2) + 2) % 2;
}
bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable");
WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall);
walls_computation.generateInsets(layer);
@@ -457,25 +544,36 @@ void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int lay
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers)
{
// only remove empty layers if we haven't generate support, because then support was added underneath the model.
// for some materials it's better to print on support than on the buildplate.
if (storage.support.generated)
{
return; // the first layer will have support and therefore not be empty
}
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)
if (storage.support.generated && layer_idx < storage.support.supportLayers.size())
{
SliceLayer& layer = mesh.layers[layer_idx];
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
if (support_layer.supportAreas.size() > 0 || support_layer.skin.size() > 0)
{
layer_is_empty = false;
break;
}
}
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_idx];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0)
{
layer_is_empty = false;
break;
}
for (const SliceLayerPart& part : layer.parts)
{
if (part.print_outline.size() > 0)
{
layer_is_empty = false;
break;
}
}
}
if (layer_is_empty)
{
@@ -497,12 +595,26 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, cons
{
layer.printZ -= n_empty_first_layers * layer_height;
}
mesh.layer_nr_max_filled_layer -= n_empty_first_layers;
}
total_layers -= n_empty_first_layers;
storage.support.layer_nr_max_filled_layer -= n_empty_first_layers;
std::vector<SupportLayer>& support_layers = storage.support.supportLayers;
support_layers.erase(support_layers.begin(), support_layers.begin() + n_empty_first_layers);
}
}
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateSkins read (depend on) data from mesh.layers[*].parts[*].insets and write mesh.layers[n].parts[*].skin_parts
* generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area
*
* processSkinsAndInfill read (depend on) mesh.layers[*].parts[*].{insets,boundingBox}.
* write mesh.layers[n].parts[*].{skin_parts,infill_area}.
*/
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
@@ -526,6 +638,57 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned
}
}
void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage)
{
int extruder_count = storage.meshgroup->getExtruderCount();
std::vector<int>& max_print_height_per_extruder = storage.max_print_height_per_extruder;
assert(max_print_height_per_extruder.size() == 0 && "storage.max_print_height_per_extruder shouldn't have been initialized yet!");
max_print_height_per_extruder.resize(extruder_count, -1); //Initialize all as -1.
{ // compute max_object_height_per_extruder
//Height of the meshes themselves.
for (SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("anti_overhang_mesh") || mesh.getSettingBoolean("support_mesh"))
{
continue; //Special type of mesh that doesn't get printed.
}
const unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_print_height_per_extruder[extr_nr] = std::max(max_print_height_per_extruder[extr_nr], mesh.layer_nr_max_filled_layer);
}
//Height of where the support reaches.
const unsigned int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_print_height_per_extruder[support_infill_extruder_nr] =
std::max(max_print_height_per_extruder[support_infill_extruder_nr],
storage.support.layer_nr_max_filled_layer);
const unsigned int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr"); // TODO: support skin extruder should be configurable per object
max_print_height_per_extruder[support_skin_extruder_nr] =
std::max(max_print_height_per_extruder[support_skin_extruder_nr],
storage.support.layer_nr_max_filled_layer);
//Height of where the platform adhesion reaches.
if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
max_print_height_per_extruder[adhesion_extruder_nr] =
std::max(0, max_print_height_per_extruder[adhesion_extruder_nr]);
}
}
storage.max_print_height_order = order(max_print_height_per_extruder);
if (extruder_count >= 2)
{
int second_highest_extruder = storage.max_print_height_order[extruder_count - 2];
storage.max_print_height_second_to_last_extruder = max_print_height_per_extruder[second_highest_extruder];
}
else
{
storage.max_print_height_second_to_last_extruder = -1;
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
{
if (!getSettingBoolean("ooze_shield_enabled"))
@@ -535,7 +698,7 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
const int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
for (int layer_nr = 0; layer_nr <= storage.max_object_height_second_to_last_extruder; layer_nr++)
for (int layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist, ClipperLib::jtRound));
}
@@ -544,26 +707,26 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
if (angle <= 89)
{
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height"); // Allow for a 60deg angle in the oozeShield.
for (int layer_nr = 1; layer_nr <= storage.max_object_height_second_to_last_extruder; layer_nr++)
for (int layer_nr = 1; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr - 1].offset(-allowed_angle_offset));
}
for (int layer_nr = storage.max_object_height_second_to_last_extruder; layer_nr > 0; layer_nr--)
for (int layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr > 0; layer_nr--)
{
storage.oozeShield[layer_nr - 1] = storage.oozeShield[layer_nr - 1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
const float largest_printed_area = 1.0; // TODO: make var a parameter, and perhaps even a setting?
for (int layer_nr = 0; layer_nr <= storage.max_object_height_second_to_last_extruder; layer_nr++)
for (int layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr].removeSmallAreas(largest_printed_area);
}
}
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage)
{
const unsigned int draft_shield_layers = getDraftShieldLayerCount(total_layers);
const unsigned int draft_shield_layers = getDraftShieldLayerCount(storage.print_layer_count);
if (draft_shield_layers <= 0)
{
return;
@@ -573,7 +736,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
const unsigned 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 < draft_shield_layers; layer_nr += layer_skip)
for (unsigned int layer_nr = 0; layer_nr < storage.print_layer_count && layer_nr < draft_shield_layers; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
@@ -599,6 +762,8 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
case EPlatformAdhesion::RAFT:
Raft::generate(storage, train->getSettingInMicrons("raft_margin"));
break;
case EPlatformAdhesion::NONE:
break;
}
}
+12 -9
Ver Arquivo
@@ -84,10 +84,9 @@ private:
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed (used for infill meshes)
* \param total_layers The total number of layers over all objects
* \param inset_skin_progress_estimate The progress stage estimate calculator
*/
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate);
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
@@ -95,18 +94,16 @@ private:
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed
* \param total_layers The total number of layers over all objects
*/
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers);
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order);
/*!
* Process features which are derived from the basic walls, skin, and infill:
* fuzzy skin, infill combine
*
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers over all objects
*/
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers);
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh);
/*!
* Remove all bottom layers which are empty.
@@ -118,7 +115,14 @@ private:
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers);
/*!
* Set \ref SliceDataStorage::max_print_height_per_extruder and \ref SliceDataStorage::max_print_height_order and \ref SliceDataStorage::max_print_height_second_to_last_extruder
*
* \param[in,out] storage Where to retrieve mesh and support etc settings from and where the print height statistics are saved.
*/
void computePrintHeightStatistics(SliceDataStorage& storage);
/*!
* Generate the inset polygons which form the walls.
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
@@ -144,9 +148,8 @@ private:
* 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);
void processDraftShield(SliceDataStorage& storage);
/*!
* Generate the skirt/brim/raft areas/insets.
+1 -1
Ver Arquivo
@@ -60,7 +60,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
bool empty = true;
for (Mesh& mesh : meshgroup->meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
empty = false;
}
+33 -64
Ver Arquivo
@@ -6,105 +6,74 @@
namespace cura
{
GCodePathConfig::BasicConfig::BasicConfig()
: speed(0)
, acceleration(0)
, jerk(0)
, line_width(0)
, flow(100)
GCodePathConfig::GCodePathConfig(const GCodePathConfig& other)
: type(other.type)
, speed_derivatives(other.speed_derivatives)
, line_width(other.line_width)
, layer_thickness(other.layer_thickness)
, flow(other.flow)
, extrusion_mm3_per_mm(other.extrusion_mm3_per_mm)
{
}
GCodePathConfig::BasicConfig::BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow)
: speed(speed)
, acceleration(acceleration)
, jerk(jerk)
GCodePathConfig::GCodePathConfig(PrintFeatureType type, int line_width, int layer_height, double flow, GCodePathConfig::SpeedDerivatives speed_derivatives)
: type(type)
, speed_derivatives(speed_derivatives)
, line_width(line_width)
, layer_thickness(layer_height)
, flow(flow)
, extrusion_mm3_per_mm(calculateExtrusion())
{
}
void GCodePathConfig::BasicConfig::set(double speed, double acceleration, double jerk, int line_width, double flow)
void GCodePathConfig::smoothSpeed(GCodePathConfig::SpeedDerivatives first_layer_config, int layer_nr, int max_speed_layer_nr)
{
this->speed = speed;
this->acceleration = acceleration;
this->jerk = jerk;
this->line_width = line_width;
this->flow = flow;
double max_speed_layer = max_speed_layer_nr;
speed_derivatives.speed = (speed_derivatives.speed * layer_nr) / max_speed_layer + (first_layer_config.speed * (max_speed_layer - layer_nr) / max_speed_layer);
speed_derivatives.acceleration = (speed_derivatives.acceleration * layer_nr) / max_speed_layer + (first_layer_config.acceleration * (max_speed_layer - layer_nr) / max_speed_layer);
speed_derivatives.jerk = (speed_derivatives.jerk * layer_nr) / max_speed_layer + (first_layer_config.jerk * (max_speed_layer - layer_nr) / max_speed_layer);
}
GCodePathConfig::GCodePathConfig(PrintFeatureType type)
: extrusion_mm3_per_mm(0.0)
, type(type)
{
}
void GCodePathConfig::init(double speed, double acceleration, double jerk, int line_width, double flow)
{
iconic_config.set(speed, acceleration, jerk, line_width, flow);
current_config = iconic_config;
}
void GCodePathConfig::setLayerHeight(int layer_height)
{
this->layer_thickness = layer_height;
calculateExtrusion();
}
void GCodePathConfig::smoothSpeed(GCodePathConfig::BasicConfig first_layer_config, int layer_nr, double max_speed_layer)
{
current_config.speed = (iconic_config.speed * layer_nr) / max_speed_layer + (first_layer_config.speed * (max_speed_layer - layer_nr) / max_speed_layer);
current_config.acceleration = (iconic_config.acceleration * layer_nr) / max_speed_layer + (first_layer_config.acceleration * (max_speed_layer - layer_nr) / max_speed_layer);
current_config.jerk = (iconic_config.jerk * layer_nr) / max_speed_layer + (first_layer_config.jerk * (max_speed_layer - layer_nr) / max_speed_layer);
}
void GCodePathConfig::setSpeedIconic()
{
current_config.speed = iconic_config.speed;
current_config.acceleration = iconic_config.acceleration;
current_config.jerk = iconic_config.jerk;
}
double GCodePathConfig::getExtrusionMM3perMM()
double GCodePathConfig::getExtrusionMM3perMM() const
{
return extrusion_mm3_per_mm;
}
double GCodePathConfig::getSpeed()
double GCodePathConfig::getSpeed() const
{
return current_config.speed;
return speed_derivatives.speed;
}
double GCodePathConfig::getAcceleration()
double GCodePathConfig::getAcceleration() const
{
return current_config.acceleration;
return speed_derivatives.acceleration;
}
double GCodePathConfig::getJerk()
double GCodePathConfig::getJerk() const
{
return current_config.jerk;
return speed_derivatives.jerk;
}
int GCodePathConfig::getLineWidth()
int GCodePathConfig::getLineWidth() const
{
return current_config.line_width;
return line_width;
}
bool GCodePathConfig::isTravelPath()
bool GCodePathConfig::isTravelPath() const
{
return current_config.line_width == 0;
return line_width == 0;
}
double GCodePathConfig::getFlowPercentage()
double GCodePathConfig::getFlowPercentage() const
{
return current_config.flow;
return flow;
}
void GCodePathConfig::calculateExtrusion()
double GCodePathConfig::calculateExtrusion() const
{
extrusion_mm3_per_mm = INT2MM(current_config.line_width) * INT2MM(layer_thickness) * double(current_config.flow) / 100.0;
return INT2MM(line_width) * INT2MM(layer_thickness) * double(flow) / 100.0;
}
+24 -48
Ver Arquivo
@@ -16,94 +16,70 @@ class GCodePathConfig
friend class GCodePlannerTest;
public:
/*!
* The path config settings which may change from layer to layer
* A simple wrapper class for all derivatives of position which are used when printing a line
*/
struct BasicConfig
struct SpeedDerivatives
{
double speed; //!< movement speed (mm/s)
double acceleration; //!< acceleration of head movement (mm/s^2)
double jerk; //!< jerk of the head movement (around stand still) (mm/s^3)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow modifier in %
BasicConfig(); //!< basic contructor initializing with inaccurate values
BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow); //!< basic contructor initializing all values
void set(double speed, double acceleration, double jerk, int line_width, double flow); //!< Set all config values
double jerk; //!< jerk of the head movement (around stand still) as instantaneous speed change (mm/s)
};
private:
BasicConfig iconic_config; //!< The basic path configuration iconic to this print feature type
BasicConfig current_config; //!< The current path configuration for the current layer
int layer_thickness; //!< current layer height in micron
double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed
public:
const PrintFeatureType type; //!< name of the feature type
private:
SpeedDerivatives speed_derivatives; //!< The speed settings (and acceleration and jerk) of the extruded line. May be changed when smoothSpeed is called.
const int line_width; //!< width of the line extruded
const int layer_thickness; //!< current layer height in micron
const double flow; //!< extrusion flow modifier in %
const double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed
public:
GCodePathConfig(PrintFeatureType type, int line_width, int layer_height, double flow, SpeedDerivatives speed_derivatives); // , SpeedDerivatives slowdown_speed_derivatives, int layer_nr, int max_speed_layer_nr);
/*!
* Basic constructor.
* copy constructor
*/
GCodePathConfig(PrintFeatureType type);
/*!
* Initialize some of the member variables.
*
* \warning GCodePathConfig::setLayerHeight still has to be called before this object can be used.
*
* \param speed The regular speed with which to print this feature
* \param line_width The line width for this feature
* \param flow The flow modifier to apply to the extruded filament when printing this feature
*/
void init(double speed, double acceleration, double jerk, int line_width, double flow);
/*!
* Set the layer height and (re)compute the extrusion_per_mm
*/
void setLayerHeight(int layer_height);
GCodePathConfig(const GCodePathConfig& other);
/*!
* Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed.
*
* \warning This functions should not be called with @p layer_nr > @p max_speed_layer !
*
* \warning Calling this function twice will smooth the speed more toward \p first_layer_config
*
* \param first_layer_config The speed settings 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(BasicConfig first_layer_config, int layer_nr, double max_speed_layer);
/*!
* Set the speed config to the iconic speed config, i.e. the normal speed of the feature type for which this is a config.
*
* Does the same for acceleration and jerk.
*/
void setSpeedIconic();
void smoothSpeed(SpeedDerivatives first_layer_config, int layer_nr, int max_speed_layer);
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM();
double getExtrusionMM3perMM() const;
/*!
* Get the movement speed in mm/s
*/
double getSpeed();
double getSpeed() const;
/*!
* Get the current acceleration of this config
*/
double getAcceleration();
double getAcceleration() const;
/*!
* Get the current jerk of this config
*/
double getJerk();
double getJerk() const;
int getLineWidth();
int getLineWidth() const;
bool isTravelPath();
bool isTravelPath() const;
double getFlowPercentage();
double getFlowPercentage() const;
private:
void calculateExtrusion();
double calculateExtrusion() const;
};
+196 -39
Ver Arquivo
@@ -13,11 +13,11 @@ void LayerPlanBuffer::flush()
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the very last layer
insertTempCommands(); // insert preheat commands of the very last layer
}
while (!buffer.empty())
{
buffer.front().writeGCode(gcode);
buffer.front()->writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
@@ -38,41 +38,51 @@ void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, d
if (acc_time > time_after_extruder_plan_start)
{
const double time_before_path_end = acc_time - time_after_extruder_plan_start;
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, time_this_path - time_before_path_end);
bool wait = false;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait, time_this_path - time_before_path_end);
return;
}
}
extruder_plan_before.insertCommand(0, extruder, temp, false); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
bool wait = false;
unsigned int path_idx = 0;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
}
Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
double initial_print_temp = extruder_plan.initial_printing_temperature;
double in_between_time = 0.0;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{ // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_before_idx];
if (extruder_plan.extruder == extruder)
ExtruderPlan& extruder_plan_before = *extruder_plans[extruder_plan_before_idx];
if (extruder_plan_before.extruder == extruder)
{
Preheat::WarmUpResult warm_up = preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
double temp_before = preheat_config.getFinalPrintTemp(extruder);
if (temp_before == 0)
{
temp_before = extruder_plan_before.printing_temperature;
}
constexpr bool during_printing = false;
Preheat::WarmUpResult warm_up = preheat_config.getWarmUpPointAfterCoolDown(in_between_time, extruder, temp_before, preheat_config.getStandbyTemp(extruder), initial_print_temp, during_printing);
warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + extra_preheat_time);
return warm_up;
}
in_between_time += extruder_plan.estimates.getTotalTime();
in_between_time += extruder_plan_before.estimates.getTotalTime();
}
// The last extruder plan with the same extruder falls outside of the buffer
// assume the nozzle has cooled down to strandby temperature already.
Preheat::WarmUpResult warm_up;
warm_up.total_time_window = in_between_time;
warm_up.lowest_temperature = preheat_config.getStandbyTemp(extruder);
warm_up.heating_time = preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(warm_up.lowest_temperature, extruder, required_temp, false);
constexpr bool during_printing = false;
warm_up.heating_time = preheat_config.getTimeToGoFromTempToTemp(extruder, warm_up.lowest_temperature, initial_print_temp, during_printing);
if (warm_up.heating_time > in_between_time)
{
warm_up.heating_time = in_between_time;
warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder);
warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder, during_printing);
}
warm_up.heating_time = warm_up.heating_time + extra_preheat_time;
return warm_up;
@@ -82,7 +92,8 @@ Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vecto
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);
constexpr bool during_printing = true;
double time_before_extruder_plan_end = 0.5 * preheat_config.getTimeToGoFromTempToTemp(extruder, prev_extruder_plan.printing_temperature, required_temp, during_printing);
time_before_extruder_plan_end = std::min(prev_extruder_plan.estimates.getTotalTime(), time_before_extruder_plan_end);
insertPreheatCommand(prev_extruder_plan, time_before_extruder_plan_end, extruder, required_temp);
@@ -111,13 +122,13 @@ void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<ExtruderPl
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
double initial_print_temp = extruder_plan.initial_printing_temperature;
Preheat::WarmUpResult heating_time_and_from_temp = timeBeforeExtruderPlanToInsert(extruder_plans, extruder_plan_idx);
if (heating_time_and_from_temp.total_time_window < preheat_config.getMinimalTimeWindow(extruder))
{
handleStandbyTemp(extruder_plans, extruder_plan_idx, required_temp);
handleStandbyTemp(extruder_plans, extruder_plan_idx, initial_print_temp);
return; // don't insert preheat command and just stay on printing temperature
}
else
@@ -125,30 +136,32 @@ void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<ExtruderPl
handleStandbyTemp(extruder_plans, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);
}
// handle preheat command
double time_before_extruder_plan_to_insert = heating_time_and_from_temp.heating_time;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan_before = *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_before_extruder_plan_to_insert, extruder, required_temp);
insertPreheatCommand(extruder_plan_before, time_before_extruder_plan_to_insert, extruder, initial_print_temp);
return;
}
time_before_extruder_plan_to_insert -= time_here;
}
// time_before_extruder_plan_to_insert falls before all plans in the buffer
extruder_plans[0]->insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
bool wait = false;
unsigned int path_idx = 0;
extruder_plans[0]->insertCommand(path_idx, extruder, initial_print_temp, wait); // insert preheat command at verfy beginning of buffer
}
void LayerPlanBuffer::insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
void LayerPlanBuffer::insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
ExtruderPlan* prev_extruder_plan = extruder_plans[extruder_plan_idx - 1];
@@ -162,21 +175,156 @@ void LayerPlanBuffer::insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_
if (prev_extruder == extruder)
{
if (preheat_config.usesFlowDependentTemp(extruder))
{
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
}
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, extruder_plan.printing_temperature);
prev_extruder_plan->printing_temperature_command = --prev_extruder_plan->inserts.end();
}
else
{
insertPreheatCommand_multiExtrusion(extruder_plans, extruder_plan_idx);
insertFinalPrintTempCommand(extruder_plans, extruder_plan_idx - 1);
insertPrintTempCommand(extruder_plan);
}
}
void LayerPlanBuffer::insertPreheatCommands()
void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan)
{
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
unsigned int extruder = extruder_plan.extruder;
double print_temp = extruder_plan.printing_temperature;
double heated_pre_travel_time = 0;
if (preheat_config.getInitialPrintTemp(extruder) != 0)
{ // handle heating from initial_print_temperature to printing_tempreature
unsigned int path_idx;
for (path_idx = 0; path_idx < extruder_plan.paths.size(); path_idx++)
{
GCodePath& path = extruder_plan.paths[path_idx];
heated_pre_travel_time += path.estimates.getTotalTime();
if (!path.isTravelPath())
{
break;
}
}
bool wait = false;
extruder_plan.insertCommand(path_idx, extruder, print_temp, wait);
}
extruder_plan.heated_pre_travel_time = heated_pre_travel_time;
}
void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx)
{
ExtruderPlan& last_extruder_plan = *extruder_plans[last_extruder_plan_idx];
int extruder = last_extruder_plan.extruder;
double final_print_temp = preheat_config.getFinalPrintTemp(extruder);
if (final_print_temp == 0)
{
return;
}
double heated_post_travel_time = 0; // The time after the last extrude move toward the end of the extruder plan during which the nozzle is stable at the final print temperature
{ // compute heated_post_travel_time
unsigned int path_idx;
for (path_idx = last_extruder_plan.paths.size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = last_extruder_plan.paths[path_idx];
if (!path.isTravelPath())
{
break;
}
heated_post_travel_time += path.estimates.getTotalTime();
}
}
double time_window = 0; // The time window within which the nozzle needs to heat from the initial print temp to the printing temperature and then back to the final print temp; i.e. from the first to the last extrusion move with this extruder
double weighted_average_print_temp = 0; // The average of the normal printing temperatures of the extruder plans (which might be different due to flow dependent temp or due to initial layer temp) Weighted by time
double initial_print_temp = -1; // The initial print temp of the first extruder plan with this extruder
{ // compute time window and print temp statistics
double heated_pre_travel_time = -1; // The time before the first extrude move from the start of the extruder plan during which the nozzle is stable at the initial print temperature
for (unsigned int prev_extruder_plan_idx = last_extruder_plan_idx; (int)prev_extruder_plan_idx >= 0; prev_extruder_plan_idx--)
{
ExtruderPlan& prev_extruder_plan = *extruder_plans[prev_extruder_plan_idx];
if (prev_extruder_plan.extruder != extruder)
{
break;
}
double prev_extruder_plan_time = prev_extruder_plan.estimates.getTotalTime();
time_window += prev_extruder_plan_time;
heated_pre_travel_time = prev_extruder_plan.heated_pre_travel_time;
if (prev_extruder_plan.estimates.getTotalUnretractedTime() > 0)
{ // handle temp statistics
assert(prev_extruder_plan.printing_temperature != -1 && "Previous extruder plan should already have a temperature planned");
weighted_average_print_temp += prev_extruder_plan.printing_temperature * prev_extruder_plan_time;
initial_print_temp = prev_extruder_plan.initial_printing_temperature;
}
}
weighted_average_print_temp /= time_window;
time_window -= heated_pre_travel_time + heated_post_travel_time;
assert(heated_pre_travel_time != -1 && "heated_pre_travel_time must have been computed; there must have been an extruder plan!");
}
assert((time_window >= 0 || last_extruder_plan.estimates.getMaterial() == 0) && "Time window should always be positive if we actually extrude");
// ,layer change .
// : ,precool command ,layer change .
// : ____: : ,precool command .
// :/ \ _____:_____: .
// _____/ \ / \ .
// / \ / \ .
// / / .
// / / .
// .
// approximate ^ by ^ .
// This approximation is quite ok since it only determines where to insert the precool temp command,
// which means the stable temperature of the previous extruder plan and the stable temperature of the next extruder plan couldn't be reached
constexpr bool during_printing = true;
Preheat::CoolDownResult warm_cool_result = preheat_config.getCoolDownPointAfterWarmUp(time_window, extruder, initial_print_temp, weighted_average_print_temp, final_print_temp, during_printing);
double cool_down_time = warm_cool_result.cooling_time;
assert(cool_down_time >= 0);
// find extruder plan in which to insert cooling command
ExtruderPlan* precool_extruder_plan = &last_extruder_plan;
{
for (unsigned int precool_extruder_plan_idx = last_extruder_plan_idx; (int)precool_extruder_plan_idx >= 0; precool_extruder_plan_idx--)
{
precool_extruder_plan = extruder_plans[precool_extruder_plan_idx];
if (precool_extruder_plan->printing_temperature_command)
{ // the precool command ends up before the command to go to the print temperature of the next extruder plan, so remove that print temp command
precool_extruder_plan->inserts.erase(*precool_extruder_plan->printing_temperature_command);
}
double time_here = precool_extruder_plan->estimates.getTotalTime();
if (cool_down_time < time_here)
{
break;
}
cool_down_time -= time_here;
}
}
// at this point cool_down_time is what time is left if cool down time of extruder plans after precool_extruder_plan (up until last_extruder_plan) are already taken into account
{ // insert temp command in precool_extruder_plan
double extrusion_time_seen = 0;
unsigned int path_idx;
for (path_idx = precool_extruder_plan->paths.size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = precool_extruder_plan->paths[path_idx];
extrusion_time_seen += path.estimates.getTotalTime();
if (extrusion_time_seen >= cool_down_time)
{
break;
}
}
bool wait = false;
double time_after_path_start = extrusion_time_seen - cool_down_time;
precool_extruder_plan->insertCommand(path_idx, extruder, final_print_temp, wait, time_after_path_start);
}
}
void LayerPlanBuffer::insertTempCommands()
{
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;
@@ -184,9 +332,9 @@ void LayerPlanBuffer::insertPreheatCommands()
std::vector<ExtruderPlan*> extruder_plans;
extruder_plans.reserve(buffer.size() * 2);
for (GCodePlanner& layer_plan : buffer)
for (GCodePlanner* layer_plan : buffer)
{
for (ExtruderPlan& extr_plan : layer_plan.extruder_plans)
for (ExtruderPlan& extr_plan : layer_plan->extruder_plans)
{
extruder_plans.push_back(&extr_plan);
}
@@ -194,20 +342,30 @@ void LayerPlanBuffer::insertPreheatCommands()
// insert commands for all extruder plans on this layer
GCodePlanner& layer_plan = buffer.back();
GCodePlanner& layer_plan = *buffer.back();
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
{
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
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)
)
if (time <= 0.0)
{
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);
double avg_flow = extruder_plan.estimates.getMaterial() / time;
extruder_plan.printing_temperature = preheat_config.getTemp(extruder, avg_flow, extruder_plan.is_initial_layer);
extruder_plan.initial_printing_temperature = preheat_config.getInitialPrintTemp(extruder);
if (extruder_plan.initial_printing_temperature == 0
|| !extruder_used_in_meshgroup[extruder]
|| (overall_extruder_plan_idx > 0 && extruder_plans[overall_extruder_plan_idx - 1]->extruder == extruder)
)
{
extruder_plan.initial_printing_temperature = extruder_plan.printing_temperature;
extruder_used_in_meshgroup[extruder] = true;
}
assert(extruder_plan.printing_temperature != -1 && "extruder_plan.printing_temperature should now have been set");
if (buffer.size() == 1 && extruder_plan_idx == 0)
{ // the very first extruder plan of the current meshgroup
@@ -221,7 +379,7 @@ void LayerPlanBuffer::insertPreheatCommands()
// see FffGcodeWriter::processStartingCode
if (extruder_idx == extruder)
{
gcode.setInitialTemp(extruder_idx, extruder_plan.required_temp);
gcode.setInitialTemp(extruder_idx, extruder_plan.printing_temperature);
}
else
{
@@ -239,8 +397,7 @@ void LayerPlanBuffer::insertPreheatCommands()
continue;
}
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
insertPreheatCommand(extruder_plans, overall_extruder_plan_idx);
insertTempCommands(extruder_plans, overall_extruder_plan_idx);
}
}
+61 -14
Ver Arquivo
@@ -16,6 +16,19 @@
namespace cura
{
/*!
* Class for buffering multiple layer plans (\ref GCodePlanner) / extruder plans within those layer plans, so that temperature commands can be inserted in earlier layer plans.
*
* This class handles where to insert temperature commands for:
* - initial layer temperature
* - flow dependent temperature
* - starting to heat up from the standby temperature
* - initial printing temperature | printing temperature | final printing temperature
*
* \image html assets/precool.png "Temperature Regulation" width=10cm
* \image latex assets/precool.png "Temperature Regulation" width=10cm
*
*/
class LayerPlanBuffer : SettingsMessenger
{
GCodeExport& gcode;
@@ -27,48 +40,61 @@ class LayerPlanBuffer : SettingsMessenger
static constexpr const double extra_preheat_time = 1.0; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones.
std::vector<bool> extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the printing_temperature
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
std::list<GCodePlanner*> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
: SettingsMessenger(settings)
, gcode(gcode)
, extruder_used_in_meshgroup(MAX_EXTRUDERS, false)
{ }
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.
* Push a new layer plan into the buffer
*/
template<typename... Args>
GCodePlanner& emplace_back(Args&&... constructor_args)
void push(GCodePlanner& layer_plan)
{
buffer.push_back(&layer_plan);
}
/*!
* Process all layers in the buffer
* This inserts the temperature commands to start warming for a given layer in earlier layers
*
* Pop out the earliest layer in the buffer if the buffer size is exceeded
* \return A nullptr or the popped gcode_layer
*/
GCodePlanner* processBuffer()
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
insertTempCommands(); // 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);
GCodePlanner* ret = buffer.front();
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
return ret;
}
return buffer.back();
return nullptr;
}
/*!
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
*/
void flush();
private:
/*!
* Insert the preheat command for @p extruder into @p extruder_plan_before
*
@@ -118,13 +144,34 @@ public:
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to generate the preheat command
*/
void insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
void insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the temperature command to heat from the initial print temperature to the printing temperature
*
* The temperature command is insert at the start of the very first extrusion move
*
* \param extruder_plan The extruder plan in which to insert the heat up command
*/
void insertPrintTempCommand(ExtruderPlan& extruder_plan);
/*!
* Insert the temp command to start cooling from the printing temperature to the final print temp
*
* The print temp is inserted before the last extrusion move of the extruder plan corresponding to \p last_extruder_plan_idx
*
* The command is inserted at a timed offset before the end of the last extrusion move
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param last_extruder_plan_idx The index of the last extruder plan in \p extruder_plans with the same extruder as previous extruder plans
*/
void insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx);
/*!
* Insert the preheat commands for the last added layer (unless that layer was empty)
*/
void insertPreheatCommands();
private:
void insertTempCommands();
/*!
* Reconfigure the standby temperature during which we didn't print with this extruder.
* Find the previous extruder plan with the same extruder as layers[layer_plan_idx].extruder_plans[extruder_plan_idx]
+13 -4
Ver Arquivo
@@ -17,7 +17,7 @@ class MergeInfillLines
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
const 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
bool speed_equalize_flow_enabled; //!< Should the speed be varied with extrusion width
double speed_equalize_flow_max; //!< Maximum speed when adjusting speed for flow
@@ -64,9 +64,18 @@ public:
/*!
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
*/
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size, bool speed_equalize_flow_enabled, double speed_equalize_flow_max)
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size), speed_equalize_flow_enabled(speed_equalize_flow_enabled), speed_equalize_flow_max(speed_equalize_flow_max) { }
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, const GCodePathConfig& travelConfig, int64_t nozzle_size, bool speed_equalize_flow_enabled, double speed_equalize_flow_max)
: gcode(gcode)
, layer_nr(layer_nr)
, paths(paths)
, extruder_plan(extruder_plan)
, travelConfig(travelConfig)
, nozzle_size(nozzle_size)
, speed_equalize_flow_enabled(speed_equalize_flow_enabled)
, speed_equalize_flow_max(speed_equalize_flow_max)
{
}
/*!
* 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.
+8 -2
Ver Arquivo
@@ -58,6 +58,7 @@ int MeshGroup::getExtruderCount() const
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
assert((int)extruder_nr >= 0 && (int)extruder_nr < getSettingAsCount("machine_extruder_count") && "only valid extruder trains may be requested!");
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
@@ -133,7 +134,7 @@ void MeshGroup::finalize()
{
createExtruderTrain(extruder_nr); // create it if it didn't exist yet
if (getSettingAsIndex("adhesion_extruder_nr") == extruder_nr)
if (getSettingAsIndex("adhesion_extruder_nr") == extruder_nr && getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
getExtruderTrain(extruder_nr)->setIsUsed(true);
continue;
@@ -157,7 +158,12 @@ void MeshGroup::finalize()
for (const Mesh& mesh : meshes)
{
getExtruderTrain(mesh.getSettingAsIndex("extruder_nr"))->setIsUsed(true);
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
getExtruderTrain(mesh.getSettingAsIndex("extruder_nr"))->setIsUsed(true);
}
}
//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.
+184
Ver Arquivo
@@ -0,0 +1,184 @@
#include "Preheat.h"
namespace cura
{
void Preheat::setConfig(const MeshGroup& meshgroup)
{
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
{
assert(meshgroup.getExtruderTrain(extruder_nr) != nullptr);
const ExtruderTrain& extruder_train = *meshgroup.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
double machine_nozzle_cool_down_speed = extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed");
double machine_nozzle_heat_up_speed = extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed");
double material_extrusion_cool_down_speed = extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed");
assert(material_extrusion_cool_down_speed < machine_nozzle_heat_up_speed && "The extrusion cooldown speed must be smaller than the heat up speed; otherwise the printing temperature cannot be reached!");
config.time_to_cooldown_1_degree[0] = 1.0 / machine_nozzle_cool_down_speed;
config.time_to_heatup_1_degree[0] = 1.0 / machine_nozzle_heat_up_speed;
config.time_to_cooldown_1_degree[1] = 1.0 / (machine_nozzle_cool_down_speed + material_extrusion_cool_down_speed);
config.time_to_heatup_1_degree[1] = 1.0 / (machine_nozzle_heat_up_speed - material_extrusion_cool_down_speed);
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature");
config.min_time_window = extruder_train.getSettingInSeconds("machine_min_cool_heat_time_window");
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature");
config.material_print_temperature_layer_0 = extruder_train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
config.material_initial_print_temperature = extruder_train.getSettingInDegreeCelsius("material_initial_print_temperature");
config.material_final_print_temperature = extruder_train.getSettingInDegreeCelsius("material_final_print_temperature");
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]]
}
}
double Preheat::getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing)
{
Config& config = config_per_extruder[extruder];
double time;
if (temp_after > temp_before)
{
time = (temp_after - temp_before) * config.time_to_heatup_1_degree[during_printing];
}
else
{
time = (temp_before - temp_after) * config.time_to_cooldown_1_degree[during_printing];
}
return std::max(0.0, time);
}
double Preheat::getTemp(unsigned int extruder, double flow, bool is_initial_layer)
{
if (is_initial_layer && config_per_extruder[extruder].material_print_temperature_layer_0 != 0)
{
return config_per_extruder[extruder].material_print_temperature_layer_0;
}
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
Preheat::WarmUpResult Preheat::getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
WarmUpResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
result.total_time_window = time_window;
// ,temp_end
// / .
// ,temp_start / .
// \ ' ' ' ' '/ ' ' '> outer_temp .
// \________/ .
// "-> temp_mid
// ^^^^^^^^^^
// limited_time_window
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.heating_time = extra_heatup_time;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_start;
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.heating_time = 0;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_end;
}
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = std::min(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
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 < limited_time_window)
{
result.heating_time += time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = temp_mid;
}
else
{
result.heating_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.lowest_temperature = std::max(temp_mid, temp_end - result.heating_time / time_to_heatup_1_degree);
}
if (result.heating_time > time_window || result.heating_time < 0.0)
{
logWarning("getWarmUpPointAfterCoolDown returns result outside of the time window!");
}
return result;
}
Preheat::CoolDownResult Preheat::getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
CoolDownResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
assert(temp_start != -1 && temp_mid != -1 && temp_end != -1 && "temperatures must be initialized!");
result.total_time_window = time_window;
// limited_time_window
// :^^^^^^^^^^^^:
// : ________. : . . .> temp_mid
// : / \ : .
// :/ . . . . .\:. . .> outer_temp .
// ^temp_start \ .
// \ .
// ^temp_end
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.cooling_time = 0;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_end;
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.cooling_time = extra_cooldown_time;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_start;
}
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = std::max(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double cool_down_time = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_temp1 = cool_down_time * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_temp1 < limited_time_window)
{
result.cooling_time += cool_down_time;
result.highest_temperature = temp_mid;
}
else
{
result.cooling_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.highest_temperature = std::min(temp_mid, temp_end + result.cooling_time / time_to_cooldown_1_degree);
}
if (result.cooling_time > time_window || result.cooling_time < 0.0)
{
logWarning("getCoolDownPointAfterWarmUp returns result outside of the time window!");
}
return result;
}
}//namespace cura
+87 -104
Ver Arquivo
@@ -26,17 +26,21 @@ class Preheat
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 time_to_heatup_1_degree[2]; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double time_to_cooldown_1_degree[2]; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
double min_time_window; //!< Minimal time (in seconds) to allow an extruder to cool down and then warm up again.
double material_print_temperature; //!< default print temp (backward compatilibily)
double material_print_temperature_layer_0; //!< initial layer print temp
double material_initial_print_temperature; //!< print temp when first starting to extrude after a layer switch
double material_final_print_temperature; //!< print temp at the end of all extrusion moves of an extruder to which it's cooled down just before - during the extrusion
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
@@ -54,6 +58,16 @@ public:
double lowest_temperature; //!< The lower temperature from which heating starts.
};
/*!
* The type of result when computing when to start cooling down a nozzle before it's not going to be used again.
*/
struct CoolDownResult
{
double total_time_window; //!< The total time in which heating and cooling takes place.
double cooling_time; //!< The total time needed to cool down to the required temperature.
double highest_temperature; //!< The upper temperature from which cooling starts.
};
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
@@ -68,68 +82,51 @@ public:
* Get the time it takes to heat up one degree celsius
*
* \param extruder the extruder train for which to get time it takes to heat up one degree celsius
* \param during_printing whether the heating takes time during printing or when idle
* \return the time it takes to heat up one degree celsius
*/
double getTimeToHeatup1Degree(int extruder)
double getTimeToHeatup1Degree(int extruder, bool during_printing)
{
return config_per_extruder[extruder].time_to_heatup_1_degree;
return config_per_extruder[extruder].time_to_heatup_1_degree[during_printing];
}
/*!
* Get the initial print temperature when starting to extrude.
* \param during_printing whether the heating takes time during printing or when idle
*/
double getInitialPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_initial_print_temperature;
}
/*!
* Get the final print temperature at the end of all extrusion moves with the current extruder
*/
double getFinalPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_final_print_temperature;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
* \param meshgroup Where to get settings from
*/
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
void setConfig(const MeshGroup& meshgroup);
config.min_time_window = extruder_train.getSettingInSeconds("machine_min_cool_heat_time_window");
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.
* Get the optimal temperature corresponding to a given average flow,
* or the initial layer 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
* \param is_initial_layer Whether the initial layer temperature should be returned instead of flow-based 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);
}
double getTemp(unsigned int extruder, double flow, bool is_initial_layer);
/*!
* Return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
@@ -142,76 +139,62 @@ public:
}
/*!
* Decide when to start warming up again after starting to cool down towards the standby temperature.
* Decide when to start warming up again after starting to cool down towards \p temp_mid.
* 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
* \warning it is assumed that \p temp_mid is lower than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
// ,temp_end
// / .
// ,temp_start / .
// \ / .
// \________/ .
// "-> temp_mid
* \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
* \param temp_start The temperature from which to start cooling down
* \param temp_mid The temeprature to which we try to cool down
* \param temp_end The temperature to which we need to have heated up at the end of the \p time_window
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the @p time_window to insert the preheat command and the temperature from which the heating starts
*/
WarmUpResult timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
{
WarmUpResult result;
const Config& config = config_per_extruder[extruder];
result.total_time_window = time_window;
double time_ratio_cooldown_heatup = config.time_to_cooldown_1_degree / config.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)
{
result.heating_time = time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = config.standby_temp;
}
else
{
result.heating_time = time_window * config.time_to_heatup_1_degree / (config.time_to_cooldown_1_degree + config.time_to_heatup_1_degree);
result.lowest_temperature = std::max(config.standby_temp, temp - result.heating_time / config.time_to_heatup_1_degree);
}
return result;
}
WarmUpResult getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* 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.
* Decide when to start cooling down again after starting to warm up towards the \p temp_mid
* Two cases are considered:
* the case where the temperature is reached /"""\ .
* and the case where it isn't /\ .
*
* \warning it is assumed that \p temp_mid is higher than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
* \param from_temp The temperature at which the nozzle was before
// _> temp_mid
// /""""""""\ .
// / \ .
// ^temp_start \ .
// \ .
// ^temp_end
* \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
* \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)
* \param temp_start The temperature from which to start heating up
* \param temp_mid The temeprature to which we try to heat up
* \param temp_end The temperature to which we need to have cooled down after \p time_window time
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the \p time_window to insert the preheat command and the temperature from which the cooling starts
*/
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);
}
}
}
CoolDownResult getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* Get the time to go from one temperature to another temperature
* \param extruder The extruder number for which to perform the heatup / cooldown
* \param temp_before The before temperature
* \param temp_after The after temperature
* \param during_printing Whether the planned cooldown / warmup occurs during printing or while in standby mode
* \return The time needed
*/
double getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing);
};
} // namespace cura
+122 -159
Ver Arquivo
@@ -12,103 +12,32 @@
namespace cura
{
PrimeTower::PrimeTower()
: current_pre_wipe_location_idx(0)
PrimeTower::PrimeTower(const SliceDataStorage& storage)
: is_hollow(false)
, wipe_from_middle(false)
{
for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
enabled = storage.getSettingBoolean("prime_tower_enable")
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
&& storage.getSettingInMicrons("prime_tower_size") > 10;
if (enabled)
{
last_prime_tower_poly_printed[extruder_nr] = -1;
}
}
void PrimeTower::initConfigs(const MeshGroup* meshgroup)
{
extruder_count = meshgroup->getExtruderCount();
for (int extr = 0; extr < extruder_count; extr++)
{
config_per_extruder.emplace_back(PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
const ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMillimetersPerSecond("acceleration_prime_tower"), train->getSettingInMillimetersPerSecond("jerk_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
}
}
void PrimeTower::setConfigs(const MeshGroup* meshgroup, const int layer_thickness)
{
extruder_count = meshgroup->getExtruderCount();
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.meshgroup->getExtruderCount();
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)
{
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_object_height_per_extruder[extr_nr] =
std::max( max_object_height_per_extruder[extr_nr]
, mesh.layer_nr_max_filled_layer );
}
int support_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_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr"); // TODO: support skin extruder should be configurable per object
max_object_height_per_extruder[support_skin_extruder_nr] =
std::max( max_object_height_per_extruder[support_skin_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];
}
generateGroundpoly(storage);
}
}
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
extruder_count = storage.meshgroup->getExtruderCount();
int64_t prime_tower_wall_thickness = storage.getSettingInMicrons("prime_tower_wall_thickness");
int64_t tower_size = storage.getSettingInMicrons("prime_tower_size");
if (prime_tower_wall_thickness * 2 < tower_size)
{
is_hollow = true;
}
PolygonRef p = ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
@@ -116,13 +45,20 @@ void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
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));
middle = Point(x - tower_size / 2, y + tower_size / 2);
if (is_hollow)
{
ground_poly = ground_poly.difference(ground_poly.offset(-prime_tower_wall_thickness));
}
post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(const SliceDataStorage& storage, unsigned int total_layers)
void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
enabled &= storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches.
if (enabled)
{
generatePaths_denseInfill(storage);
generateWipeLocations(storage);
@@ -135,22 +71,22 @@ void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
int extra_infill_shift = 0;
generateGroundpoly(storage);
int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position
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();
std::vector<ExtrusionMoves>& patterns = patterns_per_extruder.back();
patterns.resize(n_patterns);
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
Polygons result_polygons; // should remain empty, since we generate lines pattern!
patterns[pattern_idx].polygons = ground_poly.offset(-line_width / 2);
Polygons& result_lines = patterns[pattern_idx].lines;
int outline_offset = -line_width;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
infill_comp.generate(result_polygons, result_lines);
}
@@ -158,67 +94,58 @@ void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
}
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, bool wipe)
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder) const
{
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
if (!enabled)
{
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)
if (gcodeLayer.getPrimeTowerIsPlanned())
{ // don't print the prime tower if it has been printed already
return;
}
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
if (layer_nr > storage.max_print_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
if (prev_extruder == gcodeLayer.getExtruder())
bool pre_wipe = storage.meshgroup->getExtruderTrain(new_extruder)->getSettingBoolean("dual_pre_wipe");
bool post_wipe = storage.meshgroup->getExtruderTrain(prev_extruder)->getSettingBoolean("prime_tower_wipe_enabled");
if (prev_extruder == new_extruder)
{
wipe = false;
pre_wipe = false;
post_wipe = false;
}
// pre-wipe:
if (wipe)
if (pre_wipe)
{
preWipe(storage, gcodeLayer, new_extruder);
preWipe(storage, gcodeLayer, layer_nr, new_extruder);
}
addToGcode_denseInfill(storage, gcodeLayer, gcode, layer_nr, prev_extruder);
addToGcode_denseInfill(gcodeLayer, layer_nr, new_extruder);
// post-wipe:
if (false && wipe) // TODO: make a separate setting for the post-wipe!
if (post_wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
}
gcodeLayer.setPrimeTowerIsPlanned();
}
void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder)
void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const
{
int new_extruder = gcodeLayer.getExtruder();
const ExtrusionMoves& pattern = patterns_per_extruder[extruder_nr][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
Polygons& pattern = patterns_per_extruder[new_extruder][layer_nr % 2];
const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr];
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
Polygon outer_wall = ground_poly.offset(-config.getLineWidth() / 2).back();
gcodeLayer.addPolygon(outer_wall, start_idx, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
CommandSocket::sendPolygons(PrintFeatureType::Support, pattern, config.getLineWidth());
gcode_layer.addPolygonsByOptimizer(pattern.polygons, &config);
gcode_layer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
}
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const
{
Point ret(0, 0);
int absolute_starting_points = 0;
@@ -248,45 +175,60 @@ Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
void PrimeTower::generateWipeLocations(const SliceDataStorage& storage)
{
Point from = getLocationBeforePrimeTower(storage);
// take the closer corner of the wipe tower and generate wipe locations on that side only:
//
// |
// |
// +-----
// .
// ^ nozzle switch location
wipe_from_middle = is_hollow;
// only wipe from the middle of the prime tower if we have a z hop already on the first move after the layer switch
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
wipe_from_middle &= train.getSettingBoolean("retraction_hop_enabled")
&& (!train.getSettingBoolean("retraction_hop_only_when_collides") || train.getSettingBoolean("retraction_hop_after_extruder_switch"));
}
PolygonsPointIndex segment_start; // from where to start the sequence of wipe points
PolygonsPointIndex segment_end; // where to end the sequence of wipe points
// find the single line segment closest to [from] pointing most toward [from]
PolygonsPointIndex closest_vert = PolygonUtils::findNearestVert(from, ground_poly);
PolygonsPointIndex prev = closest_vert.prev();
PolygonsPointIndex next = closest_vert.next();
int64_t prev_dot_score = dot(from - closest_vert.p(), turn90CCW(prev.p() - closest_vert.p()));
int64_t next_dot_score = dot(from - closest_vert.p(), turn90CCW(closest_vert.p() - next.p()));
if (prev_dot_score > next_dot_score)
if (wipe_from_middle)
{
segment_start = prev;
segment_end = closest_vert;
// take the same start as end point so that the whole poly os covered.
// find the inner polygon.
segment_start = segment_end = PolygonUtils::findNearestVert(middle, ground_poly);
}
else
{
segment_start = closest_vert;
segment_end = next;
}
// take the closer corner of the wipe tower and generate wipe locations on that side only:
//
// |
// |
// +-----
// .
// ^ nozzle switch location
Point from = getLocationBeforePrimeTower(storage);
// TODO: come up with alternatives for better segments once the prime tower can be different shapes
// find the single line segment closest to [from] pointing most toward [from]
PolygonsPointIndex closest_vert = PolygonUtils::findNearestVert(from, ground_poly);
PolygonsPointIndex prev = closest_vert.prev();
PolygonsPointIndex next = closest_vert.next();
int64_t prev_dot_score = dot(from - closest_vert.p(), turn90CCW(prev.p() - closest_vert.p()));
int64_t next_dot_score = dot(from - closest_vert.p(), turn90CCW(closest_vert.p() - next.p()));
if (prev_dot_score > next_dot_score)
{
segment_start = prev;
segment_end = closest_vert;
}
else
{
segment_start = closest_vert;
segment_end = next;
}
}
PolygonUtils::spreadDots(segment_start, segment_end, number_of_pre_wipe_locations, pre_wipe_locations);
}
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr)
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const
{
int current_pre_wipe_location_idx = (pre_wipe_location_skip * layer_nr) % number_of_pre_wipe_locations;
const ClosestPolygonPoint wipe_location = pre_wipe_locations[current_pre_wipe_location_idx];
current_pre_wipe_location_idx = (current_pre_wipe_location_idx + pre_wipe_location_skip) % number_of_pre_wipe_locations;
ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
const int inward_dist = train.getSettingInMicrons("machine_nozzle_size") * 3 / 2 ;
@@ -294,15 +236,36 @@ void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_la
const Point end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist);
const Point outward_dir = wipe_location.location - end;
const Point start = wipe_location.location + normal(outward_dir, start_dist);
// for hollow wipe tower:
// start from above
// go to the level of the previous layer
// wipe
// go to normal layer height (automatically on the next extrusion move...
gcode_layer.addTravel(start); // TODO: verify that this move has a z hop ==> cylindric wipe tower
// gcode_layer.makeLastPathZhopped which calls forceNewPathStart TODO ==> cylindric wipe tower
if (wipe_from_middle)
{
// for hollow wipe tower:
// start from above
// go to wipe start
// go to the Z height of the previous/current layer
// wipe
// go to normal layer height (automatically on the next extrusion move)...
GCodePath& toward_middle = gcode_layer.addTravel(middle);
toward_middle.perform_z_hop = true;
gcode_layer.forceNewPathStart();
GCodePath& toward_wipe_start = gcode_layer.addTravel_simple(start);
toward_wipe_start.perform_z_hop = false;
toward_wipe_start.retract = true;
}
else
{
gcode_layer.addTravel(start);
}
float flow = 0.0001; // force this path being interpreted as an extrusion path, so that no Z hop will occur (TODO: really separately handle travel and extrusion moves)
gcode_layer.addExtrusionMove(end, &config_per_extruder[extruder_nr], SpaceFillType::None, flow);
gcode_layer.addExtrusionMove(end, &gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr], SpaceFillType::None, flow);
}
void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
{
const Polygons outside_polygon = ground_poly.getOutsidePolygons();
for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++)
{
storage.support.supportLayers[layer].supportAreas = storage.support.supportLayers[layer].supportAreas.difference(outside_polygon);
}
}
+46 -47
Ver Arquivo
@@ -1,3 +1,6 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
@@ -16,8 +19,6 @@ class SliceDataStorage;
class GCodePlanner;
class GCodeExport;
typedef std::vector<IntPoint> PolyLine;
/*!
* Class for everything to do with the prime tower:
* - generating the areas
@@ -27,62 +28,55 @@ typedef std::vector<IntPoint> PolyLine;
class PrimeTower
{
private:
struct ExtrusionMoves
{
Polygons polygons;
Polygons lines;
};
int extruder_count; //!< number of extruders
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
bool is_hollow; //!< Whether the prime tower is hollow
bool wipe_from_middle; //!< Whether to wipe on the inside of the hollow prime tower
Point middle; //!< The middle of the prime tower
Point post_wipe_point; //!< location to post-wipe the unused nozzle off on
std::vector<PolyLine> extruder_paths; //!< Precomputed so that we don't need to generate the paths each layer over again
std::vector<ClosestPolygonPoint> pre_wipe_locations; //!< The differernt locations where to pre-wipe the active nozzle
const unsigned int pre_wipe_location_skip = 8; //!< How big the steps are when stepping through \ref PrimeTower::wipe_locations
const unsigned int number_of_pre_wipe_locations = 13; //!< The required size of \ref PrimeTower::wipe_locations
// note that the above are two consecutive numbers in the fibonacci sequence
int current_pre_wipe_location_idx; //!< Index into \ref PrimeTower::wipe_locations of where to pre-wipe the nozzle
const unsigned int pre_wipe_location_skip = 13; //!< How big the steps are when stepping through \ref PrimeTower::wipe_locations
const unsigned int number_of_pre_wipe_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations
// note that the above are two consecutive numbers in the Fibonacci sequence
public:
bool enabled; //!< Whether the prime tower is enabled.
Polygons ground_poly; //!< The outline of the prime tower to be used for each layer
/*!
* Initialize \ref PrimeTower::config_per_extruder with speed and line width settings.
*
* \param meshgroup Where to retrieve the setttings for each extruder
*/
void initConfigs(const MeshGroup* meshgroup);
std::vector<std::vector<ExtrusionMoves>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
/*!
* Complete the \ref PrimeTower::config_per_extruder by settings the layer height.
*
* \param meshgroup Where to retrieve the setttings for each extruder
* \param layer_thickness The current layer thickness
* \brief Creates a prime tower instance that will determine where and how
* the prime tower gets printed.
*
* \param storage A storage where it retrieves the prime tower settings.
*/
void setConfigs(const MeshGroup* meshgroup, const int layer_thickness);
PrimeTower(const SliceDataStorage& storage);
/*!
* Generate the prime tower area to be used on each layer
*
* Fills \ref PrimeTower::ground_poly and sets \ref PrimeTower::middle
*
* \param storage Where to retrieve prime tower settings from
*/
void generateGroundpoly(const SliceDataStorage& storage);
std::vector<std::vector<Polygons>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
/*!
* Generate the area where the prime tower should be.
*
* \param storage where to get settings from
* \param total_layers The total number of layers
*/
void generatePaths(const SliceDataStorage& storage, unsigned int total_layers);
/*!
* Compute the maximum layer at which a layer switch will occur and store the result in \ref SliceDataStorage::max_object_height_second_to_last_extruder
*
* \param[in,out] storage Where to retrieve area data and extruder settings for those areas; where to store the max_object_height_second_to_last_extruder
*/
void computePrimeTowerMax(SliceDataStorage& storage);
PrimeTower(); //!< basic constructor
void generatePaths(const SliceDataStorage& storage);
/*!
* Add path plans for the prime tower to the \p gcode_layer
@@ -91,16 +85,19 @@ public:
* \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths
* \param layer_nr The layer for which to generate the prime tower paths
* \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made
* \param wipe Whether to wipe of the (not previous, but) current nozzle on the wipe tower (only occurs if previous extruder is different fromt he current one)
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
*/
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, bool wipe);
private:
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder) const;
/*!
* 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.
* \brief Subtract the prime tower from the support areas in storage.
*
* \param storage The storage where to find the support from which to
* subtract a prime tower.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
void subtractFromSupport(SliceDataStorage& storage);
private:
/*!
* Find an approriate representation for the point representing the location before going to the prime tower
@@ -110,7 +107,7 @@ private:
* \param storage where to get settings from
* \return that location
*/
Point getLocationBeforePrimeTower(const SliceDataStorage& storage);
Point getLocationBeforePrimeTower(const SliceDataStorage& storage) const;
/*!
* \param storage where to get settings from
@@ -121,7 +118,8 @@ private:
/*!
* \see WipeTower::generatePaths
*
* Generate the area where the prime tower should be.
* Generate the extrude paths for each extruder on even and odd layers
* Fill the ground poly with dense infill.
*
* \param storage where to get settings from
* \param total_layers The total number of layers
@@ -130,24 +128,25 @@ private:
/*!
* \see PrimeTower::addToGcode
*
*
* Add path plans for the prime tower to the \p gcode_layer
*
* \param storage where to get settings from; where to get the maximum height of the prime tower from
*
* \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths
* \param layer_nr The layer for which to generate the prime tower paths
* \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made
* \param extruder The extruder we just switched to, with which the prime
* tower paths should be drawn.
*/
void addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder);
void addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder) const;
/*!
* Plan the moves for wiping the current nozzles oozed material before starting to print the prime tower.
*
* \param storage where to get settings from
* \param[out] gcode_layer where to add the planned paths for wiping
* \param layer_nr The layer number of the \p gcode_layer
* \param extruder_nr The current extruder
*/
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr);
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int layer_nr, const int extruder_nr) const;
};
+14 -3
Ver Arquivo
@@ -1,4 +1,7 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
//Copyright (C) 2013 David Braam
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkirtBrim.h"
#include "support.h"
@@ -19,7 +22,7 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
{ // add brim underneath support by removing support where there's brim around the model
const bool include_helper_parts = false; // include manually below
first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
Polygons first_layer_empty_holes;
if (outside_only)
{
@@ -35,7 +38,7 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
// || || ||[]|| > expand to fit an extra brim line
// |+-+| |+--+|
// +---+ +----+
Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2)); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
if (outside_only)
{ // don't remove support within empty holes where no brim is generated.
model_brim_covered_area.add(first_layer_empty_holes);
@@ -45,12 +48,20 @@ void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned i
first_layer_outline.add(support_layer.supportAreas);
first_layer_outline.add(support_layer.skin);
}
if (storage.primeTower.enabled)
{
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
}
}
constexpr int join_distance = 20;
first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
constexpr int smallest_line_length = 200;
constexpr int largest_error_of_removed_point = 50;
first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
if (first_layer_outline.size() == 0)
{
logError("Couldn't generate skirt / brim! No polygons on first layer.");
}
}
int SkirtBrim::generatePrimarySkirtBrimLines(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const int64_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder)
+12
Ver Arquivo
@@ -12,6 +12,12 @@ WallsComputation::WallsComputation(int wall_0_inset, int line_width_0, int line_
{
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInsets only reads and writes data for the current layer
*/
void WallsComputation::generateInsets(SliceLayerPart* part)
{
if (insetCount == 0)
@@ -58,6 +64,12 @@ void WallsComputation::generateInsets(SliceLayerPart* part)
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInsets only reads and writes data for the current layer
*/
void WallsComputation::generateInsets(SliceLayer* layer)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
+2 -2
Ver Arquivo
@@ -389,7 +389,7 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
{
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
{
const PolygonRef upperPart = parts1[prt];
ConstPolygonRef upperPart = parts1[prt];
ClosestPolygonPoint closestInPoly = PolygonUtils::findClosest(start_close_to, upperPart);
@@ -439,7 +439,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
for (unsigned int prt = 0 ; prt < supported.size(); prt++)
{
const PolygonRef upperPart = supported[prt];
ConstPolygonRef upperPart(supported[prt]);
parts.emplace_back(prt);
+10 -9
Ver Arquivo
@@ -18,8 +18,9 @@ void Wireframe2gcode::writeGCode()
{
gcode.preSetup(wireFrame.meshgroup);
gcode.setInitialTemps(*wireFrame.meshgroup);
const unsigned int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr"); // TODO: figure out how Wireframe works with dual extrusion
gcode.setInitialTemps(*wireFrame.meshgroup, start_extruder_nr);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
@@ -159,7 +160,7 @@ void Wireframe2gcode::writeGCode()
gcode.setZ(maxObjectHeight);
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.updateTotalPrintTime();
@@ -259,7 +260,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
Point3 lower = to - lowering;
gcode.writeMove(lower, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
gcode.writeMove(to + lowering, speedUp, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -268,7 +269,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
} else
{
gcode.writeMove(to, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -467,14 +468,14 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
void Wireframe2gcode::writeMoveWithRetract(Point3 to)
{
if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
void Wireframe2gcode::writeMoveWithRetract(Point to)
{
if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
@@ -562,7 +563,7 @@ void Wireframe2gcode::processStartingCode()
{
if (getSettingBoolean("material_bed_temp_prepend"))
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") != 0)
{
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
}
@@ -603,7 +604,7 @@ void Wireframe2gcode::processStartingCode()
constexpr bool wait = true;
gcode.writeTemperatureCommand(start_extruder_nr, getSettingInDegreeCelsius("material_print_temperature"), wait);
gcode.writePrimeTrain(getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&standard_retraction_config);
gcode.writeRetraction(standard_retraction_config);
}
}
+1 -1
Ver Arquivo
@@ -5,7 +5,7 @@
namespace cura {
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
{
AABB boundaryBox(outline);
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
+1 -1
Ver Arquivo
@@ -6,7 +6,7 @@ namespace cura {
class Polygons;
class SliceLayer;
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer);
}//namespace cura
+5 -3
Ver Arquivo
@@ -215,7 +215,7 @@ public:
/*!
* Adds closed polygon to the current path
*/
void sendPolygon(PrintFeatureType print_feature_type, Polygon poly, int width);
void sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef poly, int width);
private:
/*!
* Convert and add a point to the points buffer, each point being represented as two consecutive floats. All members adding a 2D point to the data should use this function.
@@ -522,7 +522,7 @@ void CommandSocket::sendPolygons(PrintFeatureType type, const Polygons& polygons
#endif
}
void CommandSocket::sendPolygon(PrintFeatureType type, Polygon& polygon, int line_width)
void CommandSocket::sendPolygon(PrintFeatureType type, ConstPolygonRef polygon, int line_width)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
@@ -642,6 +642,7 @@ void CommandSocket::sendLayerData()
{
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : data.slice_data) //Note: This is in no particular order!
{
logDebug("Sending layer data for layer %i of %i.\n", entry.first, data.slice_data.size());
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
data.sliced_objects = 0;
@@ -667,6 +668,7 @@ void CommandSocket::sendOptimizedLayerData()
{
for (std::pair<const int, std::shared_ptr<cura::proto::LayerOptimized>> entry : data.slice_data) //Note: This is in no particular order!
{
logDebug("Sending layer data for layer %i of %i.\n", entry.first, data.slice_data.size());
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
data.sliced_objects = 0;
@@ -797,7 +799,7 @@ void CommandSocket::PathCompiler::sendLineTo(PrintFeatureType print_feature_type
}
}
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, Polygon polygon, int width)
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef polygon, int width)
{
if (polygon.size() < 2)
{
+1 -1
Ver Arquivo
@@ -64,7 +64,7 @@ public:
/*!
* Send a polygon to the front-end. This is used for the layerview in the GUI
*/
static void sendPolygon(cura::PrintFeatureType type, Polygon& polygon, int line_width);
static void sendPolygon(cura::PrintFeatureType type, ConstPolygonRef polygon, int line_width);
/*!
* Send a line to the front-end. This is used for the layerview in the GUI
+67 -28
Ver Arquivo
@@ -11,6 +11,8 @@
namespace cura {
double layer_height; //!< report basic layer height in RepRap gcode file.
GCodeExport::GCodeExport()
: output_stream(&std::cout)
, currentPosition(0,0,MM2INT(20))
@@ -34,6 +36,8 @@ GCodeExport::GCodeExport()
initial_bed_temp = 0;
extruder_count = 0;
total_bounding_box = AABB3D();
}
GCodeExport::~GCodeExport()
@@ -49,14 +53,19 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
for (const Mesh& mesh : meshgroup->meshes)
{
extruder_attr[mesh.getSettingAsIndex("extruder_nr")].is_used = true;
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
extruder_attr[mesh.getSettingAsIndex("extruder_nr")].is_used = true;
}
}
for (unsigned int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
const ExtruderTrain* train = meshgroup->getExtruderTrain(extruder_nr);
if (meshgroup->getSettingAsIndex("adhesion_extruder_nr") == int(extruder_nr))
if (meshgroup->getSettingAsIndex("adhesion_extruder_nr") == int(extruder_nr) && meshgroup->getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
extruder_attr[extruder_nr].is_used = true;
}
@@ -90,6 +99,8 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
machine_name = meshgroup->getSettingString("machine_name");
layer_height = meshgroup->getSettingInMillimeters("layer_height");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
@@ -102,17 +113,19 @@ void GCodeExport::preSetup(const MeshGroup* meshgroup)
estimateCalculator.setFirmwareDefaults(meshgroup);
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
void GCodeExport::setInitialTemps(const MeshGroup& settings, const unsigned int start_extruder_nr)
{
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
const ExtruderTrain* extr_train = settings.getExtruderTrain(extr_nr);
assert(extr_train);
double temp = extr_train->getSettingInDegreeCelsius((extr_nr == 0)? "material_print_temperature" : "material_standby_temperature");
const ExtruderTrain& train = *settings.getExtruderTrain(extr_nr);
double print_temp_0 = train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
double print_temp_here = (print_temp_0 != 0)? print_temp_0 : train.getSettingInDegreeCelsius("material_print_temperature");
double temp = (extr_nr == start_extruder_nr)? print_temp_here : train.getSettingInDegreeCelsius("material_standby_temperature");
setInitialTemp(extr_nr, temp);
}
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature");
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature_layer_0");
}
void GCodeExport::setInitialTemp(int extruder_nr, double temp)
@@ -163,12 +176,12 @@ std::string GCodeExport::getFileHeader(const double* print_time, const std::vect
prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line;
}
prefix << ";PRINT.SIZE.MIN.X:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Y:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Z:0" << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(machine_dimensions.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(machine_dimensions.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(machine_dimensions.z) << new_line;
prefix << ";PRINT.SIZE.MIN.X:" << INT2MM(total_bounding_box.min.x) << new_line;
prefix << ";PRINT.SIZE.MIN.Y:" << INT2MM(total_bounding_box.min.y) << new_line;
prefix << ";PRINT.SIZE.MIN.Z:" << INT2MM(total_bounding_box.min.z) << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(total_bounding_box.max.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(total_bounding_box.max.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(total_bounding_box.max.z) << new_line;
prefix << ";END_OF_HEADER" << new_line;
return prefix.str();
default:
@@ -182,6 +195,11 @@ std::string GCodeExport::getFileHeader(const double* print_time, const std::vect
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// TODO: the second nozzle size isn't always initiated! ";NOZZLE_DIAMETER2:"
}
else if (flavor == EGCodeFlavor::REPRAP)
{
prefix << ";Filament used: " << ((filament_used.size() >= 1)? filament_used[0] / (1000 * extruder_attr[0].filament_area) : 0) << "m" << new_line;
prefix << ";Layer height: " << layer_height << new_line;
}
return prefix.str();
}
}
@@ -252,7 +270,7 @@ void GCodeExport::setFlavor(EGCodeFlavor flavor)
}
}
EGCodeFlavor GCodeExport::getFlavor()
EGCodeFlavor GCodeExport::getFlavor() const
{
return this->flavor;
}
@@ -550,6 +568,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
double extrusion_per_mm = mm3ToE(extrusion_mm3_per_mm);
Point gcode_pos = getGcodePos(x,y, current_extruder);
total_bounding_box.include(Point3(gcode_pos.X, gcode_pos.Y, z));
if (extrusion_mm3_per_mm > 0.000001)
{
@@ -623,7 +642,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool extruder_switch)
void GCodeExport::writeRetraction(const RetractionConfig& config, bool force, bool extruder_switch)
{
ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
@@ -640,7 +659,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
}
double old_retraction_e_amount = extr_attr.retraction_e_amount_current;
double new_retraction_e_amount = mmToE(config->distance);
double new_retraction_e_amount = mmToE(config.distance);
double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount;
if (std::abs(retraction_diff_e_amount) < 0.000001)
{
@@ -650,23 +669,23 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
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)
if (!force && config.retraction_count_max <= 0)
{
return;
}
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extr_attr.filament_area)
if (!force && int(extruded_volume_at_previous_n_retractions.size()) == config.retraction_count_max
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config.retraction_extrusion_window * extr_attr.filament_area)
{
return;
}
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (int(extruded_volume_at_previous_n_retractions.size()) == config->retraction_count_max + 1)
if (int(extruded_volume_at_previous_n_retractions.size()) == config.retraction_count_max + 1)
{
extruded_volume_at_previous_n_retractions.pop_back();
}
@@ -689,17 +708,17 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
}
else
{
double speed = ((retraction_diff_e_amount < 0.0)? config->speed : extr_attr.last_retraction_prime_speed) * 60;
double speed = ((retraction_diff_e_amount < 0.0)? config.speed : extr_attr.last_retraction_prime_speed) * 60;
current_e_value += retraction_diff_e_amount;
*output_stream << "G1 F" << PrecisionedDouble{1, speed} << " "
<< extr_attr.extruderCharacter << PrecisionedDouble{5, current_e_value} << new_line;
currentSpeed = speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
extr_attr.last_retraction_prime_speed = config->primeSpeed;
extr_attr.last_retraction_prime_speed = config.primeSpeed;
}
extr_attr.retraction_e_amount_current = new_retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
extr_attr.prime_volume += config->prime_volume;
extr_attr.prime_volume += config.prime_volume;
}
@@ -709,6 +728,16 @@ void GCodeExport::writeZhopStart(int hop_height)
{
isZHopped = hop_height;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z + isZHopped} << new_line;
total_bounding_box.includeZ(currentPosition.z + isZHopped);
}
}
void GCodeExport::writeZhopEnd()
{
if (isZHopped)
{
isZHopped = 0;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z} << new_line;
}
}
@@ -746,7 +775,7 @@ void GCodeExport::switchExtruder(int new_extruder, const RetractionConfig& retra
bool force = true;
bool extruder_switch = true;
writeRetraction(&const_cast<RetractionConfig&>(retraction_config_old_extruder), force, extruder_switch);
writeRetraction(retraction_config_old_extruder, force, extruder_switch);
resetExtrusionValue(); // zero the E value on the old extruder, so that the current_e_value is registered on the old extruder
@@ -813,7 +842,12 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
{
if (!wait && extruder_attr[extruder].currentTemperature == temperature)
return;
if (flavor == EGCodeFlavor::ULTIGCODE)
{ // The UM2 family doesn't support temperature commands (they are fixed in the firmware)
return;
}
if (wait)
*output_stream << "M109";
else
@@ -829,6 +863,11 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
{
if (flavor == EGCodeFlavor::ULTIGCODE)
{ // The UM2 family doesn't support temperature commands (they are fixed in the firmware)
return;
}
if (wait)
*output_stream << "M190 S";
else
@@ -868,7 +907,7 @@ void GCodeExport::writeMaxZFeedrate(double max_z_feedrate)
{
if (current_max_z_feedrate != max_z_feedrate)
{
*output_stream << "M203 Z" << int(max_z_feedrate * 60) << new_line;
*output_stream << "M203 Z" << PrecisionedDouble{2, max_z_feedrate} << new_line;
current_max_z_feedrate = max_z_feedrate;
estimateCalculator.setMaxZFeedrate(max_z_feedrate);
}
@@ -883,7 +922,7 @@ void GCodeExport::finalize(const char* endCode)
{
writeFanCommand(0);
writeCode(endCode);
long print_time = getTotalPrintTime();
int64_t print_time = getTotalPrintTime();
int mat_0 = getTotalFilamentUsed(0);
log("Print time: %d\n", print_time);
log("Print time (readable): %dh %dm %ds\n", print_time / 60 / 60, (print_time / 60) % 60, print_time % 60);
+15 -3
Ver Arquivo
@@ -99,6 +99,8 @@ private:
double current_jerk; //!< The current jerk in the XY direction (in mm/s^3)
double current_max_z_feedrate; //!< The current max z speed
AABB3D total_bounding_box; //!< The bounding box of all g-code.
/*!
* The z position to be used on the next xy move, if the head wasn't in the correct z position yet.
*
@@ -186,7 +188,7 @@ public:
Point getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const;
void setFlavor(EGCodeFlavor flavor);
EGCodeFlavor getFlavor();
EGCodeFlavor getFlavor() const;
void setZ(int z);
@@ -259,7 +261,7 @@ private:
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
public:
void writeRetraction(RetractionConfig* config, bool force = false, bool extruder_switch = false);
void writeRetraction(const RetractionConfig& config, bool force = false, bool extruder_switch = false);
/*!
* Start a z hop with the given \p hop_height
@@ -268,6 +270,12 @@ public:
*/
void writeZhopStart(int hop_height);
/*!
* End a z hop: go back to the layer height
*
*/
void writeZhopEnd();
/*!
* Start the new_extruder:
* - set new extruder
@@ -341,13 +349,17 @@ public:
* See FffGcodeWriter::processStartingCode
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
* \param start_extruder_nr The extruder with which to start this print
*/
void setInitialTemps(const MeshGroup& settings);
void setInitialTemps(const MeshGroup& settings, const unsigned int start_extruder_nr);
/*!
* Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
* This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
*
* \warning This function must be called before any of the layers in the meshgroup are written to file!
* That's because it sets the current temperature in the gcode!
*
* \param extruder_nr The extruder number for which to better specify the temp
* \param temp The temp at which the nozzle should be at startup
*/
+132 -175
Ver Arquivo
@@ -5,28 +5,19 @@
#include "sliceDataStorage.h"
#include "utils/polygonUtils.h"
#include "MergeInfillLines.h"
#include "raft.h" // getTotalExtraLayers
namespace cura {
TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other)
{
extrude_time -= other.extrude_time;
unretracted_travel_time -= other.unretracted_travel_time;
retracted_travel_time -= other.retracted_travel_time;
material -= other.material;
return *this;
}
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
: extruder(extruder)
, required_temp(-1)
, heated_pre_travel_time(0)
, initial_printing_temperature(-1)
, printing_temperature(-1)
, start_position(start_position)
, layer_nr(layer_nr)
, is_initial_layer(is_initial_layer)
, layer_thickness(layer_thickness)
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
, retraction_config(retraction_config)
@@ -65,7 +56,7 @@ double ExtruderPlan::getFanSpeed()
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
GCodePath* GCodePlanner::getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow) // spiralize can only change when a travel path is in between
@@ -73,6 +64,7 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceF
paths.emplace_back();
GCodePath* ret = &paths.back();
ret->retract = false;
ret->perform_prime = false;
ret->perform_z_hop = false;
ret->config = config;
ret->done = false;
@@ -89,22 +81,26 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(const SliceDataStorage& storage, int layer_nr, int z, int layer_thickness, PlanningState last_planned_state, const std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, configs_storage(storage, layer_nr, layer_thickness)
, layer_nr(layer_nr)
, is_initial_layer(layer_nr == 0 - Raft::getTotalExtraLayers(storage))
, z(z)
, layer_thickness(layer_thickness)
, start_position(last_position)
, lastPosition(last_position)
, last_extruder_previous_layer(current_extruder)
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(current_extruder))
, start_position(last_planned_state.last_position)
, lastPosition(last_planned_state.last_position)
, has_prime_tower_planned(false)
, last_extruder_previous_layer(last_planned_state.current_extruder)
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(last_planned_state.current_extruder))
, comb_boundary_inside(computeCombBoundaryInside(combing_mode))
, fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder)
{
int current_extruder = last_planned_state.current_extruder;
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
comb = nullptr;
was_inside = is_inside_mesh;
was_inside = last_planned_state.is_inside_mesh_layer_part;
is_inside = false; // assumes the next move will not be to inside a layer part (overwritten just before going into a layer part)
if (combing_mode != CombingMode::OFF)
{
@@ -146,12 +142,12 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
else
{
Polygons comb_boundary;
for (SliceMeshStorage& mesh : storage.meshes)
for (const SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
const SliceLayer& layer = mesh.layers[layer_nr];
if (mesh.getSettingAsCombingMode("retraction_combing") == CombingMode::NO_SKIN)
{
for (SliceLayerPart& part : layer.parts)
for (const SliceLayerPart& part : layer.parts)
{
comb_boundary.add(part.infill_area);
}
@@ -202,7 +198,8 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
assert((int)extruder_plans.size() <= storage.meshgroup->getExtruderCount() && "Never use the same extruder twice on one layer!");
}
last_planned_extruder_setting_base = storage.meshgroup->getExtruderTrain(extruder);
@@ -244,15 +241,15 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
}
}
void GCodePlanner::addTravel(Point p)
GCodePath& GCodePlanner::addTravel(Point p)
{
GCodePath* path = nullptr;
GCodePathConfig& travel_config = storage.travel_config_per_extruder[getExtruder()];
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[getExtruder()];
const GCodePathConfig& travel_config = configs_storage.travel_config_per_extruder[getExtruder()];
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[getExtruder()];
bool combed = false;
SettingsBaseVirtual* extr = getLastPlannedExtruderTrainSettings();
const SettingsBaseVirtual* extr = getLastPlannedExtruderTrainSettings();
const bool perform_z_hops = extr->getSettingBoolean("retraction_hop_enabled");
@@ -330,43 +327,78 @@ void GCodePlanner::addTravel(Point p)
}
}
addTravel_simple(p, path);
GCodePath& ret = addTravel_simple(p, path);
was_inside = is_inside;
return ret;
}
void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
GCodePath& GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
path = getLatestPathWithConfig(&storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
path = getLatestPathWithConfig(&configs_storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
}
path->points.push_back(p);
lastPosition = p;
return *path;
}
void GCodePlanner::planPrime()
{
forceNewPathStart();
GCodePath& prime_travel = addTravel_simple(lastPosition + Point(0, 100));
prime_travel.retract = false;
prime_travel.perform_prime = true;
forceNewPathStart();
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
void GCodePlanner::addExtrusionMove(Point p, const GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
getLatestPathWithConfig(config, space_fill_type, flow, spiralize)->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, bool spiralize)
void GCodePlanner::addPolygon(ConstPolygonRef polygon, int start_idx, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize)
{
Point p0 = polygon[startIdx];
Point p0 = polygon[start_idx];
addTravel(p0);
for(unsigned int i=1; i<polygon.size(); i++)
for (unsigned int point_idx = 1; point_idx < polygon.size(); point_idx++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[startIdx];
const Point& p1 = polygon[start_idx];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
if (wall_0_wipe_dist > 0)
{ // apply outer wall wipe
p0 = polygon[start_idx];
int distance_traversed = 0;
for (unsigned int point_idx = 1; ; point_idx++)
{
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
int p0p1_dist = vSize(p1 - p0);
if (distance_traversed + p0p1_dist >= wall_0_wipe_dist)
{
Point vector = p1 - p0;
Point half_way = p0 + normal(vector, wall_0_wipe_dist - distance_traversed);
addTravel_simple(half_way);
break;
}
else
{
addTravel_simple(p1);
distance_traversed += p0p1_dist;
}
p0 = p1;
}
forceNewPathStart();
}
}
else
{
@@ -374,13 +406,13 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, bool spiralize)
void GCodePlanner::addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, Point z_seam_pos, coord_t wall_0_wipe_dist, bool spiralize)
{
if (polygons.size() == 0)
{
return;
}
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_type);
PathOrderOptimizer orderOptimizer(lastPosition, z_seam_pos, z_seam_type);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
orderOptimizer.addPolygon(polygons[poly_idx]);
@@ -388,10 +420,10 @@ void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* c
orderOptimizer.optimize();
for (unsigned int poly_idx : orderOptimizer.polyOrder)
{
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, spiralize);
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, wall_0_wipe_dist, spiralize);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
void GCodePlanner::addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
{
LineOrderOptimizer orderOptimizer(lastPosition);
for (unsigned int line_idx = 0; line_idx < polygons.size(); line_idx++)
@@ -401,12 +433,12 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
orderOptimizer.optimize();
for (int poly_idx : orderOptimizer.polyOrder)
{
PolygonRef polygon = polygons[poly_idx];
ConstPolygonRef polygon = polygons[poly_idx];
int start = orderOptimizer.polyStart[poly_idx];
int end = 1 - start;
Point& p0 = polygon[start];
const Point& p0 = polygon[start];
addTravel(p0);
Point& p1 = polygon[end];
const Point& p1 = polygon[end];
addExtrusionMove(p1, config, space_fill_type);
if (wipe_dist != 0)
{
@@ -524,7 +556,7 @@ TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time)
{
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
const FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
totalPrintTime = estimates.getTotalTime();
if (force_minimal_layer_time)
@@ -563,22 +595,23 @@ void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_t
}
/*
Supposing no influence of minimal layer time; i.e. layer time > min layer time fan speed min:
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
zero..|/__________________
layer nr >
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
speed_0..|/
|
|__________________
layer nr >
*/
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
fan_speed = fan_speed * std::max(0, layer_nr) / fsml.cool_fan_full_layer;
fan_speed = fsml.cool_fan_speed_0 + (fan_speed - fsml.cool_fan_speed_0) * std::max(0, layer_nr) / fsml.cool_fan_full_layer;
}
}
@@ -606,18 +639,22 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
void GCodePlanner::writeGCode(GCodeExport& gcode)
{
completeConfigs();
CommandSocket::setLayerForSend(layer_nr);
CommandSocket::setSendCurrentPosition( gcode.getPositionXY() );
gcode.setLayerNr(layer_nr);
gcode.writeLayerComment(layer_nr);
if (layer_nr == 1 - Raft::getTotalExtraLayers(storage))
{
bool wait = false;
gcode.writeBedTemperatureCommand(storage.getSettingInDegreeCelsius("material_bed_temperature"), wait);
}
gcode.setZ(z);
GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
const GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
int extruder = gcode.getExtruderNr();
bool acceleration_enabled = storage.getSettingBoolean("acceleration_enabled");
@@ -626,7 +663,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
for(unsigned int extruder_plan_idx = 0; extruder_plan_idx < extruder_plans.size(); extruder_plan_idx++)
{
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder_plan.extruder];
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[extruder_plan.extruder];
if (extruder != extruder_plan.extruder)
{
@@ -642,19 +679,29 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
{ // require printing temperature to be met
constexpr bool wait = true;
gcode.writeTemperatureCommand(extruder, extruder_plan.required_temp, wait);
gcode.writeTemperatureCommand(extruder, extruder_plan.initial_printing_temperature, wait);
}
// prime extruder if it hadn't been used yet
gcode.writePrimeTrain(storage.meshgroup->getExtruderTrain(extruder)->getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
if (extruder_plan.prev_extruder_standby_temp)
{ // turn off previous extruder
constexpr bool wait = false;
gcode.writeTemperatureCommand(prev_extruder, *extruder_plan.prev_extruder_standby_temp, wait);
double prev_extruder_temp = *extruder_plan.prev_extruder_standby_temp;
int prev_layer_nr = (extruder_plan_idx == 0)? layer_nr - 1 : layer_nr;
if (prev_layer_nr == storage.max_print_height_per_extruder[prev_extruder])
{
prev_extruder_temp = 0; // TODO ? should there be a setting for extruder_off_temperature ?
}
gcode.writeTemperatureCommand(prev_extruder, prev_extruder_temp, wait);
}
}
else if (extruder_plan_idx == 0 && layer_nr != 0 && storage.meshgroup->getExtruderTrain(extruder)->getSettingBoolean("retract_at_layer_change"))
{
gcode.writeRetraction(retraction_config);
}
gcode.writeFanCommand(extruder_plan.getFanSpeed());
std::vector<GCodePath>& paths = extruder_plan.paths;
@@ -677,6 +724,12 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
GCodePath& path = paths[path_idx];
if (path.perform_prime)
{
gcode.writePrimeTrain(train->getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(retraction_config);
}
if (acceleration_enabled)
{
gcode.writeAcceleration(path.config->getAcceleration());
@@ -688,11 +741,15 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
if (path.retract)
{
gcode.writeRetraction(&retraction_config);
gcode.writeRetraction(retraction_config);
if (path.perform_z_hop)
{
gcode.writeZhopStart(retraction_config.zHop);
}
else
{
gcode.writeZhopEnd();
}
}
if (!path.config->isTravelPath() && last_extrusion_config != path.config)
{
@@ -708,7 +765,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
else
speed *= extruder_plan.getExtrudeSpeedFactor();
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config_per_extruder[extruder], nozzle_size, speed_equalize_flow_enabled, speed_equalize_flow_max).mergeInfillLines(path_idx)) // !! has effect on path_idx !!
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, configs_storage.travel_config_per_extruder[extruder], nozzle_size, speed_equalize_flow_enabled, speed_equalize_flow_max).mergeInfillLines(path_idx)) // !! has effect on path_idx !!
{ // !! has effect on path_idx !!
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
continue;
@@ -731,7 +788,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
bool spiralize = path.spiralize;
if (!spiralize) // normal (extrusion) move (with coasting
{
CoastingConfig& coasting_config = storage.coasting_config[extruder];
const CoastingConfig& coasting_config = storage.coasting_config[extruder];
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
@@ -802,15 +859,15 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
if (train->getSettingBoolean("cool_lift_head") && extruder_plan.extraTime > 0.0)
{
gcode.writeComment("Small layer, adding delay");
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
gcode.writeRetraction(&retraction_config);
const RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
gcode.writeRetraction(retraction_config);
if (extruder_plan_idx == extruder_plans.size() - 1 || !train->getSettingBoolean("machine_extruder_end_pos_abs"))
{ // only move the head if it's the last extruder plan; otherwise it's already at the switching bay area
// or do it anyway when we switch extruder in-place
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), storage.travel_config_per_extruder[extruder].getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY(), configs_storage.travel_config_per_extruder[extruder].getSpeed(), 0);
// TODO: is this safe?! wouldn't the head move into the sides then?!
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config_per_extruder[extruder].getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), configs_storage.travel_config_per_extruder[extruder].getSpeed(), 0);
}
gcode.writeDelay(extruder_plan.extraTime);
}
@@ -830,106 +887,6 @@ void GCodePlanner::overrideFanSpeeds(double speed)
}
void GCodePlanner::completeConfigs()
{
storage.support_config.setLayerHeight(layer_thickness);
storage.support_skin_config.setLayerHeight(layer_thickness);
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setLayerHeight(layer_thickness);
mesh.insetX_config.setLayerHeight(layer_thickness);
mesh.skin_config.setLayerHeight(layer_thickness);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setLayerHeight(layer_thickness);
}
}
storage.primeTower.setConfigs(storage.meshgroup, layer_thickness);
processInitialLayersSpeedup();
}
void GCodePlanner::processInitialLayersSpeedup()
{
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
if (layer_nr >= 0 && layer_nr < initial_speedup_layers)
{
GCodePathConfig::BasicConfig initial_layer_speed_config;
int extruder_nr_support_infill = storage.getSettingAsIndex((layer_nr == 0)? "support_extruder_nr_layer_0" : "support_infill_extruder_nr");
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("speed_print_layer_0");
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("acceleration_print_layer_0");
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("jerk_print_layer_0");
//Support (global).
storage.support_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Support roof (global).
int extruder_nr_support_skin = storage.getSettingAsIndex("support_interface_extruder_nr");
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("speed_print_layer_0");
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("acceleration_print_layer_0");
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("jerk_print_layer_0");
storage.support_skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
{
const ExtruderTrain* extruder_train = storage.meshgroup->getExtruderTrain(extruder_nr);
initial_layer_speed_config.speed = extruder_train->getSettingInMillimetersPerSecond("speed_travel_layer_0");
initial_layer_speed_config.acceleration = extruder_train->getSettingInMillimetersPerSecond("acceleration_travel_layer_0");
initial_layer_speed_config.jerk = extruder_train->getSettingInMillimetersPerSecond("jerk_travel_layer_0");
//Travel speed (per extruder).
storage.travel_config_per_extruder[extruder_nr].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
}
for (SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed_config.speed = mesh.getSettingInMillimetersPerSecond("speed_print_layer_0");
initial_layer_speed_config.acceleration = mesh.getSettingInMillimetersPerSecond("acceleration_print_layer_0");
initial_layer_speed_config.jerk = mesh.getSettingInMillimetersPerSecond("jerk_print_layer_0");
//Outer wall speed (per mesh).
mesh.inset0_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Inner wall speed (per mesh).
mesh.insetX_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Skin speed (per mesh).
mesh.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
{
//Infill speed (per combine part per mesh).
mesh.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
}
}
}
else if (layer_nr == initial_speedup_layers) //At the topmost layer of the gradient, reset all speeds to the typical speeds.
{
storage.support_config.setSpeedIconic();
storage.support_skin_config.setSpeedIconic();
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
{
storage.travel_config_per_extruder[extruder_nr].setSpeedIconic();
}
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setSpeedIconic();
mesh.insetX_config.setSpeedIconic();
mesh.skin_config.setSpeedIconic();
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setSpeedIconic();
}
}
}
}
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
{
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
+89 -280
Ver Arquivo
@@ -6,6 +6,9 @@
#include "gcodeExport.h"
#include "pathPlanning/Comb.h"
#include "pathPlanning/GCodePath.h"
#include "pathPlanning/NozzleTempInsert.h"
#include "pathPlanning/TimeMaterialEstimates.h"
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
@@ -13,6 +16,7 @@
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
#include "GCodePathConfig.h"
#include "settings/PathConfigStorage.h"
#include "utils/optional.h"
@@ -21,249 +25,6 @@ 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 ExtruderPlan; // 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 ExtruderPlan; // cause there the naive estimates are calculated
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion)
double material; //!< Material used (in mm^3)
public:
/*!
* Basic contructor
*
* \param extrude_time Time in seconds occupied by extrusion
* \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion)
* \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion)
* \param material Material used (in mm^3)
*/
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material)
: extrude_time(extrude_time)
, unretracted_travel_time(unretracted_travel_time)
, retracted_travel_time(retracted_travel_time)
, material(material)
{
}
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
, retracted_travel_time(0.0)
, material(0.0)
{
}
/*!
* Set all estimates to zero.
*/
void reset()
{
extrude_time = 0.0;
unretracted_travel_time = 0.0;
retracted_travel_time = 0.0;
material = 0.0;
}
/*!
* Pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return The resulting estimates
*/
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
}
/*!
* In place pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return These estimates
*/
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other)
{
extrude_time += other.extrude_time;
unretracted_travel_time += other.unretracted_travel_time;
retracted_travel_time += other.retracted_travel_time;
material += other.material;
return *this;
}
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
*
* Each of the estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
* This causes the estimates in this instance to change. Each of the
* estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return A reference to this instance.
*/
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
/*!
* Get total time estimate. The different time estimate member values added together.
*
* \return the total of all different time estimate values
*/
double getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
/*!
* Get the total time during which the head is not retracted.
*
* This includes extrusion time and non-retracted travel time
*
* \return the total time during which the head is not retracted.
*/
double getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
/*!
* Get the total travel time.
*
* This includes the retracted travel time as well as the unretracted travel time.
*
* \return the total travel time.
*/
double getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
/*!
* Get the extrusion time.
*
* \return extrusion time.
*/
double getExtrudeTime() const
{
return extrude_time;
}
/*!
* Get the amount of material used in mm^3.
*
* \return amount of material
*/
double getMaterial() const
{
return material;
}
};
/*!
* A class for representing a planned path.
*
* A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc.
*
* This is a compact premature representation in which are line segments have the same config, i.e. the config of this path.
*
* In the final representation (gcode) each line segment may have different properties,
* which are added when the generated GCodePaths are processed.
*/
class GCodePath
{
public:
GCodePathConfig* config; //!< The configuration settings of the path.
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
bool perform_z_hop; //!< Whether to perform a z_hop in this path, which is assumed to be a travel path.
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
/*!
* Whether this config is the config of a travel path.
*
* \return Whether this config is the config of a travel path.
*/
bool isTravelPath()
{
return config->isTravelPath();
}
/*!
* Get the material flow in mm^3 per mm traversed.
*
* \warning Can only be called after the layer height has been set (which is done while writing the gcode!)
*
* \return The flow
*/
double getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
};
class GCodePlanner; // forward declaration so that ExtruderPlan can be a friend
class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend
@@ -281,7 +42,10 @@ protected:
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
int extruder; //!< The extruder used for this paths in the current plan.
double required_temp; //!< The required temperature at the start of this extruder plan.
double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature
double initial_printing_temperature; //!< The required temperature at the start of this extruder plan.
double printing_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the initial and final print temp
std::optional<std::list<NozzleTempInsert>::iterator> printing_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing temperature of the next extruder plan (if it has the same extruder).
std::optional<double> prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder.
TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan.
@@ -294,7 +58,7 @@ public:
* \param extruder The extruder number for which this object is a plan.
* \param start_position The position the head is when this extruder plan starts
*/
ExtruderPlan(int extruder, Point start_position, int layer_nr, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
/*!
* Add a new Insert, constructed with the given arguments
@@ -398,9 +162,11 @@ protected:
Point start_position; //!< The position the print head was at at the start of this extruder plan
int layer_nr; //!< The layer number at which we are currently printing.
bool is_initial_layer; //!< Whether this extruder plan is printed on the very first layer (which might be raft)
int layer_thickness; //!< The thickness of this layer in Z-direction
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
const FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
const RetractionConfig& retraction_config; //!< The retraction settings for the extruder of this plan
@@ -450,10 +216,27 @@ class GCodePlanner : public NoCopy
{
friend class LayerPlanBuffer;
friend class GCodePlannerTest;
public:
/*!
* The state which is passed along between layer plans.
* This is what a \ref GCodePlanner delivers to further computation in \ref FffGcodeWriter
* This is the state which is currently planned, not which is written to gcode.
*/
struct PlanningState
{
Point last_position; //!< The position of the head before planning the next layer
int current_extruder; //!< The extruder train in use before planning the next layer
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
};
private:
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
const SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
public:
const PathConfigStorage configs_storage; //!< The line configs for this layer for each feature type
private:
int layer_nr; //!< The layer number of this layer plan
int is_initial_layer; //!< Whether this is the first layer (which might be raft)
int z;
@@ -461,7 +244,9 @@ private:
Point start_position;
Point lastPosition;
bool has_prime_tower_planned;
std::vector<ExtruderPlan> extruder_plans; //!< should always contain at least one ExtruderPlan
int last_extruder_previous_layer; //!< The last id of the extruder with which was printed in the previous layer
@@ -472,7 +257,7 @@ private:
Comb* comb;
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
const std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
private:
/*!
@@ -485,8 +270,9 @@ private:
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
GCodePath* getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
public:
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
*
@@ -498,7 +284,7 @@ private:
* - when changing extruder, the same travel config is used, but its extruder field is changed.
*/
void forceNewPathStart();
public:
/*!
*
* \param fan_speed_layer_time_settings_per_extruder The fan speed and layer time settings for each extruder.
@@ -507,7 +293,7 @@ public:
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(const SliceDataStorage& storage, int layer_nr, int z, int layer_height, PlanningState last_planned_state, const std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
void overrideFanSpeeds(double speed);
@@ -525,12 +311,21 @@ private:
Polygons computeCombBoundaryInside(CombingMode combing_mode);
public:
int getLayerNr()
int getLayerNr() const
{
return layer_nr;
}
Point getLastPosition()
PlanningState getPlanningState() const
{
PlanningState ret;
ret.last_position = lastPosition;
ret.current_extruder = getExtruder();
ret.is_inside_mesh_layer_part = was_inside;
return ret;
}
Point getLastPosition() const
{
return lastPosition;
}
@@ -538,14 +333,25 @@ public:
/*!
* return whether the last position planned was inside the mesh (used in combing)
*/
bool getIsInsideMesh()
bool getIsInsideMesh() const
{
return was_inside;
}
bool getPrimeTowerIsPlanned() const
{
return has_prime_tower_planned;
}
void setPrimeTowerIsPlanned()
{
has_prime_tower_planned = true;
}
/*!
* send a line segment through the command socket from the previous point to the given point \p to
*/
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width)
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width) const
{
CommandSocket::sendLineTo(print_feature_type, to, line_width);
}
@@ -557,13 +363,19 @@ public:
* Features like prime tower and support are considered outside.
*/
void setIsInside(bool going_to_comb);
/*!
* Plan a switch to a new extruder
*
* \param extruder The extruder number to which to switch
* \return whether the extruder has changed
*/
bool setExtruder(int extruder);
/*!
* Get the last planned extruder.
*/
int getExtruder()
int getExtruder() const
{
return extruder_plans.back().extruder;
}
@@ -576,7 +388,7 @@ public:
*
* \param p The point to travel to
*/
void addTravel(Point p);
GCodePath& addTravel(Point p);
/*!
* Add a travel path to a certain point and retract if needed.
@@ -586,7 +398,14 @@ public:
* \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);
GCodePath& addTravel_simple(Point p, GCodePath* path = nullptr);
/*!
* Plan a prime poop at the current location.
*
* \warning A nonretracted move is introduced so that the LayerPlanBuffer classifies this move as an extrusion move.
*/
void planPrime();
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
@@ -597,7 +416,7 @@ public:
* \param flow A modifier of the extrusion width which would follow from the \p config
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
*/
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
void addExtrusionMove(Point p, const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
/*!
* Add polygon to the gcode starting at vertex \p startIdx
@@ -605,9 +424,10 @@ public:
* \param startIdx The index of the starting vertex of the \p polygon
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param wall_0_wipe_dist The distance to travel along the polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
*/
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
void addPolygon(ConstPolygonRef polygon, int startIdx, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add polygons to the gcode with optimized order.
@@ -622,9 +442,11 @@ public:
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param z_seam_type The seam type / poly start optimizer
* \param z_seam_pos The location near where to start each part in case \p z_seam_type is 'back'
* \param wall_0_wipe_dist The distance to travel along each polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
*/
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
void addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add lines to the gcode with optimized order.
@@ -633,7 +455,7 @@ public:
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
void addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
@@ -651,20 +473,7 @@ public:
* \param gcode The gcode to write the planned paths to
*/
void writeGCode(GCodeExport& gcode);
/*!
* Complete all GcodePathConfigs by
* - altering speeds to conform to speed_print_layer_0 and
* speed_travel_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.
+75 -44
Ver Arquivo
@@ -17,11 +17,10 @@ int Infill::computeScanSegmentIdx(int x, int line_width)
return x / line_width;
}
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh)
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
const Polygons* outline = &in_outline;
Polygons outline_offsetted;
switch(pattern)
{
@@ -41,15 +40,21 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, SliceMe
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
outline_offsetted = in_outline.offset(outline_offset - infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
outline = &outline_offsetted;
generateConcentricInfill(*outline, result_polygons, line_distance);
generateConcentricInfill(result_polygons, line_distance);
break;
case EFillMethod::CONCENTRIC_3D:
generateConcentric3DInfill(result_polygons);
break;
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
break;
case EFillMethod::CUBICSUBDIV:
generateCubicSubDivInfill(result_lines, mesh);
if (!mesh)
{
logError("Cannot generate Cubic Subdivision infill without a mesh!\n");
break;
}
generateCubicSubDivInfill(result_lines, *mesh);
break;
default:
logError("Fill pattern has unknown value.\n");
@@ -57,15 +62,53 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, SliceMe
}
}
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
void Infill::generateConcentricInfill(Polygons& result, int inset_value)
{
while(outline.size() > 0)
Polygons first_concentric_wall = in_outline.offset(outline_offset - line_distance + infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
if (perimeter_gaps)
{
result.add(outline);
outline = outline.offset(-inset_value);
}
const Polygons inner = first_concentric_wall.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
const Polygons gaps_here = in_outline.difference(inner);
perimeter_gaps->add(gaps_here);
}
generateConcentricInfill(first_concentric_wall, result, inset_value);
}
void Infill::generateConcentricInfill(Polygons& first_concentric_wall, Polygons& result, int inset_value)
{
result.add(first_concentric_wall);
Polygons* prev_inset = &first_concentric_wall;
Polygons next_inset;
while (prev_inset->size() > 0)
{
next_inset = prev_inset->offset(-inset_value);
result.add(next_inset);
if (perimeter_gaps)
{
const Polygons outer = prev_inset->offset(-infill_line_width / 2 - perimeter_gaps_extra_offset);
const Polygons inner = next_inset.offset(infill_line_width / 2);
const Polygons gaps_here = outer.difference(inner);
perimeter_gaps->add(gaps_here);
}
prev_inset = &next_inset;
}
}
void Infill::generateConcentric3DInfill(Polygons& result)
{
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
Polygons first_wall;
// in contrast to concentric infill we dont do "- infill_line_width / 2" cause this is already handled by the max two lines above
first_wall = in_outline.offset(outline_offset - shift);
generateConcentricInfill(first_wall, result, period);
first_wall = in_outline.offset(outline_offset - period + shift);
generateConcentricInfill(first_wall, result, period);
}
void Infill::generateGridInfill(Polygons& result)
{
@@ -83,14 +126,15 @@ void Infill::generateCubicInfill(Polygons& result)
void Infill::generateTetrahedralInfill(Polygons& result)
{
int shift = int64_t(one_over_sqrt_2 * z) % line_distance;
shift = std::min(shift, line_distance - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, line_distance / 2 - infill_line_width / 2); // don't put lines too close to each other
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle, -shift);
generateLineInfill(result, line_distance, fill_angle + 90, shift);
generateLineInfill(result, line_distance, fill_angle + 90, -shift);
generateLineInfill(result, period, fill_angle, shift);
generateLineInfill(result, period, fill_angle, -shift);
generateLineInfill(result, period, fill_angle + 90, shift);
generateLineInfill(result, period, fill_angle + 90, -shift);
}
void Infill::generateTriangleInfill(Polygons& result)
@@ -100,43 +144,26 @@ void Infill::generateTriangleInfill(Polygons& result)
generateLineInfill(result, line_distance, fill_angle + 120, 0);
}
void Infill::generateCubicSubDivInfill(Polygons& result, SliceMeshStorage* mesh)
void Infill::generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh)
{
if (mesh == nullptr)
{
logError("Cannot generate Cubic Subdivision infill without a mesh!\n");
return;
}
Polygons uncropped;
mesh->base_subdiv_cube->generateSubdivisionLines(z, uncropped);
mesh.base_subdiv_cube->generateSubdivisionLines(z, uncropped);
addLineSegmentsInfill(result, uncropped);
}
void Infill::addLineSegmentsInfill(Polygons& result, Polygons& input)
{
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(from);
p.add(to);
};
ClipperLib::PolyTree interior_segments_tree = in_outline.lineSegmentIntersection(input);
ClipperLib::Paths interior_segments;
ClipperLib::OpenPathsFromPolyTree(interior_segments_tree, interior_segments);
for(uint64_t idx = 0; idx < interior_segments.size(); idx++){
addLine(interior_segments[idx][0], interior_segments[idx][1]);
for (uint64_t idx = 0; idx < interior_segments.size(); idx++)
{
result.addLine(interior_segments[idx][0], interior_segments[idx][1]);
}
}
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t shift)
{
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(rotation_matrix.unapply(from));
p.add(rotation_matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
@@ -162,7 +189,7 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
{ // segment is too short to create infill
continue;
}
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
result.addLine(rotation_matrix.unapply(Point(x, crossings[crossing_idx])), rotation_matrix.unapply(Point(x, crossings[crossing_idx + 1])));
}
scanline_idx += 1;
}
@@ -241,14 +268,18 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
if (outline_offset != 0)
{
outline = in_outline.offset(outline_offset);
if (perimeter_gaps)
{
perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset)));
}
}
else
{
outline = in_outline;
}
outline = outline.offset(infill_overlap);
outline = outline.offset(infill_overlap);
if (outline.size() == 0)
{
return;
+49 -8
Ver Arquivo
@@ -12,7 +12,7 @@
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "infill/subDivCube.h"
#include "infill/SubDivCube.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
@@ -21,6 +21,8 @@ namespace cura
class Infill
{
static constexpr int perimeter_gaps_extra_offset = 15; // extra offset so that the perimeter gaps aren't created everywhere due to rounding errors
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
@@ -30,12 +32,33 @@ class Infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
int64_t z; //!< height of the layer for which we generate infill
int64_t shift; //!< shift of the scanlines in the direction perpendicular to the fill_angle
Polygons* perimeter_gaps; //!< (optional output) The areas in between consecutive insets when Concentric infill is used.
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0)
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, int64_t z, int64_t shift, bool connected_zigzags = false, bool use_endpieces = false)
/*!
* \warning If \p perimeter_gaps is given, then the difference between the \p in_outline
* and the polygons which result from offsetting it by the \p outline_offset
* and then expanding it again by half the \p infill_line_width
* is added to the \p perimeter_gaps
*
* \param[out] perimeter_gaps (optional output) The areas in between consecutive insets when Concentric infill is used.
*/
Infill(EFillMethod pattern
, const Polygons& in_outline
, int outline_offset
, int infill_line_width
, int line_distance
, int infill_overlap
, double fill_angle
, int64_t z
, int64_t shift
, Polygons* perimeter_gaps = nullptr
, bool connected_zigzags = false
, bool use_endpieces = false
)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
@@ -45,6 +68,7 @@ public:
, fill_angle(fill_angle)
, z(z)
, shift(shift)
, perimeter_gaps(perimeter_gaps)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
@@ -56,7 +80,7 @@ public:
* \param result_lines (output) The resulting line segments (from linear infill types)
* \param mesh The mesh for which to geenrate infill (should only be used for non-helper objects)
*/
void generate(Polygons& result_polygons, Polygons& result_lines, SliceMeshStorage* mesh = nullptr);
void generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh = nullptr);
private:
/*!
@@ -70,13 +94,30 @@ private:
* \param line_distance the width of the scan segments
*/
static inline int computeScanSegmentIdx(int x, int line_distance);
/*!
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
* Generate sparse concentric infill
*
* Also adds \ref Inifll::perimeter_gaps between \ref Infill::in_outline and the first wall
*
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateConcentricInfill(Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill starting from a specific outer wall
* \param first_wall The outer wall from which to start
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons& first_wall, Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill
* \param result (output) The resulting polygons
*/
void generateConcentric3DInfill(Polygons& result);
/*!
* Generate a rectangular grid of infill lines
@@ -107,8 +148,8 @@ private:
* \param[out] result The resulting lines
* \param[in] mesh Where the Cubic Subdivision Infill precomputation is stored
*/
void generateCubicSubDivInfill(Polygons& result, SliceMeshStorage* mesh);
void generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh);
/*!
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
* \param result (output) The resulting lines
@@ -1,18 +1,20 @@
#include "subDivCube.h"
#include "SubDivCube.h"
#include <functional>
#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/math.h"
#define ONE_OVER_SQRT_2 0.7071067811865475244008443621048490392848359376884740 //1 / sqrt(2)
#define ONE_OVER_SQRT_3 0.577350269189625764509148780501957455647601751270126876018 //1 / sqrt(3)
#define ONE_OVER_SQRT_6 0.408248290463863016366214012450981898660991246776111688072 //1 / sqrt(6)
#define SQRT_TWO_THIRD 0.816496580927726032732428024901963797321982493552223376144 //sqrt(2 / 3)
namespace cura
{
std::vector<int64_t> SubDivCube::side_length;
std::vector<int64_t> SubDivCube::height;
std::vector<int64_t> SubDivCube::square_height;
std::vector<int64_t> SubDivCube::max_draw_z_diff;
std::vector<int64_t> SubDivCube::max_line_offset;
std::vector<SubDivCube::CubeProperties> SubDivCube::cube_properties_per_recursion_step;
double SubDivCube::radius_multiplier = 1;
int32_t SubDivCube::radius_addition = 0;
Point3Matrix SubDivCube::rotation_matrix;
@@ -34,15 +36,25 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh)
radius_multiplier = mesh.getSettingAsRatio("sub_div_rad_mult");
radius_addition = mesh.getSettingInMicrons("sub_div_rad_add");
double infill_angle = M_PI / 4.0;
coord_t furthest_dist_from_origin = std::sqrt(square(mesh.getSettingInMicrons("machine_height")) + square(mesh.getSettingInMicrons("machine_depth") / 2) + square(mesh.getSettingInMicrons("machine_width") / 2));
coord_t max_side_length = furthest_dist_from_origin * 2;
int curr_recursion_depth = 0;
for (int64_t curr_side_length = mesh.getSettingInMicrons("infill_line_distance") * 2; curr_side_length < 25600000; curr_side_length *= 2) //!< 25600000 is an arbitrarily large number. It is imperative that any infill areas are inside of the cube defined by this number.
const int64_t infill_line_distance = mesh.getSettingInMicrons("infill_line_distance");
if (infill_line_distance > 0)
{
side_length.push_back(curr_side_length);
height.push_back(sqrt(3) * curr_side_length);
square_height.push_back(sqrt(2) * curr_side_length);
max_draw_z_diff.push_back((1.0 / sqrt(3.0)) * curr_side_length);
max_line_offset.push_back((sqrt(1.0 / 6.0) * curr_side_length));
curr_recursion_depth++;
for (int64_t curr_side_length = infill_line_distance * 2; curr_side_length < max_side_length * 2; curr_side_length *= 2)
{
cube_properties_per_recursion_step.emplace_back();
CubeProperties& cube_properties_here = cube_properties_per_recursion_step.back();
cube_properties_here.side_length = curr_side_length;
cube_properties_here.height = sqrt(3) * curr_side_length;
cube_properties_here.square_height = sqrt(2) * curr_side_length;
cube_properties_here.max_draw_z_diff = ONE_OVER_SQRT_3 * curr_side_length;
cube_properties_here.max_line_offset = ONE_OVER_SQRT_6 * curr_side_length;
curr_recursion_depth++;
}
}
Point3 center(0, 0, 0);
@@ -58,12 +70,9 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh)
// / .O. \ | .
// /.~' '~.\ O---->X .
// X """"""""""" Y .
double one_over_sqrt_3 = 1.0 / sqrt(3.0);
double one_over_sqrt_6 = 1.0 / sqrt(6.0);
double sqrt_two_third = sqrt(2.0 / 3.0);
tilt.matrix[0] = -one_over_sqrt_2; tilt.matrix[1] = one_over_sqrt_2; tilt.matrix[2] = 0;
tilt.matrix[3] = -one_over_sqrt_6; tilt.matrix[4] = -one_over_sqrt_6; tilt.matrix[5] = sqrt_two_third ;
tilt.matrix[6] = one_over_sqrt_3; tilt.matrix[7] = one_over_sqrt_3; tilt.matrix[8] = one_over_sqrt_3;
tilt.matrix[0] = -ONE_OVER_SQRT_2; tilt.matrix[1] = ONE_OVER_SQRT_2; tilt.matrix[2] = 0;
tilt.matrix[3] = -ONE_OVER_SQRT_6; tilt.matrix[4] = -ONE_OVER_SQRT_6; tilt.matrix[5] = SQRT_TWO_THIRD ;
tilt.matrix[6] = ONE_OVER_SQRT_3; tilt.matrix[7] = ONE_OVER_SQRT_3; tilt.matrix[8] = ONE_OVER_SQRT_3;
infill_rotation_matrix = PointMatrix(infill_angle);
Point3Matrix infill_angle_mat(infill_rotation_matrix);
@@ -73,77 +82,53 @@ void SubDivCube::precomputeOctree(SliceMeshStorage& mesh)
mesh.base_subdiv_cube = new SubDivCube(mesh, center, curr_recursion_depth - 1);
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result, Polygons** directional_line_groups)
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result)
{
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(from);
p.add(to);
};
/*!
* Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than epsilon.
* \param group the polygons to add the line to
* \param from the first endpoint of the line
* \param to the second endpoint of the line
*/
auto addLineAndCombine = [&](Polygons& group, Point from, Point to)
{
int epsilon = 10;
for (unsigned int idx = 0; idx < group.size(); idx++)
{
if (abs(from.X - group[idx][1].X) < epsilon && abs(from.Y - group[idx][1].Y) < epsilon)
{
from = group[idx][0];
group.remove(idx);
idx--;
continue;
}
if (abs(to.X - group[idx][0].X) < epsilon && abs(to.Y - group[idx][0].Y) < epsilon)
{
to = group[idx][1];
group.remove(idx);
idx--;
continue;
}
}
PolygonRef p = group.newPoly();
p.add(from);
p.add(to);
};
bool top_level = false; //!< if this cube is the top level of the recursive call
if (directional_line_groups == nullptr) //!< if directional_line_groups is null then set the top level flag and create directional line groups
{
top_level = true;
directional_line_groups = (Polygons**)calloc(3, sizeof(Polygons*));
for (int idx = 0; idx < 3; idx++)
{
directional_line_groups[idx] = new Polygons();
}
}
int32_t z_diff = abs(z - center.z); //!< the difference between the cube center and the target layer.
if (z_diff > height[depth] / 2) //!< this cube does not touch the target layer. Early exit.
if (cube_properties_per_recursion_step.empty()) //Infill is set to 0%.
{
return;
}
if (z_diff < max_draw_z_diff[depth]) //!< this cube has lines that need to be drawn.
Polygons directional_line_groups[3];
generateSubdivisionLines(z, result, directional_line_groups);
for (int dir_idx = 0; dir_idx < 3; dir_idx++)
{
Polygons& line_group = directional_line_groups[dir_idx];
for (unsigned int line_idx = 0; line_idx < line_group.size(); line_idx++)
{
result.addLine(line_group[line_idx][0], line_group[line_idx][1]);
}
}
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3])
{
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
int32_t z_diff = std::abs(z - center.z); //!< the difference between the cube center and the target layer.
if (z_diff > cube_properties.height / 2) //!< this cube does not touch the target layer. Early exit.
{
return;
}
if (z_diff < cube_properties.max_draw_z_diff) //!< this cube has lines that need to be drawn.
{
Point relative_a, relative_b; //!< relative coordinates of line endpoints around cube center
Point a, b; //!< absolute coordinates of line endpoints
relative_a.X = (square_height[depth] / 2) * ((double)(max_draw_z_diff[depth] - z_diff) / (double)max_draw_z_diff[depth]);
relative_a.X = (cube_properties.square_height / 2) * (cube_properties.max_draw_z_diff - z_diff) / cube_properties.max_draw_z_diff;
relative_b.X = -relative_a.X;
relative_a.Y = max_line_offset[depth] - ((z - (center.z - max_draw_z_diff[depth])) * one_over_sqrt_2);
relative_a.Y = cube_properties.max_line_offset - ((z - (center.z - cube_properties.max_draw_z_diff)) * ONE_OVER_SQRT_2);
relative_b.Y = relative_a.Y;
rotatePointInitial(relative_a);
rotatePointInitial(relative_b);
for (int idx = 0; idx < 3; idx++)//!< draw the line, then rotate 120 degrees.
for (int dir_idx = 0; dir_idx < 3; dir_idx++)//!< draw the line, then rotate 120 degrees.
{
a.X = center.x + relative_a.X;
a.Y = center.y + relative_a.Y;
b.X = center.x + relative_b.X;
b.Y = center.y + relative_b.Y;
addLineAndCombine(*(directional_line_groups[idx]), a, b);
if (idx < 2)
addLineAndCombine(directional_line_groups[dir_idx], a, b);
if (dir_idx < 2)
{
rotatePoint120(relative_a);
rotatePoint120(relative_b);
@@ -157,21 +142,9 @@ void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result, Polygons*
children[idx]->generateSubdivisionLines(z, result, directional_line_groups);
}
}
if (top_level) //!< copy directional groups into result, then free the directional groups
{
for (int temp = 0; temp < 3; temp++)
{
for (unsigned int idx = 0; idx < directional_line_groups[temp]->size(); idx++)
{
addLine((*directional_line_groups[temp])[idx][0], (*directional_line_groups[temp])[idx][1]);
}
delete directional_line_groups[temp];
}
free(directional_line_groups);
}
}
SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, int depth)
SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth)
{
this->depth = depth;
this->center = center;
@@ -180,8 +153,14 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, int depth)
{
return;
}
if (depth >= cube_properties_per_recursion_step.size()) //Depth is out of bounds of what we pre-computed.
{
return;
}
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
Point3 child_center;
coord_t radius = double(radius_multiplier * double(height[depth])) / 4.0 + radius_addition;
coord_t radius = double(radius_multiplier * double(cube_properties.height)) / 4.0 + radius_addition;
int child_nr = 0;
std::vector<Point3> rel_child_centers;
@@ -195,7 +174,7 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, int depth)
rel_child_centers.emplace_back(-1, -1, 1);
for (Point3 rel_child_center : rel_child_centers)
{
child_center = center + rotation_matrix.apply(rel_child_center * int32_t(side_length[depth] / 4));
child_center = center + rotation_matrix.apply(rel_child_center * int32_t(cube_properties.side_length / 4));
if (isValidSubdivision(mesh, child_center, radius))
{
children[child_nr] = new SubDivCube(mesh, child_center, depth - 1);
@@ -207,15 +186,15 @@ SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, int depth)
bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius)
{
int64_t distance2;
long int sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer
coord_t sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer
bool inside_somewhere = false;
bool outside_somewhere = false;
int inside;
double part_dist;//what percentage of the radius the target layer is away from the center along the z axis. 0 - 1
const long int layer_height = mesh.getSettingInMicrons("layer_height");
long int bottom_layer = (center.z - radius) / layer_height;
long int top_layer = (center.z + radius) / layer_height;
for (long int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain.
const coord_t layer_height = mesh.getSettingInMicrons("layer_height");
int bottom_layer = (center.z - radius) / layer_height;
int top_layer = (center.z + radius) / layer_height;
for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain.
{
part_dist = (double)(test_layer * layer_height - center.z) / radius;
sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist));
@@ -242,9 +221,9 @@ bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int6
return false;
}
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, long int layer_nr, Point& location, int64_t* distance2)
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2)
{
if (layer_nr < 0 || (unsigned long int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
if (layer_nr < 0 || (unsigned int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
{
return 2;
}
@@ -270,10 +249,34 @@ void SubDivCube::rotatePointInitial(Point& target)
void SubDivCube::rotatePoint120(Point& target)
{
constexpr double sqrt_three_fourths = 0.8660254037844386467637231707529361834714026269051903; //!< sqrt(3.0 / 4.0) = sqrt(3) / 2
int64_t x;
x = (-0.5) * target.X - sqrt_three_fourths * target.Y;
target.Y = (-0.5)*target.Y + sqrt_three_fourths * target.X;
target.X = x;
}
void SubDivCube::addLineAndCombine(Polygons& group, Point from, Point to)
{
int epsilon = 10; // the smallest distance of two points which are viewed as coincident (dist > 0 due to rounding errors)
for (unsigned int idx = 0; idx < group.size(); idx++)
{
if (std::abs(from.X - group[idx][1].X) < epsilon && std::abs(from.Y - group[idx][1].Y) < epsilon)
{
from = group[idx][0];
group.remove(idx);
idx--;
continue;
}
if (std::abs(to.X - group[idx][0].X) < epsilon && std::abs(to.Y - group[idx][0].Y) < epsilon)
{
to = group[idx][1];
group.remove(idx);
idx--;
continue;
}
}
group.addLine(from, to);
}
}//namespace cura
@@ -17,7 +17,7 @@ public:
* \param my_center the center of the cube
* \param depth the recursion depth of the cube (0 is most recursed)
*/
SubDivCube(SliceMeshStorage& mesh, Point3& center, int depth);
SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth);
~SubDivCube(); //!< destructor (also destroys children
@@ -30,10 +30,24 @@ public:
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
* \param directional_line_groups Should be nullptr. Used internally to keep track of line segments that are all pointing the same direction for line segment combining
*/
void generateSubdivisionLines(int64_t z, Polygons& result, Polygons** directional_line_groups = nullptr);
void generateSubdivisionLines(int64_t z, Polygons& result);
private:
/*!
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
* \param directional_line_groups Array of 3 times a polylines. Used to keep track of line segments that are all pointing the same direction for line segment combining
*/
void generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3]);
struct CubeProperties
{
int64_t side_length; //!< side length of cubes
int64_t height; //!< height of cubes based. This is the distance from one point of a cube to its 3d opposite.
int64_t square_height; //!< square cut across lengths. This is the diagonal distance across a face of the cube.
int64_t max_draw_z_diff; //!< maximum draw z differences. This is the maximum difference in z at which lines need to be drawn.
int64_t max_line_offset; //!< maximum line offsets. This is the maximum distance at which subdivision lines should be drawn from the 2d cube center.
};
/*!
* Rotates a point 120 degrees about the origin.
* \param target the point to rotate.
@@ -60,21 +74,24 @@ private:
* \param[out] distance2 the squared distance to the infill border
* \return Code 0: outside, 1: inside, 2: boundary does not exist at specified layer
*/
static int distanceFromPointToMesh(SliceMeshStorage& mesh, long int layer_nr, Point& location, int64_t* distance2);
int depth; //!< the recursion depth of the cube (0 is most recursed)
static int distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2);
/*!
* Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than epsilon.
* \param[out] group the polygons to add the line to
* \param from the first endpoint of the line
* \param to the second endpoint of the line
*/
void addLineAndCombine(Polygons& group, Point from, Point to);
unsigned int depth; //!< the recursion depth of the cube (0 is most recursed)
Point3 center; //!< center location of the cube in absolute coordinates
SubDivCube* children[8] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; //!< pointers to this cube's eight octree children
static std::vector<int64_t> side_length; //!< precomputed array of side lengths of cubes based on recursion depth.
static std::vector<int64_t> height; //!< precomputed array of heights of cubes based on recursion depth. This is the distance from one point of a cube to its 3d opposite.
static std::vector<int64_t> square_height; //!< precomputed array of square cut across lengths based on recursion depth. This is the diagonal distance across a face of the cube.
static std::vector<int64_t> max_draw_z_diff; //!< precomputed array of maximum draw z differences based on recursion depth. This is the maximum difference in z at which lines need to be drawn.
static std::vector<int64_t> max_line_offset; //!< precomputed array of maximum line offsets. This is the maximum distance at which subdivision lines should be drawn from the 2d cube center.
static std::vector<CubeProperties> cube_properties_per_recursion_step; //!< precomputed array of basic properties of cubes based on recursion depth.
static double radius_multiplier; //!< multiplier for the bounding radius when determining if a cube should be subdivided
static Point3Matrix rotation_matrix; //!< The rotation matrix to get from axis aligned cubes to cubes standing on a corner point aligned with the infill_angle
static PointMatrix infill_rotation_matrix; //!< Horizontal rotation applied to infill
static int32_t radius_addition; //!< addition to the bounding radius when determining if a cube should be subdivided
static constexpr double sqrt_three_fourths = 0.8660254037844386467637231707529361834714026269051903; //!< sqrt(3.0 / 4.0)
static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0)
};
}
+1 -3
Ver Arquivo
@@ -109,9 +109,7 @@ protected:
*/
void addLine(Point from, Point to)
{
PolygonRef line_poly = result.newPoly();
line_poly.add(rotation_matrix.unapply(from));
line_poly.add(rotation_matrix.unapply(to));
result.addLine(rotation_matrix.unapply(from), rotation_matrix.unapply(to));
}
/*!
+4 -2
Ver Arquivo
@@ -44,9 +44,11 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
}
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
const auto total_layers = slicer->layers.size();
assert(mesh.layers.size() == total_layers);
#pragma omp parallel for default(none) shared(mesh,slicer) firstprivate(union_layers,union_all_remove_holes) schedule(dynamic)
for(unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
{
mesh.layers.push_back(SliceLayer());
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
+15 -1
Ver Arquivo
@@ -20,6 +20,8 @@
#include "settings/SettingsToGV.h"
#include <omp.h> // omp_get_num_threads
namespace cura
{
@@ -328,7 +330,19 @@ int main(int argc, char **argv)
print_usage();
exit(1);
}
#pragma omp parallel
{
#pragma omp master
{
#ifdef _OPENMP
log("OpenMP multithreading enabled, likely number of threads to be used: %u\n", omp_get_num_threads());
#else
log("OpenMP multithreading disabled\n");
#endif
}
}
if (stringcasecompare(argv[1], "connect") == 0)
{
connect(argc, argv);
+4 -1
Ver Arquivo
@@ -74,7 +74,10 @@ AABB3D Mesh::getAABB() const
}
void Mesh::expandXY(int64_t offset)
{
aabb.expandXY(offset);
if (offset)
{
aabb.expandXY(offset);
}
}
+22 -5
Ver Arquivo
@@ -3,20 +3,26 @@
namespace cura
{
void carveMultipleVolumes(std::vector<Slicer*> &volumes)
void carveMultipleVolumes(std::vector<Slicer*> &volumes, bool alternate_carve_order)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for (unsigned int volume_1_idx = 0; volume_1_idx < volumes.size(); volume_1_idx++)
for (unsigned int volume_1_idx = 1; volume_1_idx < volumes.size(); volume_1_idx++)
{
Slicer& volume_1 = *volumes[volume_1_idx];
if (volume_1.mesh->getSettingBoolean("infill_mesh"))
if (volume_1.mesh->getSettingBoolean("infill_mesh")
|| volume_1.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_1.mesh->getSettingBoolean("support_mesh")
)
{
continue;
}
for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++)
{
Slicer& volume_2 = *volumes[volume_2_idx];
if (volume_2.mesh->getSettingBoolean("infill_mesh"))
if (volume_2.mesh->getSettingBoolean("infill_mesh")
|| volume_2.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_2.mesh->getSettingBoolean("support_mesh")
)
{
continue;
}
@@ -28,7 +34,14 @@ void carveMultipleVolumes(std::vector<Slicer*> &volumes)
{
SlicerLayer& layer1 = volume_1.layers[layerNr];
SlicerLayer& layer2 = volume_2.layers[layerNr];
layer1.polygons = layer1.polygons.difference(layer2.polygons);
if (alternate_carve_order && layerNr % 2 == 0)
{
layer2.polygons = layer2.polygons.difference(layer1.polygons);
}
else
{
layer1.polygons = layer1.polygons.difference(layer2.polygons);
}
}
}
}
@@ -48,6 +61,8 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
{
int overlap = volume->mesh->getSettingInMicrons("multiple_mesh_overlap");
if (volume->mesh->getSettingBoolean("infill_mesh")
|| volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| volume->mesh->getSettingBoolean("support_mesh")
|| overlap == 0)
{
continue;
@@ -60,6 +75,8 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
for (Slicer* other_volume : volumes)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| other_volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| other_volume->mesh->getSettingBoolean("support_mesh")
|| !other_volume->mesh->getAABB().hit(aabb)
|| other_volume == volume
)
+5 -1
Ver Arquivo
@@ -7,7 +7,11 @@
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
namespace cura {
void carveMultipleVolumes(std::vector<Slicer*> &meshes);
/*!
*
* \param alternate_carve_order Whether to switch which model carves out of which with every layer
*/
void carveMultipleVolumes(std::vector<Slicer*> &meshes, bool alternate_carve_order);
/*!
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
+11 -29
Ver Arquivo
@@ -16,7 +16,7 @@ void PathOrderOptimizer::optimize()
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
for (ConstPolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
@@ -92,7 +92,7 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
switch (type)
{
case EZSeamType::BACK: return getFarthestPointInPolygon(poly_idx);
case EZSeamType::BACK: return getClosestPointInPolygon(z_seam_pos, 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);
@@ -102,15 +102,15 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
ConstPolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float best_point_score = std::numeric_limits<float>::infinity();
Point p0 = poly.back();
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p1 = poly[point_idx];
Point& p2 = poly[(point_idx + 1) % poly.size()];
const Point& p1 = poly[point_idx];
const Point& p2 = poly[(point_idx + 1) % poly.size()];
int64_t dist = vSize2(p1 - prev_point);
float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
// this score is in the order of 5 mm
@@ -129,24 +129,6 @@ 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;
}
/**
*
*/
@@ -161,7 +143,7 @@ void LineOrderOptimizer::optimize()
{
int best_point_idx = -1;
float best_point_dist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[poly_idx];
ConstPolygonRef poly = polygons[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
{
float dist = vSize2f(poly[point_idx] - startPoint);
@@ -217,13 +199,13 @@ void LineOrderOptimizer::optimize()
if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
{
PolygonRef best_line = polygons[best_line_idx];
ConstPolygonRef best_line = polygons[best_line_idx];
assert(best_line.size() == 2);
int line_start_point_idx = polyStart[best_line_idx];
int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
Point& line_start = best_line[line_start_point_idx];
Point& line_end = best_line[line_end_point_idx];
const Point& line_start = best_line[line_start_point_idx];
const Point& line_end = best_line[line_end_point_idx];
prev_point = line_end;
incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));
@@ -239,8 +221,8 @@ void LineOrderOptimizer::optimize()
inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal)
{
Point& p0 = polygons[poly_idx][0];
Point& p1 = polygons[poly_idx][1];
const Point& p0 = polygons[poly_idx][0];
const Point& p1 = polygons[poly_idx][1];
float dot_score = getAngleScore(incoming_perpundicular_normal, p0, p1);
{ /// check distance to first point on line (0)
float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
+25 -7
Ver Arquivo
@@ -18,23 +18,30 @@ 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)
Point startPoint; //!< A location near the prefered start location
Point z_seam_pos; //!< The position near where to create the z_seam (if \ref PathOrderOptimizer::type == 'back')
std::vector<ConstPolygonRef> 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, EZSeamType type = EZSeamType::SHORTEST)
PathOrderOptimizer(Point startPoint, Point z_seam_pos = Point(0, 0), EZSeamType type = EZSeamType::SHORTEST)
: type(type)
, startPoint(startPoint)
, z_seam_pos(z_seam_pos)
{
}
void addPolygon(PolygonRef polygon)
{
this->polygons.push_back(polygon);
this->polygons.emplace_back(polygon);
}
void addPolygons(Polygons& polygons)
void addPolygon(ConstPolygonRef polygon)
{
this->polygons.emplace_back(polygon);
}
void addPolygons(const Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(polygons[i]);
@@ -43,9 +50,15 @@ public:
void optimize(); //!< sets #polyStart and #polyOrder
private:
/*!
* Get the starting vertex of a polygon, depending on the \ref PathOrderOptimizer::type
* \param prev_point The previous planned location
* \param poly_idx The index of the polygon in \ref PathOrderOptimizer::polygons
* \return the index of the starting vertex in \ref PathOrderOptimizer::polygons[\p poly_idx]
*/
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);
@@ -58,7 +71,7 @@ class LineOrderOptimizer
{
public:
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<ConstPolygonRef> 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
@@ -72,6 +85,11 @@ public:
this->polygons.push_back(polygon);
}
void addPolygon(ConstPolygonRef polygon)
{
this->polygons.push_back(polygon);
}
void addPolygons(Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
+110 -55
Ver Arquivo
@@ -2,37 +2,28 @@
#include "Comb.h"
#include <algorithm>
#include <functional> // function
#include <unordered_set>
#include "../utils/polygonUtils.h"
#include "../utils/linearAlg2D.h"
#include "../utils/PolygonsPointIndex.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"
namespace cura {
// boundary_outside is only computed when it's needed!
Polygons& Comb::getBoundaryOutside()
LocToLineGrid& Comb::getOutsideLocToLine()
{
if (!boundary_outside)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
}
return *boundary_outside;
}
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& Comb::getOutsideLocToLine()
{
if (!outside_loc_to_line)
{
Polygons& outside = getBoundaryOutside();
outside_loc_to_line = PolygonUtils::createLocToLineGrid(outside, offset_from_inside_to_outside * 3 / 2);
}
return *outside_loc_to_line;
}
Polygons& Comb::getBoundaryOutside()
{
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)
Comb::Comb(const SliceDataStorage& storage, int layer_nr, const 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
@@ -41,23 +32,31 @@ Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_insi
, offset_from_inside_to_outside(offset_from_outlines + offset_from_outlines_outside)
, max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation
, 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)
, outside_loc_to_line(nullptr)
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
, boundary_inside( comb_boundary_inside ) // copy the boundary, because the partsView_inside will reorder the polygons
, partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !!
, inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset))
, boundary_outside(
[&storage, layer_nr, travel_avoid_distance]()
{
return storage.getLayerOutlines(layer_nr, false).offset(travel_avoid_distance);
}
)
, outside_loc_to_line(
[](Comb* comber, const int64_t offset_from_inside_to_outside)
{
return PolygonUtils::createLocToLineGrid(comber->getBoundaryOutside(), offset_from_inside_to_outside * 3 / 2);
}
, this
, offset_from_inside_to_outside
)
{
}
Comb::~Comb()
{
if (boundary_outside)
if (inside_loc_to_line)
{
delete boundary_outside;
}
if (outside_loc_to_line)
{
delete outside_loc_to_line;
delete inside_loc_to_line;
}
}
@@ -84,7 +83,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
return LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
return LinePolygonsCrossings::comb(part, *inside_loc_to_line, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
@@ -99,31 +98,31 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
return false;
}
Crossing start_crossing(startPoint, startInside, start_part_idx, start_part_boundary_poly_idx, boundary_inside);
Crossing end_crossing(endPoint, endInside, end_part_idx, end_part_boundary_poly_idx, boundary_inside);
Crossing start_crossing(startPoint, startInside, start_part_idx, start_part_boundary_poly_idx, boundary_inside, inside_loc_to_line);
Crossing end_crossing(endPoint, endInside, end_part_idx, end_part_boundary_poly_idx, boundary_inside, inside_loc_to_line);
{ // find crossing over the in-between area between inside and outside
start_crossing.findCrossingInOrMid(partsView_inside, endPoint);
end_crossing.findCrossingInOrMid(partsView_inside, start_crossing.in_or_mid);
}
bool avoid_other_parts_now = avoid_other_parts;
if (avoid_other_parts_now && vSize2(start_crossing.in_or_mid - end_crossing.in_or_mid) < offset_from_inside_to_outside * offset_from_inside_to_outside * 4)
bool skip_avoid_other_parts_path = false;
if (skip_avoid_other_parts_path && vSize2(start_crossing.in_or_mid - end_crossing.in_or_mid) < offset_from_inside_to_outside * offset_from_inside_to_outside * 4)
{ // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside
avoid_other_parts_now = false;
skip_avoid_other_parts_path = true;
}
if (avoid_other_parts_now)
if (avoid_other_parts && !skip_avoid_other_parts_path)
{ // compute the crossing points when moving through air
Polygons& outside = getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
// comb through all air, since generally the outside consists of a single part
bool success = start_crossing.findOutside(outside, end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this);
bool success = start_crossing.findOutside(*boundary_outside, end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this);
if (!success)
{
return false;
}
success = end_crossing.findOutside(outside, start_crossing.out, fail_on_unavoidable_obstacles, *this);
success = end_crossing.findOutside(*boundary_outside, start_crossing.out, fail_on_unavoidable_obstacles, *this);
if (!success)
{
return false;
@@ -136,7 +135,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
// start to boundary
assert(start_crossing.dest_part.size() > 0 && "The part we start inside when combing should have been computed already!");
combPaths.emplace_back();
bool combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, startPoint, start_crossing.in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, *inside_loc_to_line, startPoint, start_crossing.in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{ // Couldn't comb between start point and computed crossing from the start part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon
return false;
@@ -144,7 +143,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
// throught air from boundary to boundary
if (avoid_other_parts_now)
if (avoid_other_parts && !skip_avoid_other_parts_path)
{
combPaths.emplace_back();
combPaths.throughAir = true;
@@ -155,7 +154,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
else
{
bool combing_succeeded = LinePolygonsCrossings::comb(getBoundaryOutside(), start_crossing.out, end_crossing.out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(*boundary_outside, *outside_loc_to_line, start_crossing.out, end_crossing.out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{
return false;
@@ -166,10 +165,28 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
{ // directly through air (not avoiding other parts)
combPaths.emplace_back();
combPaths.throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().cross_boundary = true; // note: we don't actually know whether this is cross boundary, but it might very well be
combPaths.back().push_back(start_crossing.in_or_mid);
combPaths.back().push_back(end_crossing.in_or_mid);
}
if (skip_avoid_other_parts_path)
{
if (startInside == endInside && start_part_idx == end_part_idx)
{
if (startInside)
{ // both start and end are inside
combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *inside_loc_to_line);
}
else
{ // both start and end are outside
combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *outside_loc_to_line);
}
}
else
{
combPaths.back().cross_boundary = true;
}
}
if (endInside)
{
@@ -177,7 +194,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
assert(end_crossing.dest_part.size() > 0 && "The part we end up inside when combing should have been computed already!");
combPaths.emplace_back();
bool combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, end_crossing.in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, *inside_loc_to_line, end_crossing.in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{ // Couldn't comb between end point and computed crossing to the end part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon
return false;
@@ -188,21 +205,25 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
}
Comb::Crossing::Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside)
Comb::Crossing::Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside, const LocToLineGrid* inside_loc_to_line)
: dest_is_inside(dest_is_inside)
, dest_crossing_poly(boundary_inside[dest_part_boundary_crossing_poly_idx]) // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
, boundary_inside(boundary_inside)
, inside_loc_to_line(inside_loc_to_line)
, dest_point(dest_point)
, dest_part_idx(dest_part_idx)
{
if (dest_is_inside)
{
dest_crossing_poly.emplace(boundary_inside[dest_part_boundary_crossing_poly_idx]); // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
}
}
bool Comb::moveInside(bool is_inside, Point& dest_point, unsigned int& inside_poly)
{
if (is_inside)
{
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_moveInside_distance2);
if (cpp.point_idx == NO_INDEX)
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_moveInside_distance2, &boundary_inside, inside_loc_to_line);
if (!cpp.isValid())
{
return false;
}
@@ -223,10 +244,43 @@ void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, cons
Point _dest_point(dest_point); // copy to local variable for lambda capture
std::function<int(Point)> close_towards_start_penalty_function([_dest_point](Point candidate){ return vSize2((candidate - _dest_point) / 10); });
dest_part = partsView_inside.assemblePart(dest_part_idx);
Point result(close_to);
ClosestPolygonPoint boundary_crossing_point;
{ // set [result] to a point on the destination part closest to close_to (but also a bit close to _dest_point)
std::unordered_set<unsigned int> dest_part_poly_indices;
for (unsigned int poly_idx : partsView_inside[dest_part_idx])
{
dest_part_poly_indices.emplace(poly_idx);
}
coord_t dist2_score = std::numeric_limits<coord_t>::max();
std::function<bool (const PolygonsPointIndex&)> line_processor
= [close_to, _dest_point, &boundary_crossing_point, &dist2_score, &dest_part_poly_indices](const PolygonsPointIndex& boundary_segment)
{
if (dest_part_poly_indices.find(boundary_segment.poly_idx) == dest_part_poly_indices.end())
{ // we're not looking at a polygon from the dest_part
return true; // a.k.a. continue;
}
Point closest_here = LinearAlg2D::getClosestOnLineSegment(close_to, boundary_segment.p(), boundary_segment.next().p());
coord_t dist2_score_here = vSize2(close_to - closest_here) + vSize2(_dest_point - closest_here) / 10;
if (dist2_score_here < dist2_score)
{
dist2_score = dist2_score_here;
boundary_crossing_point = ClosestPolygonPoint(closest_here, boundary_segment.point_idx, boundary_segment.getPolygon(), boundary_segment.poly_idx);
}
return true;
};
inside_loc_to_line->processLine(std::make_pair(dest_point, close_to), line_processor);
}
Point result(boundary_crossing_point.p()); // the inside point of the crossing
if (!boundary_crossing_point.isValid())
{ // no point has been found in the sparse grid
result = dest_point;
}
int64_t max_dist2 = std::numeric_limits<int64_t>::max();
ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, close_towards_start_penalty_function);
if (crossing_1_in_cp.point_idx != NO_INDEX)
ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, boundary_crossing_point, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, &boundary_inside, inside_loc_to_line, close_towards_start_penalty_function);
if (crossing_1_in_cp.isValid())
{
dest_crossing_poly = crossing_1_in_cp.poly;
in_or_mid = result;
@@ -263,7 +317,8 @@ bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to,
if (dest_is_inside && in_out_dist2_1 > comber.max_crossing_dist2) // moveInside moved too far
{ // if move is too far over in_between
// find crossing closer by
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(outside, dest_crossing_poly, dest_point, close_to, comber);
assert(dest_crossing_poly && "destination crossing poly should have been instantiated!");
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(outside, *dest_crossing_poly, dest_point, close_to, comber);
if (best)
{
in_or_mid = PolygonUtils::moveInside(best->first, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
@@ -278,7 +333,7 @@ bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to,
}
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::Crossing::findBestCrossing(const Polygons& outside, const PolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber)
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> Comb::Crossing::findBestCrossing(const Polygons& outside, ConstPolygonRef from, const Point estimated_start, const Point estimated_end, Comb& comber)
{
ClosestPolygonPoint* best_in = nullptr;
ClosestPolygonPoint* best_out = nullptr;
+28 -18
Ver Arquivo
@@ -4,9 +4,11 @@
#include <memory> // shared_ptr
#include "../utils/optional.h"
#include "../utils/polygon.h"
#include "../utils/SparsePointGridInclusive.h"
#include "../utils/polygonUtils.h"
#include "../utils/LazyInitialization.h"
#include "LinePolygonsCrossings.h"
#include "CombPath.h"
@@ -30,7 +32,7 @@ class SliceDataStorage;
* 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
class Comb
{
friend class LinePolygonsCrossings;
private:
@@ -46,7 +48,9 @@ private:
Point in_or_mid; //!< The point on the inside boundary, or in between the inside and outside boundary if the start/end point isn't inside the inside boudary
Point out; //!< The point on the outside boundary
PolygonsPart dest_part; //!< The assembled inside-boundary PolygonsPart in which the dest_point lies. (will only be initialized when Crossing::dest_is_inside holds)
PolygonRef dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
std::optional<ConstPolygonRef> dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
const Polygons& boundary_inside; //!< The inside boundary as in \ref Comb::boundary_inside
const LocToLineGrid* inside_loc_to_line; //!< The loc to line grid \ref Comb::inside_loc_to_line
/*!
* Simple constructor
@@ -57,7 +61,7 @@ private:
* \param dest_part_boundary_crossing_poly_idx The index in \p boundary_inside of the polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon).
* \param boundary_inside The boundary within which to comb.
*/
Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside);
Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside, const LocToLineGrid* inside_loc_to_line);
/*!
* Find the not-outside location (Combing::in_or_mid) of the crossing between to the outside boundary
@@ -93,11 +97,11 @@ private:
* \param comber[in] The combing calculator which has references to the offsets and boundaries to use in combing.
* \return A pair of which the first is the crossing point on the inside boundary and the second the crossing point on the outside boundary
*/
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findBestCrossing(const Polygons& outside, const PolygonRef from, Point estimated_start, Point estimated_end, Comb& comber);
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findBestCrossing(const Polygons& outside, ConstPolygonRef from, Point estimated_start, Point estimated_end, Comb& comber);
};
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
const SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
const int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
const int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
@@ -111,20 +115,21 @@ private:
const 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)
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* outside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the outside boundary.
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
Polygons boundary_inside; //!< The boundary within which to comb. (Will be reordered by the partsView_inside)
const PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
LocToLineGrid* inside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the inner boundary.
LazyInitialization<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)
LazyInitialization<LocToLineGrid, Comb*, const int64_t> outside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the outside boundary.
/*!
* 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();
/*!
* Get the SparsePointGridInclusive mapping locations to line segments of the outside boundary. Calculate it when it hasn't been calculated yet.
*/
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& getOutsideLocToLine();
LocToLineGrid& getOutsideLocToLine();
/*!
* 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();
/*!
* Move the startPoint or endPoint inside when it should be inside
@@ -138,6 +143,9 @@ private:
public:
/*!
* Initializes the combing areas for every mesh in the layer (not support)
*
* \warning \ref Comb::calc changes the order of polygons in \p Comb::comb_boundary_inside
*
* \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.
@@ -145,13 +153,15 @@ public:
* \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(const SliceDataStorage& storage, int layer_nr, const Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~Comb();
/*!
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
*
* \warning Changes the order of polygons in \ref Comb::comb_boundary_inside
*
* \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
@@ -160,7 +170,7 @@ public:
* \param via_outside_makes_combing_fail When going through air is inavoidable, stop calculation early and return false.
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \return Whether combing has succeeded; otherwise a retraction is needed.
*/
*/
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored, bool via_outside_makes_combing_fail, bool fail_on_unavoidable_obstacles);
};
+24
Ver Arquivo
@@ -0,0 +1,24 @@
//Copyright (C) 2016 Ultimaker
//Released under terms of the AGPLv3 License
#include "GCodePath.h"
namespace cura
{
bool GCodePath::isTravelPath()
{
return config->isTravelPath();
}
double GCodePath::getExtrusionMM3perMM()
{
return flow * config->getExtrusionMM3perMM();
}
int GCodePath::getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
}
+64
Ver Arquivo
@@ -0,0 +1,64 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_G_CODE_PATH_H
#define PATH_PLANNING_G_CODE_PATH_H
#include "../SpaceFillType.h"
#include "../GCodePathConfig.h"
#include "TimeMaterialEstimates.h"
namespace cura
{
/*!
* A class for representing a planned path.
*
* A path consists of several segments of the same type of movement: retracted travel, infill extrusion, etc.
*
* This is a compact premature representation in which are line segments have the same config, i.e. the config of this path.
*
* In the final representation (gcode) each line segment may have different properties,
* which are added when the generated GCodePaths are processed.
*/
class GCodePath
{
public:
const GCodePathConfig* config; //!< The configuration settings of the path.
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
bool perform_z_hop; //!< Whether to perform a z_hop in this path, which is assumed to be a travel path.
bool perform_prime; //!< Whether this path is preceded by a prime (poop)
std::vector<Point> points; //!< The points constituting this path.
bool done;//!< Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
bool spiralize; //!< Whether to gradually increment the z position during the printing of this path. A sequence of spiralized paths should start at the given layer height and end in one layer higher.
TimeMaterialEstimates estimates; //!< Naive time and material estimates
/*!
* Whether this config is the config of a travel path.
*
* \return Whether this config is the config of a travel path.
*/
bool isTravelPath();
/*!
* Get the material flow in mm^3 per mm traversed.
*
* \warning Can only be called after the layer height has been set (which is done while writing the gcode!)
*
* \return The flow
*/
double getExtrusionMM3perMM();
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth();
};
}//namespace cura
#endif//PATH_PLANNING_G_CODE_PATH_H
+5 -5
Ver Arquivo
@@ -145,7 +145,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - std::abs(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
@@ -166,7 +166,7 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
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)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x + std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y)));
}
@@ -194,9 +194,9 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx], loc_to_line_grid))
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx - 1], loc_to_line_grid))
{
comb_path.cross_boundary = true;
}
@@ -209,7 +209,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
// 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)
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx]))
if (PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx], loc_to_line_grid))
{
break;
}
+12 -4
Ver Arquivo
@@ -3,6 +3,8 @@
#define PATH_PLANNING_LINE_POLYGONS_CROSSINGS_H
#include "../utils/polygon.h"
#include "../utils/polygonUtils.h"
#include "../utils/SparseLineGrid.h"
#include "CombPath.h"
@@ -80,6 +82,7 @@ private:
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.
LocToLineGrid& loc_to_line_grid; //!< Mapping from locations to line segments of \ref LinePolygonsCrossings::boundary
Point startPoint; //!< The start point of the scanline.
Point endPoint; //!< The end point of the scanline.
@@ -163,8 +166,12 @@ private:
* \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)
LinePolygonsCrossings(Polygons& boundary, LocToLineGrid& loc_to_line_grid, Point& start, Point& end, int64_t dist_to_move_boundary_point_outside)
: boundary(boundary)
, loc_to_line_grid(loc_to_line_grid)
, startPoint(start)
, endPoint(end)
, dist_to_move_boundary_point_outside(dist_to_move_boundary_point_outside)
{
}
@@ -173,15 +180,16 @@ 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 loc_to_line_grid A sparse grid mapping cells to all line segments of (at least) \p boundary in those cells
* \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.
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \return Whether combing succeeded, i.e. we didn't cross any gaps/other parts
*/
static bool comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
static bool comb(Polygons& boundary, LocToLineGrid& loc_to_line_grid, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
LinePolygonsCrossings linePolygonsCrossings(boundary, loc_to_line_grid, startPoint, endPoint, dist_to_move_boundary_point_outside);
return linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
};
};
+24
Ver Arquivo
@@ -0,0 +1,24 @@
//Copyright (C) 2016 Ultimaker
//Released under terms of the AGPLv3 License
#include "NozzleTempInsert.h"
namespace cura
{
NozzleTempInsert::NozzleTempInsert(unsigned int path_idx, int extruder, double temperature, bool wait, double time_after_path_start)
: path_idx(path_idx)
, time_after_path_start(time_after_path_start)
, extruder(extruder)
, temperature(temperature)
, wait(wait)
{
assert(temperature != 0 && temperature != -1 && "Temperature command must be set!");
}
void NozzleTempInsert::write(GCodeExport& gcode)
{
gcode.writeTemperatureCommand(extruder, temperature, wait);
}
}
+32
Ver Arquivo
@@ -0,0 +1,32 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_NOZZLE_TEMP_INSERT_H
#define PATH_PLANNING_NOZZLE_TEMP_INSERT_H
#include "../gcodeExport.h"
namespace cura
{
/*!
* 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);
/*!
* Write the temperature command at the current position in the gcode.
* \param gcode The actual gcode writer
*/
void write(GCodeExport& gcode);
};
}//namespace cura
#endif//PATH_PLANNING_NOZZLE_TEMP_INSERT_H
+84
Ver Arquivo
@@ -0,0 +1,84 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "TimeMaterialEstimates.h"
namespace cura
{
TimeMaterialEstimates::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::TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
, retracted_travel_time(0.0)
, material(0.0)
{
}
TimeMaterialEstimates TimeMaterialEstimates::operator-(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time - other.extrude_time,unretracted_travel_time - other.unretracted_travel_time,retracted_travel_time - other.retracted_travel_time,material - other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstimates& other)
{
extrude_time -= other.extrude_time;
unretracted_travel_time -= other.unretracted_travel_time;
retracted_travel_time -= other.retracted_travel_time;
material -= other.material;
return *this;
}
TimeMaterialEstimates TimeMaterialEstimates::operator+(const TimeMaterialEstimates& other)
{
return TimeMaterialEstimates(extrude_time+other.extrude_time, unretracted_travel_time+other.unretracted_travel_time, retracted_travel_time+other.retracted_travel_time, material+other.material);
}
TimeMaterialEstimates& TimeMaterialEstimates::operator+=(const TimeMaterialEstimates& other)
{
extrude_time += other.extrude_time;
unretracted_travel_time += other.unretracted_travel_time;
retracted_travel_time += other.retracted_travel_time;
material += other.material;
return *this;
}
double TimeMaterialEstimates::getExtrudeTime() const
{
return extrude_time;
}
double TimeMaterialEstimates::getMaterial() const
{
return material;
}
double TimeMaterialEstimates::getTotalTime() const
{
return extrude_time + unretracted_travel_time + retracted_travel_time;
}
double TimeMaterialEstimates::getTotalUnretractedTime() const
{
return extrude_time + unretracted_travel_time;
}
double TimeMaterialEstimates::getTravelTime() const
{
return retracted_travel_time + unretracted_travel_time;
}
void TimeMaterialEstimates::reset()
{
extrude_time = 0.0;
unretracted_travel_time = 0.0;
retracted_travel_time = 0.0;
material = 0.0;
}
}//namespace cura
+124
Ver Arquivo
@@ -0,0 +1,124 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
#define PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
#include "../gcodeExport.h"
namespace cura
{
class ExtruderPlan; // 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 ExtruderPlan; // cause there the naive estimates are calculated
private:
double extrude_time; //!< Time in seconds occupied by extrusion
double unretracted_travel_time; //!< Time in seconds occupied by non-retracted travel (non-extrusion)
double retracted_travel_time; //!< Time in seconds occupied by retracted travel (non-extrusion)
double material; //!< Material used (in mm^3)
public:
/*!
* Basic contructor
*
* \param extrude_time Time in seconds occupied by extrusion
* \param unretracted_travel_time Time in seconds occupied by non-retracted travel (non-extrusion)
* \param retracted_travel_time Time in seconds occupied by retracted travel (non-extrusion)
* \param material Material used (in mm^3)
*/
TimeMaterialEstimates(double extrude_time, double unretracted_travel_time, double retracted_travel_time, double material);
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates();
/*!
* Set all estimates to zero.
*/
void reset();
/*!
* Pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return The resulting estimates
*/
TimeMaterialEstimates operator+(const TimeMaterialEstimates& other);
/*!
* In place pointwise addition of estimate stats
*
* \param other The estimates to add to these estimates.
* \return These estimates
*/
TimeMaterialEstimates& operator+=(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
*
* Each of the estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
* This causes the estimates in this instance to change. Each of the
* estimates in this class are individually subtracted.
*
* \param other The estimates to subtract from these estimates.
* \return A reference to this instance.
*/
TimeMaterialEstimates& operator-=(const TimeMaterialEstimates& other);
/*!
* Get total time estimate. The different time estimate member values added together.
*
* \return the total of all different time estimate values
*/
double getTotalTime() const;
/*!
* Get the total time during which the head is not retracted.
*
* This includes extrusion time and non-retracted travel time
*
* \return the total time during which the head is not retracted.
*/
double getTotalUnretractedTime() const;
/*!
* Get the total travel time.
*
* This includes the retracted travel time as well as the unretracted travel time.
*
* \return the total travel time.
*/
double getTravelTime() const;
/*!
* Get the extrusion time.
*
* \return extrusion time.
*/
double getExtrudeTime() const;
/*!
* Get the amount of material used in mm^3.
*
* \return amount of material
*/
double getMaterial() const;
};
}//namespace cura
#endif//PATH_PLANNING_TIME_MATERIAL_ESTIMATES_H
+5 -1
Ver Arquivo
@@ -43,7 +43,7 @@ int Raft::getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage)
{
return 0;
}
const int64_t airgap = std::max(0, train.getSettingInMicrons("raft_airgap"));
const int64_t airgap = std::max((coord_t)0, train.getSettingInMicrons("raft_airgap"));
const int64_t layer_0_overlap = storage.getSettingInMicrons("layer_0_z_overlap");
const int64_t layer_height_0 = storage.getSettingInMicrons("layer_height_0");
@@ -75,6 +75,10 @@ int Raft::getFillerLayerHeight(const SliceDataStorage& storage)
int Raft::getTotalExtraLayers(const SliceDataStorage& storage)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
if (train.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
{
return 0;
}
return 2 + train.getSettingAsCount("raft_surface_layers") + getFillerLayerCount(storage);
}
+235
Ver Arquivo
@@ -0,0 +1,235 @@
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#include "PathConfigStorage.h"
#include "settings.h" // MAX_INFILL_COMBINE
#include "../sliceDataStorage.h" // SliceDataStorage
namespace cura
{
GCodePathConfig getPerimeterGapConfig(const SliceMeshStorage& mesh, int layer_thickness)
{
// The perimeter gap config follows the skin config, but has a different line width:
// wall_line_width_x divided by two because the gaps are between 0 and 1 times the wall line width
const int perimeter_gaps_line_width = mesh.getSettingInMicrons("wall_line_width_x") / 2;
double perimeter_gaps_speed = mesh.getSettingInMillimetersPerSecond("speed_topbottom");
if (mesh.getSettingBoolean("speed_equalize_flow_enabled"))
{
perimeter_gaps_speed = perimeter_gaps_speed * mesh.getSettingInMicrons("skin_line_width") / perimeter_gaps_line_width;
}
return GCodePathConfig(
PrintFeatureType::Skin
, perimeter_gaps_line_width
, layer_thickness
, mesh.getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{perimeter_gaps_speed, mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom")}
);
}
PathConfigStorage::MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, int layer_thickness)
: inset0_config(
PrintFeatureType::OuterWall
, mesh.getSettingInMicrons("wall_line_width_0")
, layer_thickness
, mesh.getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_0"), mesh.getSettingInMillimetersPerSecond("jerk_wall_0")}
)
, insetX_config(
PrintFeatureType::InnerWall
, mesh.getSettingInMicrons("wall_line_width_x")
, layer_thickness
, mesh.getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMillimetersPerSecond("acceleration_wall_x"), mesh.getSettingInMillimetersPerSecond("jerk_wall_x")}
)
, skin_config(
PrintFeatureType::Skin
, mesh.getSettingInMicrons("skin_line_width")
, layer_thickness
, mesh.getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMillimetersPerSecond("acceleration_topbottom"), mesh.getSettingInMillimetersPerSecond("jerk_topbottom")}
)
, perimeter_gap_config(getPerimeterGapConfig(mesh, layer_thickness))
{
infill_config.reserve(MAX_INFILL_COMBINE);
for (int combine_idx = 0; combine_idx < MAX_INFILL_COMBINE; combine_idx++)
{
infill_config.emplace_back(
PrintFeatureType::Infill
, mesh.getSettingInMicrons("infill_line_width") * (combine_idx + 1)
, layer_thickness
, mesh.getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMillimetersPerSecond("acceleration_infill"), mesh.getSettingInMillimetersPerSecond("jerk_infill")}
);
}
}
PathConfigStorage::PathConfigStorage(const SliceDataStorage& storage, int layer_nr, int layer_thickness)
: adhesion_extruder_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr")))
, support_infill_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr")))
, support_interface_train(storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr")))
, raft_base_config(
PrintFeatureType::SupportInterface
, adhesion_extruder_train->getSettingInMicrons("raft_base_line_width")
, adhesion_extruder_train->getSettingInMicrons("raft_base_thickness")
, adhesion_extruder_train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_base_jerk")}
)
, raft_interface_config(
PrintFeatureType::Support
, adhesion_extruder_train->getSettingInMicrons("raft_interface_line_width")
, adhesion_extruder_train->getSettingInMicrons("raft_interface_thickness")
, adhesion_extruder_train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_interface_jerk")}
)
, raft_surface_config(
PrintFeatureType::SupportInterface
, adhesion_extruder_train->getSettingInMicrons("raft_surface_line_width")
, adhesion_extruder_train->getSettingInMicrons("raft_surface_thickness")
, adhesion_extruder_train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_speed"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_acceleration"), adhesion_extruder_train->getSettingInMillimetersPerSecond("raft_surface_jerk")}
)
, support_infill_config(
PrintFeatureType::Support
, support_infill_train->getSettingInMicrons("support_line_width")
, layer_thickness
, support_infill_train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{support_infill_train->getSettingInMillimetersPerSecond("speed_support_infill"), support_infill_train->getSettingInMillimetersPerSecond("acceleration_support_infill"), support_infill_train->getSettingInMillimetersPerSecond("jerk_support_infill")}
)
, support_interface_config(
PrintFeatureType::SupportInterface
, support_interface_train->getSettingInMicrons("support_interface_line_width")
, layer_thickness
, support_interface_train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{support_interface_train->getSettingInMillimetersPerSecond("speed_support_interface"), support_interface_train->getSettingInMillimetersPerSecond("acceleration_support_interface"), support_interface_train->getSettingInMillimetersPerSecond("jerk_support_interface")}
)
{
int extruder_count = storage.meshgroup->getExtruderCount();
travel_config_per_extruder.reserve(extruder_count);
skirt_brim_config.reserve(extruder_count);
prime_tower_config_per_extruder.reserve(extruder_count);
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
travel_config_per_extruder.emplace_back(
PrintFeatureType::MoveCombing
, 0
, 0
, 0.0
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("speed_travel"), train->getSettingInMillimetersPerSecond("acceleration_travel"), train->getSettingInMillimetersPerSecond("jerk_travel")}
);
skirt_brim_config.emplace_back(
PrintFeatureType::SkirtBrim
, train->getSettingInMicrons("skirt_brim_line_width")
, layer_thickness
, train->getSettingInPercentage("material_flow")
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("skirt_brim_speed"), train->getSettingInMillimetersPerSecond("acceleration_skirt_brim"), train->getSettingInMillimetersPerSecond("jerk_skirt_brim")}
);
prime_tower_config_per_extruder.emplace_back(
PrintFeatureType::SupportInfill
, train->getSettingInMicrons("prime_tower_line_width")
, layer_thickness
, train->getSettingInPercentage("prime_tower_flow")
, GCodePathConfig::SpeedDerivatives{train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMillimetersPerSecond("acceleration_prime_tower"), train->getSettingInMillimetersPerSecond("jerk_prime_tower")}
);
}
mesh_configs.reserve(storage.meshes.size());
for (const SliceMeshStorage& mesh_storage : storage.meshes)
{
mesh_configs.emplace_back(mesh_storage, layer_thickness);
}
int initial_speedup_layer_count = storage.getSettingAsCount("speed_slowdown_layers");
if (layer_nr < initial_speedup_layer_count)
{
handleInitialLayerSpeedup(storage, layer_nr, initial_speedup_layer_count);
}
}
void cura::PathConfigStorage::handleInitialLayerSpeedup(const SliceDataStorage& storage, int layer_nr, int initial_speedup_layer_count)
{
std::vector<GCodePathConfig::SpeedDerivatives> global_first_layer_config_per_extruder;
global_first_layer_config_per_extruder.reserve(storage.meshgroup->getExtruderCount());
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
const ExtruderTrain* extruder = storage.meshgroup->getExtruderTrain(extruder_nr);
global_first_layer_config_per_extruder.emplace_back(
GCodePathConfig::SpeedDerivatives{
extruder->getSettingInMillimetersPerSecond("speed_print_layer_0")
, extruder->getSettingInMillimetersPerSecond("acceleration_print_layer_0")
, extruder->getSettingInMillimetersPerSecond("jerk_print_layer_0")
});
}
{ // support
if (layer_nr < initial_speedup_layer_count)
{
int extruder_nr_support_infill = storage.getSettingAsIndex((layer_nr <= 0)? "support_extruder_nr_layer_0" : "support_infill_extruder_nr");
GCodePathConfig::SpeedDerivatives& first_layer_config_infill = global_first_layer_config_per_extruder[extruder_nr_support_infill];
support_infill_config.smoothSpeed(first_layer_config_infill, std::max(0, layer_nr), initial_speedup_layer_count);
int extruder_nr_support_interface = storage.getSettingAsIndex("support_interface_extruder_nr");
GCodePathConfig::SpeedDerivatives& first_layer_config_interface = global_first_layer_config_per_extruder[extruder_nr_support_interface];
support_interface_config.smoothSpeed(first_layer_config_interface, std::max(0, layer_nr), initial_speedup_layer_count);
}
}
{ // extruder configs: travel, skirt/brim (= shield)
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
{
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
GCodePathConfig::SpeedDerivatives initial_layer_speed_config{
train->getSettingInMillimetersPerSecond("speed_travel_layer_0")
, train->getSettingInMillimetersPerSecond("acceleration_travel_layer_0")
, train->getSettingInMillimetersPerSecond("jerk_travel_layer_0")
};
GCodePathConfig& travel = travel_config_per_extruder[extruder_nr];
travel.smoothSpeed(initial_layer_speed_config, std::max(0, layer_nr), initial_speedup_layer_count);
// don't smooth speed for the skirt/brim!
// NOTE: not smoothing skirt/brim means the speeds are also not smoothed for the draft/ooze shield
GCodePathConfig& prime_tower = prime_tower_config_per_extruder[extruder_nr];
prime_tower.smoothSpeed(initial_layer_speed_config, std::max(0, layer_nr), initial_speedup_layer_count);
}
}
{ // meshes
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
const SliceMeshStorage& mesh = storage.meshes[mesh_idx];
GCodePathConfig::SpeedDerivatives initial_layer_speed_config{
mesh.getSettingInMillimetersPerSecond("speed_print_layer_0")
, mesh.getSettingInMillimetersPerSecond("acceleration_print_layer_0")
, mesh.getSettingInMillimetersPerSecond("jerk_print_layer_0")
};
MeshPathConfigs& mesh_config = mesh_configs[mesh_idx];
//Outer wall speed (per mesh).
mesh_config.inset0_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
//Inner wall speed (per mesh).
mesh_config.insetX_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
//Skin speed (per mesh).
mesh_config.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
mesh_config.perimeter_gap_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
{
//Infill speed (per combine part per mesh).
mesh_config.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layer_count);
}
}
}
}
}//namespace cura
+63
Ver Arquivo
@@ -0,0 +1,63 @@
/** Copyright (C) 2017 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_PATH_CONFIGS_H
#define SETTINGS_PATH_CONFIGS_H
#include <vector>
#include "../utils/intpoint.h" // coord_t
#include "../GCodePathConfig.h"
namespace cura
{
class SliceDataStorage; // forward decl for SliceDataStorage
class SliceMeshStorage; // forward decl for SliceDataStorage
class ExtruderTrain; // forward decl for SliceDataStorage
/*!
* A class to represent all configurations for all features types of printed lines in a meshgroup.
*/
class PathConfigStorage
{
private:
const ExtruderTrain* adhesion_extruder_train;
const ExtruderTrain* support_infill_train;
const ExtruderTrain* support_interface_train;
public:
class MeshPathConfigs
{
public:
GCodePathConfig inset0_config;
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
GCodePathConfig perimeter_gap_config;
std::vector<GCodePathConfig> infill_config;
MeshPathConfigs(const SliceMeshStorage& mesh, int layer_thickness);
};
GCodePathConfig raft_base_config;
GCodePathConfig raft_interface_config;
GCodePathConfig raft_surface_config;
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
std::vector<GCodePathConfig> skirt_brim_config; //!< Configuration for skirt and brim per extruder.
std::vector<GCodePathConfig> prime_tower_config_per_extruder; //!< Configuration for the prime tower per extruder.
GCodePathConfig support_infill_config; //!< The config used to print the normal support, rather than the support interface
GCodePathConfig support_interface_config; //!< The config to use to print the dense roofs and bottoms of support
std::vector<MeshPathConfigs> mesh_configs; //!< For each meash the config for all its feature types
/*!
* \warning Note that the layer_nr might be below zero for raft (filler) layers
*/
PathConfigStorage(const SliceDataStorage& storage, int layer_nr, int layer_thickness);
private:
void handleInitialLayerSpeedup(const SliceDataStorage& storage, int layer_nr, int initial_speedup_layer_count);
};
}; // namespace cura
#endif // SETTINGS_PATH_CONFIGS_H
+10 -8
Ver Arquivo
@@ -125,6 +125,11 @@ bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::strin
int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
{
if (extruder_train_ids.empty())
{
logError("Couldn't find any extruder trains!\n");
return -1;
}
if (extruder_nr >= extruder_train_ids.size())
{
logWarning("Couldn't load extruder.def.json file for extruder %i. Index out of bounds.\n Loading first extruder definition instead.\n", extruder_nr);
@@ -220,8 +225,7 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
if (json_document.HasMember("settings"))
{
std::list<std::string> path;
handleChildren(json_document["settings"], path, settings_base, warn_duplicates);
handleChildren(json_document["settings"], settings_base, warn_duplicates);
}
if (json_document.HasMember("overrides"))
@@ -243,7 +247,7 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
return 0;
}
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates)
{
if (!settings_list.IsObject())
{
@@ -252,12 +256,10 @@ void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std:
}
for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
{
handleSetting(setting_iterator, path, settings_base, warn_duplicates);
handleSetting(setting_iterator, settings_base, warn_duplicates);
if (setting_iterator->value.HasMember("children"))
{
std::list<std::string> path_here = path;
path_here.push_back(setting_iterator->name.GetString());
handleChildren(setting_iterator->value["children"], path_here, settings_base, warn_duplicates);
handleChildren(setting_iterator->value["children"], settings_base, warn_duplicates);
}
}
}
@@ -275,7 +277,7 @@ bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
}
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates)
{
const rapidjson::Value& json_setting = json_setting_it->value;
if (!json_setting.IsObject())
+2 -3
Ver Arquivo
@@ -174,17 +174,16 @@ private:
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
void handleChildren(const rapidjson::Value& settings_list, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Handle a json object for a setting.
*
* \param json_setting_it Iterator for the setting which contains the key (setting name) and attributes info
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, SettingsBase* settings_base, bool warn_duplicates);
};
}//namespace cura
+31 -11
Ver Arquivo
@@ -42,7 +42,7 @@ std::string toString(EGCodeFlavor flavor)
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
: parent(nullptr)
{
}
@@ -52,7 +52,7 @@ SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
: SettingsBaseVirtual(nullptr)
{
}
@@ -93,21 +93,23 @@ void SettingsBase::setSettingInheritBase(std::string key, const SettingsBaseVirt
std::string SettingsBase::getSettingString(std::string key) const
{
if (setting_values.find(key) != setting_values.end())
auto value_it = setting_values.find(key);
if (value_it != setting_values.end())
{
return setting_values.at(key);
return value_it->second;
}
if (setting_inherit_base.find(key) != setting_inherit_base.end())
auto inherit_override_it = setting_inherit_base.find(key);
if (inherit_override_it != setting_inherit_base.end())
{
return setting_inherit_base.at(key)->getSettingString(key);
return inherit_override_it->second->getSettingString(key);
}
if (parent)
{
return parent->getSettingString(key);
}
const_cast<SettingsBase&>(*this).setting_values[key] = "";
cura::logWarning("Unregistered setting %s\n", key.c_str());
cura::logError("Trying to retrieve unregistered setting with no value given: '%s'\n", key.c_str());
std::exit(-1);
return "";
}
@@ -156,7 +158,7 @@ double SettingsBaseVirtual::getSettingInMillimeters(std::string key) const
return atof(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
coord_t SettingsBaseVirtual::getSettingInMicrons(std::string key) const
{
return getSettingInMillimeters(key) * 1000.0;
}
@@ -359,6 +361,8 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "concentric_3d")
return EFillMethod::CONCENTRIC_3D;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
@@ -371,6 +375,8 @@ EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
if (value == "none")
return EPlatformAdhesion::NONE;
return EPlatformAdhesion::SKIRT;
}
@@ -408,7 +414,21 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
return ESurfaceMode::NORMAL;
}
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key) const
{
std::string value = getSettingString(key);
if (value == "nowhere")
{
return FillPerimeterGapMode::NOWHERE;
}
if (value == "everywhere")
{
return FillPerimeterGapMode::EVERYWHERE;
}
return FillPerimeterGapMode::NOWHERE;
}
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key) const
{
std::string value = getSettingString(key);
if (value == "off")
@@ -426,7 +446,7 @@ CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
return CombingMode::ALL;
}
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key)
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key) const
{
std::string value = getSettingString(key);
if (value == "xy_overrides_z")
+13 -4
Ver Arquivo
@@ -109,6 +109,7 @@ enum class EFillMethod
TETRAHEDRAL,
TRIANGLES,
CONCENTRIC,
CONCENTRIC_3D,
ZIG_ZAG,
NONE
};
@@ -120,7 +121,8 @@ enum class EPlatformAdhesion
{
SKIRT,
BRIM,
RAFT
RAFT,
NONE
};
/*!
@@ -147,6 +149,12 @@ enum class ESurfaceMode
BOTH
};
enum class FillPerimeterGapMode
{
NOWHERE,
EVERYWHERE
};
enum class CombingMode
{
OFF,
@@ -223,7 +231,7 @@ public:
double getSettingInAngleDegrees(std::string key) const;
double getSettingInAngleRadians(std::string key) const;
double getSettingInMillimeters(std::string key) const;
int getSettingInMicrons(std::string key) const;
coord_t getSettingInMicrons(std::string key) const;
bool getSettingBoolean(std::string key) const;
double getSettingInDegreeCelsius(std::string key) const;
double getSettingInMillimetersPerSecond(std::string key) const;
@@ -242,8 +250,9 @@ public:
ESupportType getSettingAsSupportType(std::string key) const;
EZSeamType getSettingAsZSeamType(std::string key) const;
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
CombingMode getSettingAsCombingMode(std::string key);
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key) const;
CombingMode getSettingAsCombingMode(std::string key) const;
SupportDistPriority getSettingAsSupportDistPriority(std::string key) const;
};
class SettingRegistry;
+71 -8
Ver Arquivo
@@ -10,7 +10,16 @@
namespace cura
{
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateSkinAreas reads data from mesh.layers.parts[*].insets and writes to mesh.layers[n].parts[*].skin_parts
* generateSkinInsets only read/writes the skin_parts from the current layer.
*
* generateSkins therefore reads (depends on) data from mesh.layers[*].parts[*].insets and writes mesh.layers[n].parts[*].skin_parts
*/
void generateSkins(int layerNr, SliceMeshStorage& mesh, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_line_width, int insetCount, bool no_small_gaps_heuristic)
{
generateSkinAreas(layerNr, mesh, innermost_wall_line_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
@@ -23,6 +32,12 @@ void generateSkins(int layerNr, SliceMeshStorage& mesh, int downSkinCount, int u
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateSkinAreas reads data from mesh.layers[*].parts[*].insets and writes to mesh.layers[n].parts[*].skin_parts
*/
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
SliceLayer& layer = mesh.layers[layer_nr];
@@ -31,7 +46,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
{
return;
}
int min_infill_area = mesh.getSettingInMillimeters("min_infill_area");
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
{
SliceLayerPart& part = layer.parts[partNr];
@@ -63,12 +78,22 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
{
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
{
downskin = downskin.difference(getInsidePolygons(mesh.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr - downSkinCount]);
if (min_infill_area > 0)
{
not_air.removeSmallAreas(min_infill_area);
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(mesh.layers.size()))
{
upskin = upskin.difference(getInsidePolygons(mesh.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + upSkinCount]);
if (min_infill_area > 0)
{
not_air.removeSmallAreas(min_infill_area);
}
upskin = upskin.difference(not_air); // skin overlaps with the walls
}
}
else
@@ -80,6 +105,10 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
{
not_air = not_air.intersection(getInsidePolygons(mesh.layers[downskin_layer_nr]));
}
if (min_infill_area > 0)
{
not_air.removeSmallAreas(min_infill_area);
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
@@ -90,6 +119,10 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
{
not_air = not_air.intersection(getInsidePolygons(mesh.layers[upskin_layer_nr]));
}
if (min_infill_area > 0)
{
not_air.removeSmallAreas(min_infill_area);
}
upskin = upskin.difference(not_air); // skin overlaps with the walls
}
}
@@ -106,7 +139,12 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateSkinInsets only read/writes the skin_parts from the current layer.
*/
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount)
{
if (insetCount == 0)
@@ -139,17 +177,33 @@ void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int ins
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area
*/
void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = mesh.layers[layerNr];
int extra_offset = 0;
EFillMethod fill_pattern = mesh.getSettingAsFillMethod("infill_pattern");
if ((fill_pattern == EFillMethod::CONCENTRIC || fill_pattern == EFillMethod::CONCENTRIC_3D)
&& mesh.getSettingBoolean("alternate_extra_perimeter")
&& layerNr % 2 == 0
&& mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") * 2)
{
extra_offset = -innermost_wall_line_width;
}
for(SliceLayerPart& part : layer.parts)
{
if (int(part.insets.size()) < wall_line_count)
{
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_line_width / 2 - infill_skin_overlap);
Polygons infill = part.insets.back().offset(extra_offset - innermost_wall_line_width / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
@@ -162,8 +216,17 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wal
}
}
infill.removeSmallAreas(MIN_AREA_SIZE);
part.infill_area = infill.offset(infill_skin_overlap);
Polygons final_infill = infill.offset(infill_skin_overlap);
if (mesh.getSettingBoolean("infill_hollow"))
{
part.print_outline = part.print_outline.difference(final_infill);
}
else
{
part.infill_area = final_infill;
}
}
}
+128 -47
Ver Arquivo
@@ -1,7 +1,10 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "sliceDataStorage.h"
#include "FffProcessor.h" //To create a mesh group with if none is provided.
#include "infill/subDivCube.h" // For the destructor
#include "infill/SubDivCube.h" // For the destructor
namespace cura
@@ -32,7 +35,7 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
{
if (external_polys_only)
{
result.add(const_cast<SliceLayerPart&>(part).outline.outerPolygon()); // TODO: make a const version of outerPolygon()
result.add(part.outline.outerPolygon());
}
else
{
@@ -54,12 +57,12 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
{
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[1]); // TODO const cast!
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(const_cast<SliceLayerPart&>(part).insets[0]); // TODO const cast!
layer_walls.add(part.insets[0]);
continue;
}
// offset_from_outlines was so large that it completely destroyed our isle,
@@ -84,38 +87,13 @@ std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeTravelConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
travel_config_per_extruder.emplace_back(PrintFeatureType::MoveCombing);
}
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeSkirtBrimConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
skirt_brim_config.emplace_back(PrintFeatureType::SkirtBrim);
}
return ret;
}
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
print_layer_count(0),
retraction_config_per_extruder(initializeRetractionConfigs()),
extruder_switch_retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config_per_extruder(initializeTravelConfigs()),
skirt_brim_config(initializeSkirtBrimConfigs()),
raft_base_config(PrintFeatureType::SupportInterface),
raft_interface_config(PrintFeatureType::Support),
raft_surface_config(PrintFeatureType::SupportInterface),
support_config(PrintFeatureType::Support),
support_skin_config(PrintFeatureType::SupportInterface),
max_object_height_second_to_last_extruder(-1)
max_print_height_second_to_last_extruder(-1),
primeTower(*this)
{
}
@@ -152,13 +130,13 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
{
for (const SliceMeshStorage& mesh : meshes)
{
if (mesh.getSettingBoolean("infill_mesh"))
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
{
continue;
}
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make all getSetting functions const??
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
@@ -171,7 +149,10 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
if (primeTower.enabled)
{
total.add(primeTower.ground_poly);
}
}
return total;
}
@@ -199,7 +180,7 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
{
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(total);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make getSetting const? make settings.setting_values mapping mutable??
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
@@ -212,27 +193,33 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
if (primeTower.enabled)
{
total.add(primeTower.ground_poly);
}
}
return total;
}
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
std::vector<bool> SliceDataStorage::getExtrudersUsed() const
{
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_brim[extr_nr].size() > 0)
if (getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
ret[extr_nr] = true;
continue;
if (skirt_brim[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
}
@@ -246,9 +233,103 @@ std::vector< bool > SliceDataStorage::getExtrudersUsed()
ret[getSettingAsIndex("support_interface_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
for (const SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
}
return ret;
}
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr) const
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
bool include_adhesion = true;
bool include_helper_parts = true;
bool include_models = true;
if (layer_nr < 0)
{
include_models = false;
if (layer_nr < -Raft::getFillerLayerCount(*this))
{
include_helper_parts = false;
}
else
{
layer_nr = 0; // because the helper parts are copied from the initial layer in the filler layer
include_adhesion = false;
}
}
else if (layer_nr > 0 || getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{ // only include adhesion only for layers where platform adhesion actually occurs
// i.e. layers < 0 are for raft, layer 0 is for brim/skirt
include_adhesion = false;
}
if (include_adhesion)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt_brim[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
}
// TODO: ooze shield, draft shield ..?
if (include_helper_parts)
{
// support
if (layer_nr < int(support.supportLayers.size()))
{
const SupportLayer& support_layer = support.supportLayers[layer_nr];
if (layer_nr == 0)
{
if (support_layer.supportAreas.size() > 0)
{
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
}
}
else
{
if (support_layer.supportAreas.size() > 0)
{
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
}
}
if (support_layer.skin.size() > 0)
{
ret[getSettingAsIndex("support_interface_extruder_nr")] = true;
}
}
}
if (include_models)
{
for (const SliceMeshStorage& mesh : meshes)
{
if (layer_nr >= int(mesh.layers.size()))
{
continue;
}
const SliceLayer& layer = mesh.layers[layer_nr];
if (layer.parts.size() > 0)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
}
}
return ret;
}
+30 -42
Ver Arquivo
@@ -8,10 +8,9 @@
#include "utils/NoCopy.h"
#include "utils/AABB.h"
#include "mesh.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "PrimeTower.h"
#include "GCodePathConfig.h"
#include "gcodeExport.h" // CoastingConfig
namespace cura
{
@@ -125,6 +124,8 @@ class SupportLayer
public:
Polygons supportAreas; //!< normal support areas
Polygons skin; //!< the support areas which are to be printed as denser roofs and/or bottoms. Note that the roof/bottom areas and support areas should be mutually exclusive.
Polygons support_mesh; //!< Areas from support meshes
Polygons anti_overhang; //!< Areas where no overhang should be detected.
};
class SupportStorage
@@ -136,7 +137,11 @@ public:
std::vector<SupportLayer> supportLayers;
SupportStorage() : generated(false), layer_nr_max_filled_layer(-1) { }
SupportStorage()
: generated(false)
, layer_nr_max_filled_layer(-1)
{
}
~SupportStorage(){ supportLayers.clear(); }
};
/******************/
@@ -150,25 +155,14 @@ public:
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content (modified while infill meshes are processed)
GCodePathConfig inset0_config;
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
SubDivCube* base_subdiv_cube;
SliceMeshStorage(SettingsBaseVirtual* settings, unsigned int slice_layer_count)
: SettingsMessenger(settings)
, layer_nr_max_filled_layer(0)
, inset0_config(PrintFeatureType::OuterWall)
, insetX_config(PrintFeatureType::InnerWall)
, skin_config(PrintFeatureType::Skin)
, base_subdiv_cube(nullptr)
{
layers.reserve(slice_layer_count);
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(PrintFeatureType::Infill);
layers.resize(slice_layer_count);
}
virtual ~SliceMeshStorage();
@@ -179,50 +173,30 @@ 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.)
unsigned int print_layer_count; //!< The total number of layers (except the raft and filler layers)
Point3 model_size, model_min, model_max;
std::vector<SliceMeshStorage> meshes;
std::vector<RetractionConfig> retraction_config_per_extruder; //!< Retraction config per extruder.
std::vector<RetractionConfig> extruder_switch_retraction_config_per_extruder; //!< Retraction config per extruder for when performing an extruder switch
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
std::vector<GCodePathConfig> skirt_brim_config; //!< Configuration for skirt and brim 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_skin_config; //!< The config to use to print the dense roofs and bottoms of support
SupportStorage support;
Polygons skirt_brim[MAX_EXTRUDERS]; //!< Skirt and brim 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
int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder
std::vector<int> max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used.
std::vector<size_t> max_print_height_order; //!< Ordered indices into max_print_height_per_extruder: back() will return the extruder number with the highest print height.
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.
/*!
* Construct the initial retraction_config_per_extruder
*/
std::vector<RetractionConfig> initializeRetractionConfigs();
/*!
* Construct the initial travel_config_per_extruder
*/
std::vector<GCodePathConfig> initializeTravelConfigs();
/*!
* Construct the initial skirt & brim configurations for each extruder.
*/
std::vector<GCodePathConfig> initializeSkirtBrimConfigs();
/*!
* \brief Creates a new slice data storage that stores the slice data of the
* specified mesh group.
@@ -263,7 +237,21 @@ public:
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
std::vector<bool> getExtrudersUsed() const;
/*!
* Get the extruders used on a particular layer.
*
* \param layer_nr the layer for which to check
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed(int layer_nr) const;
private:
/*!
* Construct the retraction_config_per_extruder
*/
std::vector<RetractionConfig> initializeRetractionConfigs();
};
}//namespace cura
+10 -6
Ver Arquivo
@@ -216,7 +216,7 @@ SlicerLayer::findPossibleStitches(
// insert the starts of the polylines).
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
{
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
ConstPolygonRef polyline_0 = open_polylines[polyline_0_idx];
if (polyline_0.size() < 1) continue;
@@ -231,7 +231,7 @@ SlicerLayer::findPossibleStitches(
{
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
{
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
ConstPolygonRef polyline_0 = open_polylines[polyline_0_idx];
if (polyline_0.size() < 1) continue;
@@ -245,7 +245,7 @@ SlicerLayer::findPossibleStitches(
// search for nearby end points
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
{
const PolygonRef polyline_1 = open_polylines[polyline_1_idx];
ConstPolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
@@ -759,7 +759,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
for (PolygonRef polyline : open_polylines)
{
if (polyline.size() > 0)
openPolylines.add(polyline);
polygons.add(polyline);
}
}
@@ -887,10 +887,14 @@ Slicer::Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bo
}
}
log("slice of mesh took %.3f seconds\n",slice_timer.restart());
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
std::vector<SlicerLayer>& layers_ref = layers; // force layers not to be copied into the threads
#pragma omp parallel for default(none) shared(mesh,layers_ref) firstprivate(keep_none_closed, extensive_stitching)
for(unsigned int layer_nr=0; layer_nr<layers_ref.size(); layer_nr++)
{
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
layers_ref[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
}
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
}
+139 -75
Ver Arquivo
@@ -1,4 +1,7 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
//Copyright (C) 2013 David Braam
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include <cmath> // sqrt
#include <utility> // pair
#include <deque>
@@ -65,39 +68,66 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
{
int max_layer_nr_support_mesh_filled;
for (max_layer_nr_support_mesh_filled = storage.support.supportLayers.size() - 1; max_layer_nr_support_mesh_filled >= 0; max_layer_nr_support_mesh_filled--)
{
const SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
if (support_layer.supportAreas.size() > 0)
{
break;
}
}
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, max_layer_nr_support_mesh_filled);
for (int layer_nr = 0; layer_nr < max_layer_nr_support_mesh_filled; layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
support_layer.support_mesh = support_layer.support_mesh.unionPolygons();
}
// 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++)
if (layer_count > storage.support.supportLayers.size())
{ // there might alsready be anti_overhang_area data in the supportLayers
storage.support.supportLayers.resize(layer_count);
}
// generate support areas
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh"))
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
{
continue;
}
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
if (mesh.getSettingBoolean("support_interface_enable"))
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
{
generateSupportInterface(storage, mesh, supportAreas, layer_count);
}
else
{
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
{
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
}
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
}
}
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
{
Polygons& support_areas = storage.support.supportLayers[layer_idx].supportAreas;
support_areas = support_areas.unionPolygons();
}
// handle support interface
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
{
continue;
}
if (mesh.getSettingBoolean("support_interface_enable"))
{
generateSupportInterface(storage, mesh, layer_count);
}
}
}
/*
@@ -135,6 +165,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
const int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
const int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
const double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
const bool use_towers = mesh.getSettingBoolean("support_use_towers") && supportMinAreaSqrt > 0;
const int layerThickness = storage.getSettingInMicrons("layer_height");
const int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
@@ -204,7 +235,10 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt);
if (use_towers)
{
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt);
}
std::deque<std::pair<Polygons, Polygons>> basic_and_full_overhang_above;
for (unsigned int layer_idx = support_layer_count - 1; layer_idx != support_layer_count - 1 - layerZdistanceTop ; layer_idx--)
@@ -212,7 +246,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
}
bool still_in_upper_empty_layers = true;
int overhang_points_pos = overhang_points.size() - 1;
Polygons supportLayer_last;
std::vector<Polygons> towerRoofs;
@@ -235,7 +268,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
supportLayer_this = supportLayer_this.offset(extension_offset);
}
if (supportMinAreaSqrt > 0)
if (use_towers)
{
// handle straight walls
AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter);
@@ -247,8 +280,9 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
{ // join with support from layer up
supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, max_smoothing_angle, conical_support, conical_support_offset, conical_smallest_breadth);
}
supportLayer_this = supportLayer_this.unionPolygons(storage.support.supportLayers[layer_idx].support_mesh);
// move up from model
if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom)
{
@@ -256,8 +290,8 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight;
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false));
}
supportLayer_last = supportLayer_this;
@@ -277,18 +311,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
}
else
{
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
supportLayer_this = supportLayer_this.difference(outlines.offset(supportXYDistance));
}
}
supportAreas[layer_idx] = supportLayer_this;
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
}
@@ -326,6 +354,28 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
}
}
//Enforce top Z distance.
if (layerZdistanceTop > 1)
{
// this is performed after the main support generation loop above, because it affects the joining of polygons
// if this would be performed in the main loop then some support would not have been generated under the overhangs and consequently no support is generated for that,
// meaning almost no support would be generated in some cases which definitely need support.
for (size_t layer_idx = 0; layer_idx < storage.support.supportLayers.size() && layer_idx < support_layer_count - (layerZdistanceTop - 1); layer_idx++)
{
supportAreas[layer_idx] = supportAreas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layerZdistanceTop - 1, false));
}
}
for (unsigned int layer_idx = supportAreas.size() - 1; layer_idx != (unsigned int) std::max(-1, storage.support.layer_nr_max_filled_layer) ; layer_idx--)
{
const Polygons& support_here = supportAreas[layer_idx];
if (support_here.size() > 0)
{
storage.support.layer_nr_max_filled_layer = layer_idx;
break;
}
}
storage.support.generated = true;
}
@@ -349,6 +399,12 @@ std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const Sli
Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
const SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
if (support_layer.anti_overhang.size())
{
basic_overhang = basic_overhang.difference(support_layer.anti_overhang);
}
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
@@ -388,12 +444,17 @@ void AreaSupport::detectOverhangPoints(
if (part_poly.size() > 0)
{
Polygons part_poly_recomputed = part_poly.difference(storage.support.supportLayers[layer_idx].anti_overhang);
if (part_poly_recomputed.size() == 0)
{
continue;
}
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
overhang_points.back().second.push_back(part_poly);
overhang_points.back().second.push_back(part_poly_recomputed);
else
{
std::vector<Polygons> small_part_polys;
small_part_polys.push_back(part_poly);
small_part_polys.push_back(part_poly_recomputed);
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
}
}
@@ -444,15 +505,21 @@ void AreaSupport::handleTowers(
}
// make tower roofs
//for (Polygons& tower_roof : towerRoofs)
for (unsigned int r = 0; r < towerRoofs.size(); r++)
for (unsigned int roof_idx = 0; roof_idx < towerRoofs.size(); roof_idx++)
{
supportLayer_this = supportLayer_this.unionPolygons(towerRoofs[r]);
Polygons& tower_roof = towerRoofs[r];
if (tower_roof.size() > 0 && tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
Polygons& tower_roof = towerRoofs[roof_idx];
if (tower_roof.size() > 0)
{
towerRoofs[r] = tower_roof.offset(towerRoofExpansionDistance);
supportLayer_this = supportLayer_this.unionPolygons(tower_roof);
if (tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
{
tower_roof = tower_roof.offset(towerRoofExpansionDistance);
}
else
{
tower_roof.clear();
}
}
}
}
@@ -505,7 +572,7 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count)
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_count)
{
const unsigned int roof_layer_count = round_divide(mesh.getSettingInMicrons("support_roof_height"), storage.getSettingInMicrons("layer_height"));
const unsigned int bottom_layer_count = round_divide(mesh.getSettingInMicrons("support_bottom_height"), storage.getSettingInMicrons("layer_height"));
@@ -522,44 +589,41 @@ void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const Slic
const unsigned int top_layer_idx_above = layer_idx + roof_layer_count + z_distance_top;
const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom));
if (top_layer_idx_above < supportLayers.size())
if (top_layer_idx_above >= supportLayers.size())
{
Polygons roofs;
if (roof_layer_count > 0)
{
Polygons model;
const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count);
const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans));
for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip)
{
const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines();
model = model.unionPolygons(outlines_above);
}
roofs = support_areas[layer_idx].intersection(model);
}
Polygons bottoms;
if (bottom_layer_count > 0)
{
Polygons model;
const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count);
const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans));
for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip)
{
const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines();
model = model.unionPolygons(outlines_below);
}
bottoms = support_areas[layer_idx].intersection(model);
}
// expand skin a bit so that we're sure it's not too thin to be printed.
Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(support_areas[layer_idx]);
skin.removeSmallAreas(1.0);
layer.skin.add(skin);
layer.supportAreas.add(support_areas[layer_idx].difference(layer.skin));
continue;
}
else
Polygons roofs;
if (roof_layer_count > 0)
{
layer.skin.add(support_areas[layer_idx]);
Polygons model;
const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count);
const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans));
for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip)
{
const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines();
model = model.unionPolygons(outlines_above);
}
roofs = layer.supportAreas.intersection(model);
}
Polygons bottoms;
if (bottom_layer_count > 0)
{
Polygons model;
const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count);
const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans));
for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip)
{
const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines();
model = model.unionPolygons(outlines_below);
}
bottoms = layer.supportAreas.intersection(model);
}
// expand skin a bit so that we're sure it's not too thin to be printed.
Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(layer.supportAreas);
skin.removeSmallAreas(1.0);
layer.skin.add(skin);
layer.supportAreas = layer.supportAreas.difference(layer.skin);
}
}
+1 -3
Ver Arquivo
@@ -17,7 +17,6 @@ public:
* \param layer_count total number of layers
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
private:
/*!
* Generate support polygons over all layers for one object.
@@ -37,10 +36,9 @@ private:
*
* \param storage Output storage: support area + support skin area output
* \param mesh The mesh to generate support skins for.
* \param support_areas The basic support areas for the current mesh
* \param layer_count The number of layers in this mesh group.
*/
static void generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count);
static void generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_count);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
+1 -1
Ver Arquivo
@@ -57,7 +57,7 @@ public:
};
private:
double max_feedrate[NUM_AXIS] = {600, 600, 40, 25};
double max_feedrate[NUM_AXIS] = {600, 600, 40, 25}; // mm/s
double minimumfeedrate = 0.01;
double acceleration = 3000;
double max_acceleration[NUM_AXIS] = {9000, 9000, 100, 10000};
+2 -2
Ver Arquivo
@@ -24,7 +24,7 @@ AABB::AABB(const Polygons& polys)
calculate(polys);
}
AABB::AABB(const PolygonRef poly)
AABB::AABB(ConstPolygonRef poly)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
calculate(poly);
@@ -43,7 +43,7 @@ void AABB::calculate(const Polygons& polys)
}
}
void AABB::calculate(const PolygonRef poly)
void AABB::calculate(ConstPolygonRef poly)
{
min = Point(POINT_MAX, POINT_MAX);
max = Point(POINT_MIN, POINT_MIN);
+2 -2
Ver Arquivo
@@ -20,10 +20,10 @@ public:
AABB(); //!< initializes with invalid min and max
AABB(Point& min, Point& max); //!< initializes with given min and max
AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons
AABB(const PolygonRef poly); //!< Computes the boundary box for the given polygons
AABB(ConstPolygonRef poly); //!< Computes the boundary box for the given polygons
void calculate(const Polygons& polys); //!< Calculates the aabb for the given polygons (throws away old min and max data of this aabb)
void calculate(const PolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
void calculate(ConstPolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
/*!
* Check whether this aabb overlaps with another.
+8 -2
Ver Arquivo
@@ -15,8 +15,8 @@ AABB3D::AABB3D()
bool AABB3D::hit(const AABB3D& other) const
{
if ( max.x < other.min.y
|| min.x > other.max.y
if ( max.x < other.min.x
|| min.x > other.max.x
|| max.y < other.min.y
|| min.y > other.max.y
|| max.z < other.min.z
@@ -37,6 +37,12 @@ void AABB3D::include(Point3 p)
max.z = std::max(max.z, p.z);
}
void AABB3D::includeZ(int32_t z)
{
min.z = std::min(min.z, z);
max.z = std::max(max.z, z);
}
void AABB3D::offset(Point3 offset)
{
min += offset;
+8
Ver Arquivo
@@ -38,6 +38,14 @@ struct AABB3D
*/
void include(Point3 p);
/*!
* Expand the AABB3D to include a z-coordinate.
*
* This is for including a point of which the X and Y coordinates are
* unknown but known to already be included in the bounding box.
*/
void includeZ(int32_t z);
/*!
* Offset the coordinates of the bounding box.
* \param offset The offset with which to offset the AABB3D.
+129
Ver Arquivo
@@ -0,0 +1,129 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_LAZY_INITIALIZATION_H
#define UTILS_LAZY_INITIALIZATION_H
#include <functional> // bind, function
#include "optional.h"
namespace cura
{
/*!
* Class for initializing an object only when it's requested
*
* Credits to Johannes Goller
*
* \tparam T The type of the object to instantiate lazily
* \tparam Args The types of the arguments to the constructor or constructor function object
*/
template <typename T, typename... Args>
class LazyInitialization : public std::optional<T>
{
public:
/*!
* Delayed constructor call of T class
*
* \warning passing references or pointers as parameters means these objects will be given to the constructor at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(Args... args)
: std::optional<T>()
, constructor(
[args...]()
{
return new T(args...);
}
)
{ }
/*!
* Delayed function call for creating a T object
*
* Performs a copy from the return value of the function on the stack to the heap.
*
* \warning passing references or pointers as parameters means these objects will be given to the function object at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(const std::function<T (Args...)>& f, Args... args)
: std::optional<T>()
, constructor(
[f, args...]()
{
return new T(f(args...));
}
)
{ }
/*!
* Delayed function call for creating a T object
*
* \warning passing references or pointers as parameters means these objects will be given to the function object at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(const std::function<T* (Args...)>& f, Args... args)
: std::optional<T>()
, constructor(
[f, args...]()
{
return f(args...);
}
)
{
}
LazyInitialization(LazyInitialization<T, Args...>& other) //!< copy constructor
: std::optional<T>(other)
, constructor(other.constructor)
{
}
LazyInitialization(LazyInitialization<T, Args...>&& other) //!< move constructor
: std::optional<T>(other)
{
constructor = std::move(other.constructor);
}
/*!
* Dereference this lazy object
*
* Calls constructor if object isn't constructed yet.
*/
T& operator*()
{
if (!std::optional<T>::instance)
{
std::optional<T>::instance = constructor();
}
return std::optional<T>::operator*();
}
T* operator->() const
{
if (!std::optional<T>::instance)
{
std::optional<T>::instance = constructor();
}
return std::optional<T>::operator->();
}
LazyInitialization<T, Args...>& operator=(LazyInitialization<T, Args...>&& other)
{
std::optional<T>::operator=(other);
constructor = other.constructor;
return *this;
}
void swap(LazyInitialization<T, Args...>& other)
{
std::optional<T>::swap(other);
std::swap(constructor, other.constructor);
}
private:
std::function<T* ()> constructor;
};
}//namespace cura
#endif // UTILS_LAZY_INITIALIZATION_H
+36
Ver Arquivo
@@ -2,6 +2,8 @@
#include "linearAlg2D.h"
#include <cmath> // atan2
#include <cassert>
#include <algorithm> // swap
#include "intpoint.h" // dot
@@ -108,4 +110,38 @@ bool LinearAlg2D::getPointOnLineWithDist(const Point p, const Point a, const Poi
}
}
bool LinearAlg2D::lineSegmentsCollide(Point a_from_transformed, Point a_to_transformed, Point b_from_transformed, Point b_to_transformed)
{
assert(std::abs(a_from_transformed.Y - a_to_transformed.Y) < 2 && "line a is supposed to be transformed to be aligned with the X axis!");
assert(a_from_transformed.X - 2 <= a_to_transformed.X && "line a is supposed to be aligned with X axis in positive direction!");
if ((b_from_transformed.Y >= a_from_transformed.Y && b_to_transformed.Y <= a_from_transformed.Y) || (b_to_transformed.Y >= a_from_transformed.Y && b_from_transformed.Y <= a_from_transformed.Y))
{
if(b_to_transformed.Y == b_from_transformed.Y)
{
if (b_to_transformed.X < b_from_transformed.X)
{
std::swap(b_to_transformed.X, b_from_transformed.X);
}
if (b_from_transformed.X > a_to_transformed.X)
{
return false;
}
if (b_to_transformed.X < a_from_transformed.X)
{
return false;
}
return true;
}
else
{
int64_t x = b_from_transformed.X + (b_to_transformed.X - b_from_transformed.X) * (a_from_transformed.Y - b_from_transformed.Y) / (b_to_transformed.Y - b_from_transformed.Y);
if (x >= a_from_transformed.X && x <= a_to_transformed.X)
{
return true;
}
}
}
return false;
}
} // namespace cura
+7 -7
Ver Arquivo
@@ -10,21 +10,21 @@ namespace cura
{
void ListPolyIt::convertPolygonsToLists(Polygons& polys, ListPolygons& result)
void ListPolyIt::convertPolygonsToLists(const Polygons& polys, ListPolygons& result)
{
for (PolygonRef poly : polys)
for (ConstPolygonRef poly : polys)
{
result.emplace_back();
convertPolygonToList(poly, result.back());
}
}
void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result)
void ListPolyIt::convertPolygonToList(ConstPolygonRef poly, ListPolygon& result)
{
#ifdef DEBUG
Point last = poly.back();
#endif // DEBUG
for (Point& p : poly)
for (const Point& p : poly)
{
result.push_back(p);
#ifdef DEBUG
@@ -39,7 +39,7 @@ void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result)
}
void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons)
void ListPolyIt::convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons)
{
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
@@ -48,9 +48,9 @@ void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Poly
}
}
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon)
void ListPolyIt::convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon)
{
for (Point& p : list_polygon)
for (const Point& p : list_polygon)
{
polygon.add(p);
}
+6 -5
Ver Arquivo
@@ -49,10 +49,11 @@ public:
{
return !(*this == other);
}
void operator=(const ListPolyIt& other)
ListPolyIt& operator=(const ListPolyIt& other)
{
poly = other.poly;
it = other.it;
return *this;
}
//! move the iterator forward (and wrap around at the end)
ListPolyIt& operator++()
@@ -93,28 +94,28 @@ public:
* \param polys The polygons to convert
* \param result The converted polygons
*/
static void convertPolygonsToLists(Polygons& polys, ListPolygons& result);
static void convertPolygonsToLists(const Polygons& polys, ListPolygons& result);
/*!
* Convert Polygons to ListPolygons
*
* \param polys The polygons to convert
* \param result The converted polygons
*/
static void convertPolygonToList(PolygonRef poly, ListPolygon& result);
static void convertPolygonToList(ConstPolygonRef poly, ListPolygon& result);
/*!
* Convert ListPolygons to Polygons
*
* \param list_polygons The polygons to convert
* \param polygons The converted polygons
*/
static void convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons);
static void convertListPolygonsToPolygons(const ListPolygons& list_polygons, Polygons& polygons);
/*!
* Convert ListPolygons to Polygons
*
* \param list_polygons The polygons to convert
* \param polygons The converted polygons
*/
static void convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon);
static void convertListPolygonToPolygon(const ListPolygon& list_polygon, PolygonRef polygon);
/*!
* Insert a point into a ListPolygon if it's not a duplicate of the point before or the point after.
+4 -2
Ver Arquivo
@@ -112,9 +112,10 @@ void PolygonProximityLinker::findProximatePoints()
// o--->o-->o-->o->
// 2 4 6 8
std::unordered_set<ListPolyIt> nearby_lines;
auto process_func = [&nearby_lines](const ListPolyIt& elem)
std::function<bool (const ListPolyIt&)> process_func = [&nearby_lines](const ListPolyIt& elem)
{
nearby_lines.emplace(elem);
return true;
};
line_grid.processNearby(point_it.p(), proximity_distance, process_func);
for (const ListPolyIt& nearby_line : nearby_lines)
@@ -139,9 +140,10 @@ void PolygonProximityLinker::findProximatePoints()
// o--->o-->o-->o->
// 2 4 6 8
std::unordered_set<ListPolyIt> nearby_vert_its;
auto process_func = [&nearby_vert_its](const ListPolyIt& elem)
std::function<bool (const ListPolyIt&)> process_func = [&nearby_vert_its](const ListPolyIt& elem)
{
nearby_vert_its.emplace(elem);
return true;
};
line_grid.processNearby(new_point_it.p(), proximity_distance, process_func);
// because we use the same line_grid as before the resulting nearby_points
+9 -1
Ver Arquivo
@@ -43,6 +43,13 @@ public:
}
return (*polygons)[poly_idx][point_idx];
}
/*!
* Get the polygon to which this PolygonsPointIndex refers
*/
ConstPolygonRef getPolygon() const
{
return (*polygons)[poly_idx];
}
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
@@ -57,11 +64,12 @@ public:
{
return !(*this == other);
}
void operator=(const PolygonsPointIndex& other)
PolygonsPointIndex& operator=(const PolygonsPointIndex& other)
{
polygons = other.polygons;
poly_idx = other.poly_idx;
point_idx = other.point_idx;
return *this;
}
//! move the iterator forward (and wrap around at the end)
PolygonsPointIndex& operator++()
+5 -6
Ver Arquivo
@@ -93,9 +93,8 @@ public:
{
for(unsigned int j=0;j<parts.size();j++)
{
Polygon poly = parts[j];
fprintf(out, "<polygon points=\"");
for(Point& p : poly)
for (Point& p : parts[j])
{
Point fp = transform(p);
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
@@ -130,9 +129,9 @@ public:
}
}
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
void writePoints(ConstPolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
for (Point& p : poly)
for (const Point& p : poly)
{
writePoint(p, write_coords, size, color);
}
@@ -209,12 +208,12 @@ public:
}
void writePolygons(const Polygons& polys, Color color = Color::BLACK)
{
for (const PolygonRef poly : const_cast<Polygons&>(polys))
for (ConstPolygonRef poly : polys)
{
writePolygon(poly, color);
}
}
void writePolygon(const PolygonRef poly, Color color = Color::BLACK)
void writePolygon(ConstPolygonRef poly, Color color = Color::BLACK)
{
Point p0 = poly.back();
for (Point p1 : poly)
+143 -18
Ver Arquivo
@@ -8,6 +8,7 @@
#include <cassert>
#include <unordered_map>
#include <vector>
#include <functional>
namespace cura {
@@ -75,11 +76,22 @@ public:
* \param[in] query_pt The point to search around.
* \param[in] radius The search radius.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell.
* called for each element in the cell. Processing stops if function returns false.
*/
template<class ProcessFunc>
void processNearby(const Point &query_pt, coord_t radius,
ProcessFunc &process_func) const;
const std::function<bool (const ElemT&)>& process_func) const;
/*! \brief Process elements from cells that might contain sought after points along a line.
*
* Processes elements from cells that cross the line \p query_line.
* May process elements that are up to sqrt(2) * cell_size from \p query_line.
*
* \param[in] query_line The line along which to check each cell
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cells. Processing stops if function returns false.
*/
void processLine(const std::pair<Point, Point> query_line,
const std::function<bool (const Elem&)>& process_elem_func) const;
coord_t getCellSize() const;
@@ -92,11 +104,29 @@ protected:
*
* \param[in] grid_pt The grid coordinates of the cell.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell.
* called for each element in the cell. Processing stops if function returns false.
* \return Whether we need to continue processing a next cell.
*/
template<class ProcessFunc>
void processFromCell(const GridPoint &grid_pt,
ProcessFunc &process_func) const;
bool processFromCell(const GridPoint &grid_pt,
const std::function<bool (const Elem&)>& process_func) const;
/*! \brief Process cells along a line indicated by \p line.
*
* \param[in] line The line along which to process cells
* \param[in] process_func Processes each cell. process_func(elem) is
* called for each cell. Processing stops if function returns false.
*/
void processLineCells(const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func);
/*! \brief Process cells along a line indicated by \p line.
*
* \param[in] line The line along which to process cells
* \param[in] process_func Processes each cell. process_func(elem) is
* called for each cell. Processing stops if function returns false.
*/
void processLineCells(const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func) const;
/*! \brief Compute the grid coordinates of a point.
*
@@ -132,6 +162,8 @@ protected:
GridMap m_grid;
/*! \brief The cell (square) size. */
coord_t m_cell_size;
grid_coord_t nonzero_sign(const grid_coord_t z) const;
};
@@ -190,23 +222,99 @@ typename cura::coord_t SGI_THIS::toLowerCoord(const grid_coord_t& grid_coord) c
}
SGI_TEMPLATE
template<class ProcessFunc>
void SGI_THIS::processFromCell(
bool SGI_THIS::processFromCell(
const GridPoint &grid_pt,
ProcessFunc &process_func) const
const std::function<bool (const Elem&)>& process_func) const
{
auto grid_range = m_grid.equal_range(grid_pt);
for (auto iter = grid_range.first; iter != grid_range.second; ++iter)
{
process_func(iter->second);
if (!process_func(iter->second))
{
return false;
}
}
return true;
}
SGI_TEMPLATE
void SGI_THIS::processLineCells(
const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func)
{
static_cast<const SGI_THIS*>(this)->processLineCells(line, process_cell_func);
}
SGI_TEMPLATE
void SGI_THIS::processLineCells(
const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func) const
{
Point start = line.first;
Point end = line.second;
if (end.X < start.X)
{ // make sure X increases between start and end
std::swap(start, end);
}
const GridPoint start_cell = toGridPoint(start);
const GridPoint end_cell = toGridPoint(end);
const coord_t y_diff = end.Y - start.Y;
const grid_coord_t y_dir = nonzero_sign(y_diff);
grid_coord_t x_cell_start = start_cell.X;
for (grid_coord_t cell_y = start_cell.Y; cell_y * y_dir <= end_cell.Y * y_dir; cell_y += y_dir)
{ // for all Y from start to end
// nearest y coordinate of the cells in the next row
coord_t nearest_next_y = toLowerCoord(cell_y + ((nonzero_sign(cell_y) == y_dir || cell_y == 0) ? y_dir : coord_t(0)));
grid_coord_t x_cell_end; // the X coord of the last cell to include from this row
if (y_diff == 0)
{
x_cell_end = end_cell.X;
}
else
{
coord_t area = (end.X - start.X) * (nearest_next_y - start.Y);
// corresponding_x: the x coordinate corresponding to nearest_next_y
coord_t corresponding_x = start.X + area / y_diff;
x_cell_end = toGridCoord(corresponding_x + ((corresponding_x < 0) && ((area % y_diff) != 0)));
if (x_cell_end < start_cell.X)
{ // process at least one cell!
x_cell_end = x_cell_start;
}
}
for (grid_coord_t cell_x = x_cell_start; cell_x <= x_cell_end; ++cell_x)
{
GridPoint grid_loc(cell_x, cell_y);
bool continue_ = process_cell_func(grid_loc);
if (!continue_)
{
return;
}
if (grid_loc == end_cell)
{
return;
}
}
// TODO: this causes at least a one cell overlap for each row, which
// includes extra cells when crossing precisely on the corners
// where positive slope where x > 0 and negative slope where x < 0
x_cell_start = x_cell_end;
}
assert(false && "We should have returned already before here!");
}
SGI_TEMPLATE
typename SGI_THIS::grid_coord_t SGI_THIS::nonzero_sign(const grid_coord_t z) const
{
return (z >= 0) - (z < 0);
}
SGI_TEMPLATE
template<class ProcessFunc>
void SGI_THIS::processNearby(const Point &query_pt, coord_t radius,
ProcessFunc &process_func) const
const std::function<bool (const Elem&)>& process_func) const
{
Point min_loc(query_pt.X - radius, query_pt.Y - radius);
Point max_loc(query_pt.X + radius, query_pt.Y + radius);
@@ -219,19 +327,35 @@ void SGI_THIS::processNearby(const Point &query_pt, coord_t radius,
for (coord_t grid_x = min_grid.X; grid_x <= max_grid.X; ++grid_x)
{
GridPoint grid_pt(grid_x,grid_y);
processFromCell(grid_pt, process_func);
bool continue_ = processFromCell(grid_pt, process_func);
if (!continue_)
{
return;
}
}
}
}
SGI_TEMPLATE
void SGI_THIS::processLine(const std::pair<Point, Point> query_line,
const std::function<bool (const Elem&)>& process_elem_func) const
{
const std::function<bool (const GridPoint&)> process_cell_func = [&process_elem_func, this](GridPoint grid_loc)
{
return processFromCell(grid_loc, process_elem_func);
};
processLineCells(query_line, process_cell_func);
}
SGI_TEMPLATE
std::vector<typename SGI_THIS::Elem>
SGI_THIS::getNearby(const Point &query_pt, coord_t radius) const
{
std::vector<Elem> ret;
auto process_func = [&ret](const Elem &elem)
const std::function<bool (const Elem&)> process_func = [&ret](const Elem &elem)
{
ret.push_back(elem);
return true;
};
processNearby(query_pt, radius, process_func);
return ret;
@@ -252,12 +376,12 @@ bool SGI_THIS::getNearest(
{
bool found = false;
int64_t best_dist2 = static_cast<int64_t>(radius) * radius;
auto process_func =
const std::function<bool (const Elem&)> process_func =
[&query_pt, &elem_nearest, &found, &best_dist2, &precondition](const Elem &elem)
{
if (!precondition(elem))
{
return;
return true;
}
int64_t dist2 = vSize2(elem.point - query_pt);
if (dist2 < best_dist2)
@@ -266,6 +390,7 @@ bool SGI_THIS::getNearest(
elem_nearest = elem;
best_dist2 = dist2;
}
return true;
};
processNearby(query_pt, radius, process_func);
return found;
+16 -58
Ver Arquivo
@@ -6,6 +6,7 @@
#include <cassert>
#include <unordered_map>
#include <vector>
#include <functional>
#include "intpoint.h"
#include "SparseGrid.h"
@@ -50,7 +51,6 @@ protected:
/*! \brief Accessor for getting locations from elements. */
Locator m_locator;
grid_coord_t nonzero_sign(grid_coord_t z);
};
@@ -68,61 +68,19 @@ SGI_TEMPLATE
void SGI_THIS::insert(const Elem &elem)
{
const std::pair<Point, Point> line = m_locator(elem);
Point start = line.first;
Point end = line.second;
if (end.X < start.X)
{ // make sure X increases between start and end
std::swap(start, end);
}
const GridPoint start_cell = SparseGrid<ElemT>::toGridPoint(start);
const GridPoint end_cell = SparseGrid<ElemT>::toGridPoint(end);
const coord_t y_diff = end.Y - start.Y;
const grid_coord_t y_dir = nonzero_sign(y_diff);
grid_coord_t x_cell_start = start_cell.X;
for (grid_coord_t cell_y = start_cell.Y; cell_y * y_dir <= end_cell.Y * y_dir; cell_y += y_dir)
{ // for all Y from start to end
// nearest y coordinate of the cells in the next row
coord_t nearest_next_y = SparseGrid<ElemT>::toLowerCoord(cell_y + ((nonzero_sign(cell_y) == y_dir || cell_y == 0) ? y_dir : coord_t(0)));
grid_coord_t x_cell_end; // the X coord of the last cell to include from this row
if (y_diff == 0)
using GridMap = std::unordered_multimap<GridPoint, Elem>;
// below is a workaround for the fact that lambda functions cannot access private or protected members
// first we define a lambda which works on any GridMap and then we bind it to the actual protected GridMap of the parent class
std::function<bool (GridMap*, const GridPoint)> process_cell_func_ = [&elem, this](GridMap* m_grid, const GridPoint grid_loc)
{
x_cell_end = end_cell.X;
}
else
{
coord_t area = (end.X - start.X) * (nearest_next_y - start.Y);
// corresponding_x: the x coordinate corresponding to nearest_next_y
coord_t corresponding_x = start.X + area / y_diff;
x_cell_end = SparseGrid<ElemT>::toGridCoord(corresponding_x + ((corresponding_x < 0) && ((area % y_diff) != 0)));
if (x_cell_end < start_cell.X)
{ // process at least one cell!
x_cell_end = x_cell_start;
}
}
m_grid->emplace(grid_loc, elem);
return true;
};
using namespace std::placeholders; // for _1, _2, _3...
GridMap* m_grid = &(this->m_grid);
std::function<bool (const GridPoint)> process_cell_func(std::bind(process_cell_func_, m_grid, _1));
for (grid_coord_t cell_x = x_cell_start; cell_x <= x_cell_end; ++cell_x)
{
GridPoint grid_loc(cell_x, cell_y);
SparseGrid<ElemT>::m_grid.emplace(grid_loc, elem);
if (grid_loc == end_cell)
{
return;
}
}
// TODO: this causes at least a one cell overlap for each row, which
// includes extra cells when crossing precisely on the corners
// where positive slope where x > 0 and negative slope where x < 0
x_cell_start = x_cell_end;
}
assert(false && "We should have returned already before here!");
}
SGI_TEMPLATE
typename SGI_THIS::grid_coord_t SGI_THIS::nonzero_sign(grid_coord_t z)
{
return (z >= 0) - (z < 0);
SparseGrid<ElemT>::processLineCells(line, process_cell_func);
}
SGI_TEMPLATE
@@ -132,16 +90,16 @@ void SGI_THIS::debugHTML(std::string filename)
for (std::pair<GridPoint, ElemT> cell: SparseGrid<ElemT>::m_grid)
{
aabb.include(SparseGrid<ElemT>::toLowerCorner(cell.first));
aabb.include(SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(nonzero_sign(cell.first.X), nonzero_sign(cell.first.Y))));
aabb.include(SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), SparseGrid<ElemT>::nonzero_sign(cell.first.Y))));
}
SVG svg(filename.c_str(), aabb);
for (std::pair<GridPoint, ElemT> cell: SparseGrid<ElemT>::m_grid)
{
// doesn't draw cells at x = 0 or y = 0 correctly (should be double size)
Point lb = SparseGrid<ElemT>::toLowerCorner(cell.first);
Point lt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(0, nonzero_sign(cell.first.Y)));
Point rt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(nonzero_sign(cell.first.X), nonzero_sign(cell.first.Y)));
Point rb = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(nonzero_sign(cell.first.X), 0));
Point lt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(0, SparseGrid<ElemT>::nonzero_sign(cell.first.Y)));
Point rt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), SparseGrid<ElemT>::nonzero_sign(cell.first.Y)));
Point rb = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), 0));
if (lb.X == 0)
{
lb.X = -SparseGrid<ElemT>::m_cell_size;
+2 -1
Ver Arquivo
@@ -109,9 +109,10 @@ std::vector<Val>
SG_THIS::getNearbyVals(const Point &query_pt, coord_t radius) const
{
std::vector<Val> ret;
auto process_func = [&ret](const typename SG_THIS::Elem &elem)
std::function<bool (const SparsePointGridInclusiveImpl::SparsePointGridInclusiveElem<Val>&)> process_func = [&ret](const typename SG_THIS::Elem &elem)
{
ret.push_back(elem.val);
return true;
};
this->processNearby(query_pt, radius, process_func);
return ret;
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_ALGORITHM_H
#define UTILS_ALGORITHM_H
#include <algorithm>
#include <vector>
#include <functional>
#include <numeric>
// extensions to algorithm.h from the standard library
namespace cura
{
/*!
* Get the order of a vector: the sorted indices of a vector
*
* {1.6, 1.8, 1.7} returns {1, 3, 2} meaning {in[1], in[3], in[2]} is a sorted
* vector
*
* Thanks to Lukasz Wiklendt
*
* \param in The vector for which to get the order
* \return An ordered vector of indices into \p in
*/
template<typename T>
std::vector<size_t> order(const std::vector<T> &in)
{
// initialize original index locations
std::vector<size_t> order(in.size());
std::iota(order.begin(), order.end(), 0); // fill vector with 1, 2, 3,.. etc
// sort indexes based on comparing values in v
std::sort(order.begin(), order.end(),
[&in](size_t i1, size_t i2)
{
return in[i1] < in[i2];
}
);
return order;
}
} // namespace cura
#endif // UTILS_ALGORITHM_H
+17 -1
Ver Arquivo
@@ -216,7 +216,23 @@ public:
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
}
/*!
* Check whether two line segments collide.
*
* \warning Edge cases (end points of line segments fall on other line segment) register as a collision.
*
* \note All points are assumed to be transformed by the transformation matrix of the vector from \p a_from to \p a_to.
* I.e. a is a vertical line; the Y of \p a_from_transformed is the same as the Y of \p a_to_transformed.
*
* \param a_from_transformed The transformed from location of line a
* \param a_from_transformed The transformed to location of line a
* \param b_from_transformed The transformed from location of line b
* \param b_from_transformed The transformed to location of line b
* \return Whether the two line segments collide
*/
static bool lineSegmentsCollide(Point a_from_transformed, Point a_to_transformed, Point b_from_transformed, Point b_to_transformed);
/*!
* Compute the angle between two consecutive line segments.
*
+13 -6
Ver Arquivo
@@ -3,6 +3,8 @@
#define UTILS_OPTIONAL_H
#include <algorithm> // swap
#include <type_traits> // enable_if is_same
#include <cassert> // assert
namespace std
{
@@ -19,6 +21,7 @@ namespace std
template<typename T>
class optional
{
protected:
T* instance;
public:
optional() //!< create an optional value which is not instantiated
@@ -42,11 +45,11 @@ public:
other.instance = nullptr;
}
template<class... Args>
constexpr explicit optional(bool not_used, Args&&... args ) //!< construct the value in place
constexpr explicit optional(bool, Args&&... args ) //!< construct the value in place
: instance(new T(args...))
{
}
~optional() //!< simple destructor
virtual ~optional() //!< simple destructor
{
if (instance)
{
@@ -59,7 +62,7 @@ public:
* \param null_ptr exactly [nullptr]
* \return this
*/
optional& operator=(std::nullptr_t null_ptr)
optional& operator=(std::nullptr_t)
{
if (instance)
{
@@ -72,13 +75,13 @@ public:
{
if (instance)
{
delete instance;
if (other.instance)
{
*instance = *other.instance;
}
else
{
delete instance;
instance = nullptr;
}
}
@@ -101,12 +104,14 @@ public:
other.instance = nullptr;
return *this;
}
template<class U>
template<class U = T
, typename = typename std::enable_if<std::is_assignable<T&, U>::value>::type // type U is T, T& or T&&
>
optional& operator=(U&& value)
{
if (instance)
{
*instance = value;
*instance = std::forward<U>(value);
}
else
{
@@ -116,10 +121,12 @@ public:
}
constexpr T* operator->() const
{
assert(instance && "Instance should be instantiated!");
return instance;
}
constexpr T& operator*() const&
{
assert(instance && "Instance should be instantiated!");
return *instance;
}
constexpr explicit operator bool() const
+99
Ver Arquivo
@@ -0,0 +1,99 @@
/** Copyright (C) 2016 Ultimaker B.V. - Released under terms of the AGPLv3 License */
#ifndef UTILS_ORDER_OPTIMIZER_H
#define UTILS_ORDER_OPTIMIZER_H
#include <stdint.h>
#include <vector>
#include <list>
#include <utility> // pair
#include "intpoint.h"
namespace cura {
/*!
* Order optimization class.
*
* Utility class for optimizing the path order by minimizing the cyclic distance traveled between several items.
*
* The path is heuristically optimized in a way such that each node is visited and the salesman which is travelling ends up where he started.
*/
template <typename T>
class OrderOptimizer
{
public:
std::vector<std::pair<const Point, T>> items; //!< the items in arbitrary order
OrderOptimizer()
{
}
void addItem(const Point location, const T item);
/*!
* Optimize the order of \ref OrderOptimizer::items
* \return A vector of the ordered indices into \ref OrderOptimizer::items
*/
std::list<unsigned int> optimize();
};
template <typename T>
void OrderOptimizer<T>::addItem(const Point location, const T item)
{
this->items.emplace_back(location, item);
}
template <typename T>
std::list<unsigned int> OrderOptimizer<T>::optimize()
{
// least detour insertion algorithm
std::list<unsigned int> order;
if (items.size() == 0)
{
return order;
}
order.push_back(0u);
if (items.size() == 1)
{
return order;
}
order.push_back(1u);
if (items.size() == 2)
{
return order;
}
order.push_back(2u);
for (unsigned int item_idx = 3; item_idx < items.size(); item_idx++)
{
Point to_insert_item_location = items[item_idx].first;
// find best_item_to_insert_before
std::list<unsigned int>::iterator best_item_to_insert_before = order.begin();
coord_t best_detour_dist = vSize(items[*best_item_to_insert_before].first - to_insert_item_location)
+ vSize(to_insert_item_location - items[order.back()].first)
- vSize(items[*best_item_to_insert_before].first - items[order.back()].first);
std::list<unsigned int>::iterator prev = order.begin();
for (std::list<unsigned int>::iterator nearby = ++order.begin(); nearby != order.end(); ++nearby)
{
coord_t detour_dist = vSize(items[*nearby].first - to_insert_item_location)
+ vSize(to_insert_item_location - items[*prev].first)
- vSize(items[*nearby].first - items[*prev].first);
if (detour_dist < best_detour_dist)
{
best_detour_dist = detour_dist;
best_item_to_insert_before = nearby;
}
prev = nearby;
}
order.insert(best_item_to_insert_before, item_idx);
}
return order;
}
}//namespace cura
#endif//UTILS_ORDER_OPTIMIZER_H
+48 -32
Ver Arquivo
@@ -8,9 +8,9 @@
namespace cura
{
bool PolygonRef::shorterThan(int64_t check_length) const
bool ConstPolygonRef::shorterThan(int64_t check_length) const
{
const PolygonRef& polygon = *this;
const ConstPolygonRef& polygon = *this;
const Point* p0 = &polygon.back();
int64_t length = 0;
for (const Point& p1 : polygon)
@@ -25,9 +25,9 @@ bool PolygonRef::shorterThan(int64_t check_length) const
return true;
}
bool PolygonRef::_inside(Point p, bool border_result) const
bool ConstPolygonRef::_inside(Point p, bool border_result) const
{
PolygonRef thiss = *this;
const ConstPolygonRef thiss = *this;
if (size() < 1)
{
return false;
@@ -83,6 +83,21 @@ unsigned int Polygons::pointCount() const
}
bool Polygons::inside(Point p, bool border_result) const
{
int poly_count_inside = 0;
for (const ClipperLib::Path& poly : *this)
{
const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly);
if (is_inside_this_poly == -1)
{
return border_result;
}
poly_count_inside += is_inside_this_poly;
}
return (poly_count_inside % 2) == 1;
}
bool Polygons::insideOld(Point p, bool border_result) const
{
const Polygons& thiss = *this;
if (size() < 1)
@@ -185,11 +200,11 @@ Polygons Polygons::offset(int distance, ClipperLib::JoinType join_type, double m
return ret;
}
Polygons PolygonRef::offset(int distance, ClipperLib::JoinType joinType, double miter_limit) const
Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType join_type, double miter_limit) const
{
Polygons ret;
ClipperLib::ClipperOffset clipper(miter_limit, 10.0);
clipper.AddPath(*path, joinType, ClipperLib::etClosedPolygon);
clipper.AddPath(*path, join_type, ClipperLib::etClosedPolygon);
clipper.MiterLimit = miter_limit;
clipper.Execute(ret.paths, distance);
return ret;
@@ -506,7 +521,7 @@ void Polygons::removeEmptyHoles_processPolyTreeNode(const ClipperLib::PolyNode&
}
}
bool PolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length)
bool ConstPolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, ListPolyIt& p0_it, ListPolyIt& p2_it, const int64_t shortcut_length)
{
// walk away from the corner until the shortcut > shortcut_length or it would smooth a piece inward
// - walk in both directions untill shortcut > shortcut_length
@@ -688,7 +703,7 @@ bool PolygonRef::smooth_corner_complex(ListPolygon& poly, const Point p1, ListPo
return false;
}
void PolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far)
void ConstPolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far)
{
const bool forward_has_converged = forward_is_blocked || forward_is_too_far;
const bool backward_has_converged = backward_is_blocked || backward_is_too_far;
@@ -744,7 +759,7 @@ void PolygonRef::smooth_outward_step(const Point p1, const int64_t shortcut_leng
}
}
void PolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle)
void ConstPolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const Point p1, const Point p2, const ListPolyIt p0_it, const ListPolyIt p1_it, const ListPolyIt p2_it, const Point v10, const Point v12, const Point v02, const int64_t shortcut_length, float cos_angle)
{
// 1----b---->2
// ^ /
@@ -822,7 +837,6 @@ void PolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const P
Point b;
bool success = LinearAlg2D::getPointOnLineWithDist(a, p1, p2, shortcut_length, b);
// v02 has to be longer than ab!
p1_it.remove();
if (success)
{ // if not success then assume b is negligibly close to 2, but rounding errors caused a problem
#ifdef ASSERT_INSANE_OUTPUT
@@ -830,11 +844,12 @@ void PolygonRef::smooth_corner_simple(ListPolygon& poly, const Point p0, const P
#endif // #ifdef ASSERT_INSANE_OUTPUT
ListPolyIt::insertPointNonDuplicate(p1_it, p2_it, b);
}
p1_it.remove();
}
}
}
void PolygonRef::smooth_outward(float min_angle, int shortcut_length, PolygonRef result) const
void ConstPolygonRef::smooth_outward(float min_angle, int shortcut_length, PolygonRef result) const
{
// example of smoothed out corner:
//
@@ -937,7 +952,8 @@ Polygons Polygons::smooth_outward(float max_angle, int shortcut_length)
return ret;
}
void PolygonRef::smooth(int remove_length, PolygonRef result)
void ConstPolygonRef::smooth(int remove_length, PolygonRef result) const
{
// a typical zigzag with the middle part to be removed by removing (1) :
//
@@ -952,7 +968,7 @@ void PolygonRef::smooth(int remove_length, PolygonRef result)
// |
// |
// 0
PolygonRef& thiss = *this;
const ConstPolygonRef& thiss = *path;
ClipperLib::Path* poly = result.path;
if (size() > 0)
{
@@ -1021,12 +1037,12 @@ void PolygonRef::smooth(int remove_length, PolygonRef result)
}
}
Polygons Polygons::smooth(int remove_length)
Polygons Polygons::smooth(int remove_length) const
{
Polygons ret;
for (unsigned int p = 0; p < size(); p++)
{
PolygonRef poly(paths[p]);
ConstPolygonRef poly(paths[p]);
if (poly.size() < 3)
{
continue;
@@ -1046,23 +1062,23 @@ Polygons Polygons::smooth(int remove_length)
return ret;
}
void PolygonRef::smooth2(int remove_length, PolygonRef result)
void ConstPolygonRef::smooth2(int remove_length, PolygonRef result) const
{
PolygonRef& thiss = *this;
const ConstPolygonRef& thiss = *this;
ClipperLib::Path* poly = result.path;
if (size() > 0)
if (thiss.size() > 0)
{
poly->push_back(thiss[0]);
}
for (unsigned int poly_idx = 1; poly_idx < size(); poly_idx++)
for (unsigned int poly_idx = 1; poly_idx < thiss.size(); poly_idx++)
{
Point& last = thiss[poly_idx - 1];
Point& now = thiss[poly_idx];
Point& next = thiss[(poly_idx + 1) % size()];
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
const Point& last = thiss[poly_idx - 1];
const Point& now = thiss[poly_idx];
const Point& next = thiss[(poly_idx + 1) % thiss.size()];
if (shorterThen(last - now, remove_length) && shorterThen(now - next, remove_length))
{
poly_idx++; // skip the next line piece (dont escalate the removal of edges)
if (poly_idx < size())
if (poly_idx < thiss.size())
{
poly->push_back(thiss[poly_idx]);
}
@@ -1074,12 +1090,12 @@ void PolygonRef::smooth2(int remove_length, PolygonRef result)
}
}
Polygons Polygons::smooth2(int remove_length, int min_area)
Polygons Polygons::smooth2(int remove_length, int min_area) const
{
Polygons ret;
for (unsigned int p = 0; p < size(); p++)
{
PolygonRef poly(paths[p]);
ConstPolygonRef poly(paths[p]);
if (poly.size() == 0)
{
continue;
@@ -1132,14 +1148,14 @@ void Polygons::splitIntoParts_processPolyTreeNode(ClipperLib::PolyNode* node, st
}
}
unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
unsigned int PartsView::getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx) const
{
PartsView& partsView = *this;
const 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];
const 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);
std::vector<unsigned int>::const_iterator result = std::find(partView.begin(), partView.end(), poly_idx);
if (result != partView.end())
{
if (boundary_poly_idx) { *boundary_poly_idx = partView[0]; }
@@ -1163,7 +1179,7 @@ PolygonsPart PartsView::assemblePart(unsigned int part_idx) const
return ret;
}
PolygonsPart PartsView::assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx)
PolygonsPart PartsView::assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx) const
{
PolygonsPart ret;
unsigned int part_idx = getPartContaining(poly_idx, boundary_poly_idx);
@@ -1200,7 +1216,7 @@ void Polygons::splitIntoPartsView_processPolyTreeNode(PartsView& partsView, Poly
partsView.emplace_back();
unsigned int pos = partsView.size() - 1;
partsView[pos].push_back(reordered.size());
reordered.add(child->Contour);
reordered.add(child->Contour); //TODO: should this steal the internal representation for speed?
for(int i = 0; i < child->ChildCount(); i++)
{
partsView[pos].push_back(reordered.size());
+225 -125
Ver Arquivo
@@ -11,9 +11,11 @@
#include <limits> // int64_t.min
#include <list>
#include <initializer_list>
#include "intpoint.h"
//#define CHECK_POLY_ACCESS
#define CHECK_POLY_ACCESS
#ifdef CHECK_POLY_ACCESS
#define POLY_ASSERT(e) assert(e)
#else
@@ -25,6 +27,8 @@ namespace cura {
class PartsView;
class Polygons;
class Polygon;
class PolygonRef;
class ListPolyIt;
@@ -34,31 +38,60 @@ typedef std::vector<ListPolygon> ListPolygons; //!< Polygons represented by a ve
const static int clipper_init = (0);
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
class PolygonRef
class ConstPolygonRef
{
friend class Polygons;
friend class Polygon;
friend class PolygonRef;
protected:
ClipperLib::Path* path;
PolygonRef()
ConstPolygonRef()
: path(nullptr)
{}
public:
PolygonRef(ClipperLib::Path& polygon)
: path(&polygon)
ConstPolygonRef(const ClipperLib::Path& polygon)
: path(const_cast<ClipperLib::Path*>(&polygon))
{}
bool operator==(ConstPolygonRef& other) const =delete;
// needed in std::optional<ConstPolygonRef>
// ConstPolygonRef& operator=(const ConstPolygonRef& other) =delete;
ConstPolygonRef& operator=(const ConstPolygonRef& other)
{
path = other.path;
return *this;
}
unsigned int size() const
{
return path->size();
}
Point& operator[] (unsigned int index) const
const Point& operator[] (unsigned int index) const
{
POLY_ASSERT(index < size());
return (*path)[index];
}
void* data()
const ClipperLib::Path& operator*() const
{
return path->data();
return *path;
}
ClipperLib::Path::const_iterator begin() const
{
return path->begin();
}
ClipperLib::Path::const_iterator end() const
{
return path->end();
}
ClipperLib::Path::const_reference back() const
{
return path->back();
}
const void* data() const
@@ -66,37 +99,10 @@ public:
return path->data();
}
void add(const Point p)
{
path->push_back(p);
}
PolygonRef& operator=(const PolygonRef& other) { path = other.path; return *this; }
bool operator==(const PolygonRef& other) const =delete;
ClipperLib::Path& operator*() { return *path; }
template <typename... Args>
void emplace_back(Args&&... args)
{
path->emplace_back(args...);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
path->erase(path->begin() + index);
}
void clear()
{
path->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
@@ -104,11 +110,6 @@ public:
return ClipperLib::Orientation(*path);
}
void reverse()
{
ClipperLib::ReversePath(*path);
}
Polygons offset(int distance, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miter_limit = 1.2) const;
int64_t polygonLength() const
@@ -123,7 +124,7 @@ public:
}
return length;
}
bool shorterThan(int64_t check_length) const;
Point min() const
@@ -136,7 +137,7 @@ public:
}
return ret;
}
Point max() const
{
Point ret = Point(POINT_MIN, POINT_MIN);
@@ -152,19 +153,6 @@ public:
{
return ClipperLib::Area(*path);
}
/*!
* Translate the whole polygon in some direction.
*
* \param translation The direction in which to move the polygon
*/
void translate(Point translation)
{
for (Point& p : *this)
{
p += translation;
}
}
Point centerOfMass() const
{
@@ -181,14 +169,14 @@ public:
}
double area = Area(*path);
x = x / 6 / area;
y = y / 6 / area;
return Point(x, y);
}
Point closestPointTo(Point p)
Point closestPointTo(Point p) const
{
Point ret = p;
float bestDist = FLT_MAX;
@@ -203,7 +191,7 @@ public:
}
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.
@@ -239,7 +227,7 @@ public:
}
return res == 1;
}
/*!
* Smooth out small perpendicular segments and store the result in \p result.
* Smoothing is performed by removing the inner most vertex of a line segment smaller than \p remove_length
@@ -251,7 +239,7 @@ public:
* \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);
void smooth(int remove_length, PolygonRef result) const;
/*!
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
@@ -269,51 +257,8 @@ public:
* \param remove_length The length of the largest segment removed
* \param result (output) The result polygon, assumed to be empty
*/
void smooth2(int remove_length, PolygonRef result);
void smooth2(int remove_length, PolygonRef result) const;
/*!
* Removes consecutive line segments with same orientation and changes this polygon.
*
* Removes verts which are connected to line segments which are both too small.
* Removes verts which detour from a direct line from the previous and next vert by a too small amount.
*
* \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()
{
path->pop_back();
}
ClipperLib::Path::reference back() const
{
return path->back();
}
ClipperLib::Path::iterator begin()
{
return path->begin();
}
ClipperLib::Path::iterator end()
{
return path->end();
}
ClipperLib::Path::const_iterator begin() const
{
return path->begin();
}
ClipperLib::Path::const_iterator end() const
{
return path->end();
}
friend class Polygons;
friend class Polygon;
private:
/*!
@@ -373,6 +318,113 @@ private:
static void smooth_outward_step(const Point p1, const int64_t shortcut_length2, ListPolyIt& p0_it, ListPolyIt& p2_it, bool& forward_is_blocked, bool& backward_is_blocked, bool& forward_is_too_far, bool& backward_is_too_far);
};
class PolygonRef : public ConstPolygonRef
{
PolygonRef()
: ConstPolygonRef()
{}
public:
PolygonRef(ClipperLib::Path& polygon)
: ConstPolygonRef(polygon)
{}
PolygonRef& operator=(const PolygonRef& other)
{
path = other.path;
return *this;
}
Point& operator[] (unsigned int index)
{
POLY_ASSERT(index < size());
return (*path)[index];
}
ClipperLib::Path::iterator begin()
{
return path->begin();
}
ClipperLib::Path::iterator end()
{
return path->end();
}
ClipperLib::Path::reference back()
{
return path->back();
}
void* data()
{
return path->data();
}
void add(const Point p)
{
path->push_back(p);
}
PolygonRef& operator=(ConstPolygonRef& other) { path = other.path; return *this; }
ClipperLib::Path& operator*()
{
return *path;
}
template <typename... Args>
void emplace_back(Args&&... args)
{
path->emplace_back(args...);
}
void remove(unsigned int index)
{
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
path->erase(path->begin() + index);
}
void clear()
{
path->clear();
}
void reverse()
{
ClipperLib::ReversePath(*path);
}
/*!
* Translate the whole polygon in some direction.
*
* \param translation The direction in which to move the polygon
*/
void translate(Point translation)
{
for (Point& p : *this)
{
p += translation;
}
}
/*!
* Removes consecutive line segments with same orientation and changes this polygon.
*
* Removes verts which are connected to line segments which are both too small.
* Removes verts which detour from a direct line from the previous and next vert by a too small amount.
*
* \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()
{
path->pop_back();
}
};
class Polygon : public PolygonRef
{
ClipperLib::Path poly;
@@ -382,7 +434,7 @@ public:
{
}
Polygon(const PolygonRef& other)
Polygon(PolygonRef& other)
: PolygonRef(poly)
{
poly = *other.path;
@@ -395,6 +447,7 @@ class Polygons
{
friend class Polygon;
friend class PolygonRef;
friend class ConstPolygonRef;
protected:
ClipperLib::Paths paths;
public:
@@ -407,12 +460,13 @@ public:
PolygonRef operator[] (unsigned int index)
{
POLY_ASSERT(index < size());
return PolygonRef(paths[index]);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
return paths[index];
}
const PolygonRef operator[] (unsigned int index) const
ConstPolygonRef operator[] (unsigned int index) const
{
return const_cast<Polygons*>(this)->operator[](index);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
return paths[index];
}
ClipperLib::Paths::iterator begin()
{
@@ -430,11 +484,23 @@ public:
{
return paths.end();
}
/*!
* Remove a polygon from the list and move the last polygon to its place
*
* \warning changes the order of the polygons!
*/
void remove(unsigned int index)
{
POLY_ASSERT(index < size());
paths.erase(paths.begin() + index);
POLY_ASSERT(index < size() && index <= std::numeric_limits<int>::max());
if (index < paths.size() - 1)
{
paths[index] = std::move(paths.back());
}
paths.resize(paths.size() - 1);
}
/*!
* Remove a range of polygons
*/
void erase(ClipperLib::Paths::iterator start, ClipperLib::Paths::iterator end)
{
paths.erase(start, end);
@@ -443,7 +509,11 @@ public:
{
paths.clear();
}
void add(const PolygonRef& poly)
void add(ConstPolygonRef& poly)
{
paths.push_back(*poly.path);
}
void add(const ConstPolygonRef& poly)
{
paths.push_back(*poly.path);
}
@@ -456,6 +526,13 @@ public:
for(unsigned int n=0; n<other.paths.size(); n++)
paths.push_back(other.paths[n]);
}
/*!
* Add a 'polygon' consisting of two points
*/
void addLine(const Point from, const Point to)
{
paths.emplace_back((std::initializer_list<Point>){from, to});
}
template<typename... Args>
void emplace_back(Args... args)
@@ -472,6 +549,10 @@ public:
{
return PolygonRef(paths.back());
}
ConstPolygonRef back() const
{
return ConstPolygonRef(paths.back());
}
Polygons() {}
@@ -551,6 +632,20 @@ public:
return ret;
}
/*!
* Check if we are inside the polygon.
*
* We do this by counting the number of polygons inside which this point lies.
* An odd number is inside, while an even number is outside.
*
* 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) const;
/*!
* 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.
@@ -563,11 +658,13 @@ public:
*
* Returns false if outside, true if inside; if the point lies exactly on the border, will return \p border_result.
*
* \deprecated This function is old and no longer used. instead use \ref Polygons::inside
*
* \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) const;
bool insideOld(Point p, bool border_result = false) const;
/*!
* Find the polygon inside which point \p p resides.
@@ -610,7 +707,7 @@ public:
* \param remove_length The length of the largest segment removed
* \return The smoothed polygon
*/
Polygons smooth(int remove_length);
Polygons smooth(int remove_length) const;
/*!
* Smooth out sharp inner corners, by taking a shortcut which bypasses the corner
@@ -621,7 +718,7 @@ public:
*/
Polygons smooth_outward(float angle, int shortcut_length);
Polygons smooth2(int remove_length, int min_area); //!< removes points connected to small lines
Polygons smooth2(int remove_length, int min_area) const; //!< removes points connected to small lines
/*!
* removes points connected to similarly oriented lines
@@ -761,16 +858,16 @@ public:
* Removes the same polygons from this set (and also empty polygons).
* Polygons are considered the same if all points lie within [same_distance] of their counterparts.
*/
Polygons remove(Polygons& to_be_removed, int same_distance = 0)
Polygons remove(const Polygons& to_be_removed, int same_distance = 0) const
{
Polygons result;
for (unsigned int poly_keep_idx = 0; poly_keep_idx < size(); poly_keep_idx++)
{
PolygonRef poly_keep = (*this)[poly_keep_idx];
ConstPolygonRef poly_keep = (*this)[poly_keep_idx];
bool should_be_removed = false;
if (poly_keep.size() > 0)
// for (int hole_poly_idx = 0; hole_poly_idx < to_be_removed.size(); hole_poly_idx++)
for (PolygonRef poly_rem : to_be_removed)
for (ConstPolygonRef poly_rem : to_be_removed)
{
// PolygonRef poly_rem = to_be_removed[hole_poly_idx];
if (poly_rem.size() != poly_keep.size() || poly_rem.size() == 0) continue;
@@ -886,10 +983,13 @@ public:
class PolygonsPart : public Polygons
{
public:
PolygonRef outerPolygon()
PolygonRef outerPolygon()
{
Polygons& thiss = *this;
return thiss[0];
return this->paths[0];
}
ConstPolygonRef outerPolygon() const
{
return this->paths[0];
}
bool inside(Point p)
@@ -922,7 +1022,7 @@ public:
* \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);
unsigned int getPartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr) const;
/*!
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
*
@@ -930,7 +1030,7 @@ public:
* \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);
PolygonsPart assemblePartContaining(unsigned int poly_idx, unsigned int* boundary_poly_idx = nullptr) const;
/*!
* Assemble the PolygonsPart of which the polygon with index \p poly_idx is part.
*
+167 -89
Ver Arquivo
@@ -22,7 +22,7 @@ int64_t PolygonUtils::segmentLength(PolygonsPointIndex start, PolygonsPointIndex
assert(start.poly_idx == end.poly_idx);
int64_t segment_length = 0;
Point prev_vert = start.p();
const PolygonRef poly = (*start.polygons)[start.poly_idx];
ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
for (unsigned int point_idx = 1; point_idx <= poly.size(); point_idx++)
{
unsigned int vert_idx = (start.point_idx + point_idx) % poly.size();
@@ -44,7 +44,7 @@ void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end,
assert(start.poly_idx == end.poly_idx);
int64_t segment_length = segmentLength(start, end);
const PolygonRef poly = (*start.polygons)[start.poly_idx];
ConstPolygonRef poly = (*start.polygons)[start.poly_idx];
unsigned int n_dots_in_between = n_dots;
if (start == end)
{
@@ -80,7 +80,7 @@ void PolygonUtils::spreadDots(PolygonsPointIndex start, PolygonsPointIndex end,
assert(result.size() == n_dots && "we didn't generate as many wipe locations as we asked for.");
}
Point PolygonUtils::getVertexInwardNormal(PolygonRef poly, unsigned int point_idx)
Point PolygonUtils::getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx)
{
Point p1 = poly[point_idx];
@@ -110,7 +110,7 @@ Point PolygonUtils::getVertexInwardNormal(PolygonRef poly, unsigned int point_id
break;
}
}
Point& p2 = poly[p2_idx];
const Point& p2 = poly[p2_idx];
Point off0 = turn90CCW(normal(p1 - p0, MM2INT(10.0))); // 10.0 for some precision
Point off1 = turn90CCW(normal(p2 - p1, MM2INT(10.0))); // 10.0 for some precision
@@ -119,22 +119,27 @@ Point PolygonUtils::getVertexInwardNormal(PolygonRef poly, unsigned int point_id
}
Point PolygonUtils::getBoundaryPointWithOffset(PolygonRef poly, unsigned int point_idx, int64_t offset)
Point PolygonUtils::getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset)
{
return poly[point_idx] + normal(getVertexInwardNormal(poly, point_idx), -offset);
}
Point PolygonUtils::moveInsideDiagonally(ClosestPolygonPoint point_on_boundary, int64_t inset)
{
Point p0 = point_on_boundary.poly[point_on_boundary.point_idx];
Point p1 = point_on_boundary.poly[(point_on_boundary.point_idx + 1) % point_on_boundary.poly.size()];
if (!point_on_boundary.isValid())
{
return no_point;
}
ConstPolygonRef poly = *point_on_boundary.poly;
Point p0 = poly[point_on_boundary.point_idx];
Point p1 = poly[(point_on_boundary.point_idx + 1) % poly.size()];
if (vSize2(p0 - point_on_boundary.location) < vSize2(p1 - point_on_boundary.location))
{
return point_on_boundary.location + normal(getVertexInwardNormal(point_on_boundary.poly, point_on_boundary.point_idx), inset);
return point_on_boundary.location + normal(getVertexInwardNormal(poly, point_on_boundary.point_idx), inset);
}
else
{
return point_on_boundary.location + normal(getVertexInwardNormal(point_on_boundary.poly, (point_on_boundary.point_idx + 1) % point_on_boundary.poly.size()), inset);
return point_on_boundary.location + normal(getVertexInwardNormal(poly, (point_on_boundary.point_idx + 1) % poly.size()), inset);
}
}
@@ -144,23 +149,39 @@ unsigned int PolygonUtils::moveOutside(const Polygons& polygons, Point& from, in
return moveInside(polygons, from, -distance, maxDist2);
}
ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, Point& from, const int distance, const int64_t max_dist2, const std::function<int(Point)>& penalty_function)
ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& polygons, Point& from, const int distance, const int64_t max_dist2, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
{
const ClosestPolygonPoint closest_polygon_point = findClosest(from, polygons, penalty_function);
return _moveInside2(closest_polygon_point, distance, from, max_dist2);
std::optional<ClosestPolygonPoint> closest_polygon_point;
if (loc_to_line_grid)
{
closest_polygon_point = findClose(from, *loc_to_line_polygons, *loc_to_line_grid, penalty_function);
}
if (!closest_polygon_point)
{
closest_polygon_point = findClosest(from, polygons, penalty_function);
}
return _moveInside2(*closest_polygon_point, distance, from, max_dist2);
}
ClosestPolygonPoint PolygonUtils::moveInside2(const PolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const std::function<int(Point)>& penalty_function)
ClosestPolygonPoint PolygonUtils::moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance, const int64_t max_dist2, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
{
const ClosestPolygonPoint closest_polygon_point = findClosest(from, polygon, penalty_function);
return _moveInside2(closest_polygon_point, distance, from, max_dist2);
std::optional<ClosestPolygonPoint> closest_polygon_point;
if (loc_to_line_grid)
{
closest_polygon_point = findClose(from, loc_to_line_polygons, *loc_to_line_grid, penalty_function);
}
if (!closest_polygon_point)
{
closest_polygon_point = findClosest(from, polygon, penalty_function);
}
return _moveInside2(*closest_polygon_point, distance, from, max_dist2);
}
ClosestPolygonPoint PolygonUtils::_moveInside2(const ClosestPolygonPoint& closest_polygon_point, const int distance, Point& from, const int64_t max_dist2)
{
if (closest_polygon_point.point_idx == NO_INDEX)
if (!closest_polygon_point.isValid())
{
return ClosestPolygonPoint(closest_polygon_point.poly); // stub with invalid indices to signify we haven't found any
return ClosestPolygonPoint(); // stub with invalid indices to signify we haven't found any
}
const Point v_boundary_from = from - closest_polygon_point.location;
Point result = moveInside(closest_polygon_point, distance);
@@ -182,7 +203,7 @@ ClosestPolygonPoint PolygonUtils::_moveInside2(const ClosestPolygonPoint& closes
{
if (vSize2(v_boundary_from) > max_dist2)
{
return ClosestPolygonPoint(closest_polygon_point.poly); // stub with invalid indices to signify we haven't found any
return ClosestPolygonPoint(*closest_polygon_point.poly); // stub with invalid indices to signify we haven't found any
}
else
{
@@ -204,7 +225,7 @@ unsigned int PolygonUtils::moveInside(const Polygons& polygons, Point& from, int
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
const PolygonRef poly = polygons[poly_idx];
ConstPolygonRef poly = polygons[poly_idx];
if (poly.size() < 2)
continue;
Point p0 = poly[poly.size()-2];
@@ -315,15 +336,19 @@ Point PolygonUtils::moveOutside(const ClosestPolygonPoint& cpp, const int distan
Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distance)
{
if (!cpp.isValid())
{
return no_point;
}
if (distance == 0)
{ // the point which is assumed to be on the boundary doesn't have to be moved
return cpp.location;
}
const PolygonRef poly = cpp.poly;
ConstPolygonRef poly = *cpp.poly;
unsigned int point_idx = cpp.point_idx;
const Point& on_boundary = cpp.location;
Point& p1 = poly[point_idx];
const Point& p1 = poly[point_idx];
unsigned int p2_idx;
for (p2_idx = point_idx + 1; p2_idx != point_idx; p2_idx = p2_idx + 1)
{ // find the next point different from p1
@@ -336,7 +361,7 @@ Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distanc
break;
}
}
Point& p2 = poly[p2_idx];
const Point& p2 = poly[p2_idx];
if (on_boundary == p1)
{
@@ -355,14 +380,19 @@ Point PolygonUtils::moveInside(const ClosestPolygonPoint& cpp, const int distanc
}
}
ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2, const std::function<int(Point)>& penalty_function)
ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
{
ClosestPolygonPoint closest_polygon_point = moveInside2(polygons, from, preferred_dist_inside, max_dist2, penalty_function);
if (closest_polygon_point.point_idx == NO_INDEX)
ClosestPolygonPoint closest_polygon_point = moveInside2(polygons, from, preferred_dist_inside, max_dist2, loc_to_line_polygons, loc_to_line_grid, penalty_function);
return ensureInsideOrOutside(polygons, from, closest_polygon_point, preferred_dist_inside, max_dist2, loc_to_line_polygons, loc_to_line_grid, penalty_function);
}
ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons, Point& from, ClosestPolygonPoint& closest_polygon_point, int preferred_dist_inside, int64_t max_dist2, const Polygons* loc_to_line_polygons, const LocToLineGrid* loc_to_line_grid, const std::function<int(Point)>& penalty_function)
{
if (!closest_polygon_point.isValid())
{
return ClosestPolygonPoint(polygons[0]); // we couldn't move inside
return ClosestPolygonPoint(); // we couldn't move inside
}
PolygonRef closest_poly = closest_polygon_point.poly;
ConstPolygonRef closest_poly = *closest_polygon_point.poly;
bool is_outside_boundary = closest_poly.orientation();
{
@@ -376,7 +406,7 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons
// try once more with half the preferred distance inside
int64_t max_dist2_here = std::numeric_limits<int64_t>::max(); // we already concluded we are close enough to the closest_poly
moveInside2(closest_poly, from, preferred_dist_inside / 2, max_dist2_here, penalty_function);
moveInside2(*loc_to_line_polygons, closest_poly, from, preferred_dist_inside / 2, max_dist2_here, loc_to_line_grid, penalty_function);
bool is_inside = closest_poly.inside(from) == is_outside_boundary; // inside a hole is outside the part
if (is_inside == (preferred_dist_inside > 0))
{ // we ended up on the right side of the polygon
@@ -390,10 +420,10 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons
Polygons insetted = closest_poly.offset(offset / 2); // perform less inset, because chances are (thin parts of) the polygon will disappear, given that moveInside did an overshoot
if (insetted.size() == 0)
{
return ClosestPolygonPoint(polygons[0]); // we couldn't move inside
return ClosestPolygonPoint(); // we couldn't move inside
}
ClosestPolygonPoint inside = findClosest(from, insetted, penalty_function);
if (inside.point_idx != NO_INDEX)
if (inside.isValid())
{
bool is_inside = polygons.inside(inside.location) == is_outside_boundary; // inside a hole is outside the part
if (is_inside != (preferred_dist_inside > 0))
@@ -427,16 +457,16 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons
}
}
svg.writeComment("From location");
svg.writePoint(from, false, 5, SVG::Color::GREEN);
svg.writePoint(from, true, 5, SVG::Color::GREEN);
svg.writeComment("Location computed to be inside the black polygon");
svg.writePoint(inside.location, false, 5, SVG::Color::RED);
svg.writePoint(inside.location, true, 5, SVG::Color::RED);
}
catch(...)
{
}
logError("Clipper::offset failed. See generated debug.html!\n\tBlack is original\n\tBlue is offsetted polygon\n");
#endif
return ClosestPolygonPoint(polygons[0]);
return ClosestPolygonPoint();
}
from = inside.location;
} // otherwise we just return the closest polygon point without modifying the from location
@@ -447,8 +477,12 @@ ClosestPolygonPoint PolygonUtils::ensureInsideOrOutside(const Polygons& polygons
void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result, int sample_size)
{
PolygonRef poly1 = poly1_result.poly;
PolygonRef poly2 = poly2_result.poly;
if (!poly1_result.isValid() || !poly2_result.isValid())
{
return;
}
ConstPolygonRef poly1 = *poly1_result.poly;
ConstPolygonRef poly2 = *poly2_result.poly;
if (poly1.size() == 0 || poly2.size() == 0)
{
return;
@@ -477,8 +511,12 @@ void PolygonUtils::findSmallestConnection(ClosestPolygonPoint& poly1_result, Clo
void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_result, ClosestPolygonPoint& poly2_result)
{
PolygonRef poly1 = poly1_result.poly;
PolygonRef poly2 = poly2_result.poly;
if (!poly1_result.isValid() || !poly2_result.isValid())
{
return;
}
ConstPolygonRef poly1 = *poly1_result.poly;
ConstPolygonRef poly2 = *poly2_result.poly;
if (poly1_result.point_idx < 0 || poly2_result.point_idx < 0)
{
return;
@@ -499,15 +537,15 @@ void PolygonUtils::walkToNearestSmallestConnection(ClosestPolygonPoint& poly1_re
}
}
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx)
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx)
{
ClosestPolygonPoint forth = findNearestClosest(from, polygon, start_idx, 1);
if (forth.point_idx == NO_INDEX)
if (!forth.isValid())
{
return forth; // stop computation
}
ClosestPolygonPoint back = findNearestClosest(from, polygon, start_idx, -1);
assert(back.point_idx != NO_INDEX);
assert(back.isValid());
if (vSize2(forth.location - from) < vSize2(back.location - from))
{
return forth;
@@ -518,7 +556,7 @@ ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef poly
}
}
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef polygon, int start_idx, int direction)
ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx, int direction)
{
if (polygon.size() == 0)
{
@@ -534,8 +572,8 @@ ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef poly
{
int p1_idx = (polygon.size() + direction*p + start_idx) % polygon.size();
int p2_idx = (polygon.size() + direction*(p+1) + start_idx) % polygon.size();
Point& p1 = polygon[p1_idx];
Point& p2 = polygon[p2_idx];
const Point& p1 = polygon[p1_idx];
const Point& p2 = polygon[p2_idx];
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
int64_t dist = vSize2(from - closest_here);
@@ -556,23 +594,36 @@ ClosestPolygonPoint PolygonUtils::findNearestClosest(Point from, PolygonRef poly
ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygons, const std::function<int(Point)>& penalty_function)
{
ClosestPolygonPoint none(from, -1, polygons[0], -1);
ClosestPolygonPoint none;
if (polygons.size() == 0) return none;
PolygonRef aPolygon = polygons[0];
if (aPolygon.size() == 0) return none;
Point aPoint = aPolygon[0];
ClosestPolygonPoint best(aPoint, 0, aPolygon, 0);
if (polygons.size() == 0)
{
return none;
}
ConstPolygonRef any_polygon = polygons[0];
unsigned int any_poly_idx;
for (any_poly_idx = 0; any_poly_idx < polygons.size(); any_poly_idx++)
{ // find first point in all polygons
if (polygons[any_poly_idx].size() > 0)
{
any_polygon = polygons[any_poly_idx];
break;
}
}
if (any_polygon.size() == 0)
{
return none;
}
ClosestPolygonPoint best(any_polygon[0], 0, any_polygon, any_poly_idx);
int64_t closestDist2_score = vSize2(from - best.location) + penalty_function(best.location);
for (unsigned int ply = 0; ply < polygons.size(); ply++)
{
const PolygonRef poly = polygons[ply];
ConstPolygonRef poly = polygons[ply];
if (poly.size() == 0) continue;
ClosestPolygonPoint closest_here = findClosest(from, poly, penalty_function);
if (closest_here.point_idx == NO_INDEX)
if (!closest_here.isValid())
{
continue;
}
@@ -588,7 +639,7 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const Polygons& polygo
return best;
}
ClosestPolygonPoint PolygonUtils::findClosest(Point from, const PolygonRef polygon, const std::function<int(Point)>& penalty_function)
ClosestPolygonPoint PolygonUtils::findClosest(Point from, ConstPolygonRef polygon, const std::function<int(Point)>& penalty_function)
{
if (polygon.size() == 0)
{
@@ -602,11 +653,11 @@ ClosestPolygonPoint PolygonUtils::findClosest(Point from, const PolygonRef polyg
//
for (unsigned int p = 0; p<polygon.size(); p++)
{
Point& p1 = polygon[p];
const Point& p1 = polygon[p];
unsigned int p2_idx = p+1;
if (p2_idx >= polygon.size()) p2_idx = 0;
Point& p2 = polygon[p2_idx];
const Point& p2 = polygon[p2_idx];
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
@@ -627,7 +678,7 @@ PolygonsPointIndex PolygonUtils::findNearestVert(const Point from, const Polygon
PolygonsPointIndex closest_vert;
for (unsigned int poly_idx = 0; poly_idx < polys.size(); poly_idx++)
{
const PolygonRef poly = polys[poly_idx];
ConstPolygonRef poly = polys[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
int64_t dist2 = vSize2(poly[point_idx] - from);
@@ -641,7 +692,7 @@ PolygonsPointIndex PolygonUtils::findNearestVert(const Point from, const Polygon
return closest_vert;
}
unsigned int PolygonUtils::findNearestVert(const Point from, const PolygonRef poly)
unsigned int PolygonUtils::findNearestVert(const Point from, ConstPolygonRef poly)
{
int64_t best_dist2 = std::numeric_limits<int64_t>::max();
unsigned int closest_vert_idx = -1;
@@ -658,7 +709,7 @@ unsigned int PolygonUtils::findNearestVert(const Point from, const PolygonRef po
}
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* PolygonUtils::createLocToLineGrid(const Polygons& polygons, int square_size)
LocToLineGrid* PolygonUtils::createLocToLineGrid(const Polygons& polygons, int square_size)
{
unsigned int n_points = 0;
for (const auto& poly : polygons)
@@ -666,11 +717,11 @@ SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* PolygonUti
n_points += poly.size();
}
SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* ret = new SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>(square_size, n_points);
LocToLineGrid* ret = new LocToLineGrid(square_size, n_points);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
const PolygonRef poly = polygons[poly_idx];
ConstPolygonRef poly = polygons[poly_idx];
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
ret->insert(PolygonsPointIndex(&polygons, poly_idx, point_idx));
@@ -689,7 +740,7 @@ SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* PolygonUti
*/
std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
Point from, const Polygons& polygons,
const SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& loc_to_line,
const LocToLineGrid& loc_to_line,
const std::function<int(Point)>& penalty_function)
{
std::vector<PolygonsPointIndex> near_lines =
@@ -701,9 +752,9 @@ std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
PolygonsPointIndex best_point_poly_idx(nullptr, NO_INDEX, NO_INDEX);
for (PolygonsPointIndex& point_poly_index : near_lines)
{
const PolygonRef poly = polygons[point_poly_index.poly_idx];
Point& p1 = poly[point_poly_index.point_idx];
Point& p2 = poly[(point_poly_index.point_idx + 1) % poly.size()];
ConstPolygonRef poly = polygons[point_poly_index.poly_idx];
const Point& p1 = poly[point_poly_index.point_idx];
const Point& p2 = poly[(point_poly_index.point_idx + 1) % poly.size()];
Point closest_here = LinearAlg2D::getClosestOnLineSegment(from, p1 ,p2);
int64_t dist2_score = vSize2(from - closest_here) + penalty_function(closest_here);
@@ -727,8 +778,8 @@ std::optional<ClosestPolygonPoint> PolygonUtils::findClose(
std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> PolygonUtils::findClose(
const PolygonRef from, const Polygons& destination,
const SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& destination_loc_to_line,
ConstPolygonRef from, const Polygons& destination,
const LocToLineGrid& destination_loc_to_line,
const std::function<int(Point)>& penalty_function)
{
std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> ret;
@@ -766,7 +817,7 @@ std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> PolygonUtils::f
bool PolygonUtils::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, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result)
{
Point prev_poly_point = poly[(start_idx + poly_start_idx) % poly.size()];
@@ -774,7 +825,7 @@ bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, const Poly
for (unsigned int prev_idx = start_idx; prev_idx < poly.size(); prev_idx++)
{
int next_idx = (prev_idx + 1 + poly_start_idx) % poly.size(); // last checked segment is between last point in poly and poly[0]...
Point& next_poly_point = poly[next_idx];
const Point& next_poly_point = poly[next_idx];
if ( !shorterThen(next_poly_point - from, dist) )
{
/*
@@ -842,34 +893,61 @@ bool PolygonUtils::getNextPointWithDistance(Point from, int64_t dist, const Poly
}
bool PolygonUtils::polygonCollidesWithLineSegment(const Point from, const Point to, const LocToLineGrid& loc_to_line, PolygonsPointIndex* collision_result)
{
bool ret = false;
Point diff = to - from;
if (vSize2(diff) < 2)
{ // transformation matrix would fail
return false;
}
bool PolygonUtils::polygonCollidesWithlineSegment(const PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
PointMatrix transformation_matrix = PointMatrix(diff);
Point transformed_from = transformation_matrix.apply(from);
Point transformed_to = transformation_matrix.apply(to);
PolygonsPointIndex result;
std::function<bool (const PolygonsPointIndex&)> process_elem_func =
[transformed_from, transformed_to, &transformation_matrix, &result, &ret]
(const PolygonsPointIndex& line_start)
{
Point p0 = transformation_matrix.apply(line_start.p());
Point p1 = transformation_matrix.apply(line_start.next().p());
if (LinearAlg2D::lineSegmentsCollide(transformed_from, transformed_to, p0, p1))
{
result = line_start;
ret = true;
return false;
}
return true;
};
loc_to_line.processLine(std::make_pair(from, to), process_elem_func);
if (collision_result)
{
*collision_result = result;
}
return ret;
}
bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& transformed_startPoint, const 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))
if (LinearAlg2D::lineSegmentsCollide(transformed_startPoint, transformed_endPoint, p0, p1))
{
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;
return true;
}
p0 = p1;
}
return false;
}
bool PolygonUtils::polygonCollidesWithlineSegment(const PolygonRef poly, Point& startPoint, Point& endPoint)
bool PolygonUtils::polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& startPoint, const Point& endPoint)
{
Point diff = endPoint - startPoint;
@@ -877,15 +955,15 @@ bool PolygonUtils::polygonCollidesWithlineSegment(const PolygonRef poly, Point&
Point transformed_startPoint = transformation_matrix.apply(startPoint);
Point transformed_endPoint = transformation_matrix.apply(endPoint);
return PolygonUtils::polygonCollidesWithlineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix);
return PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix);
}
bool PolygonUtils::polygonCollidesWithlineSegment(const Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix)
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix)
{
for (const PolygonRef poly : const_cast<Polygons&>(polys))
for (ConstPolygonRef poly : polys)
{
if (poly.size() == 0) { continue; }
if (PolygonUtils::polygonCollidesWithlineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix))
if (PolygonUtils::polygonCollidesWithLineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix))
{
return true;
}
@@ -895,7 +973,7 @@ bool PolygonUtils::polygonCollidesWithlineSegment(const Polygons& polys, Point&
}
bool PolygonUtils::polygonCollidesWithlineSegment(const Polygons& polys, Point& startPoint, Point& endPoint)
bool PolygonUtils::polygonCollidesWithLineSegment(const Polygons& polys, const Point& startPoint, const Point& endPoint)
{
Point diff = endPoint - startPoint;
@@ -903,7 +981,7 @@ bool PolygonUtils::polygonCollidesWithlineSegment(const Polygons& polys, Point&
Point transformed_startPoint = transformation_matrix.apply(startPoint);
Point transformed_endPoint = transformation_matrix.apply(endPoint);
return polygonCollidesWithlineSegment(polys, transformed_startPoint, transformed_endPoint, transformation_matrix);
return polygonCollidesWithLineSegment(polys, transformed_startPoint, transformed_endPoint, transformation_matrix);
}
+89 -22
Ver Arquivo
@@ -20,16 +20,21 @@ namespace cura
struct ClosestPolygonPoint
{
Point location; //!< Result location
PolygonRef poly; //!< Polygon in which the result was found
std::optional<ConstPolygonRef> poly; //!< Polygon in which the result was found (or none if no result was found)
unsigned int poly_idx; //!< The index of the polygon in some Polygons where ClosestPolygonPoint::poly can be found
unsigned int point_idx; //!< Index to the first point in the polygon of the line segment on which the result was found
ClosestPolygonPoint(Point p, int pos, PolygonRef poly) : location(p), poly(poly), poly_idx(NO_INDEX), point_idx(pos) {};
ClosestPolygonPoint(Point p, int pos, PolygonRef poly, int poly_idx) : location(p), poly(poly), poly_idx(poly_idx), point_idx(pos) {};
ClosestPolygonPoint(PolygonRef poly) : poly(poly), poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly) : location(p), poly(true, poly), poly_idx(NO_INDEX), point_idx(pos) {};
ClosestPolygonPoint(Point p, int pos, ConstPolygonRef poly, int poly_idx) : location(p), poly(true, poly), poly_idx(poly_idx), point_idx(pos) {};
ClosestPolygonPoint(ConstPolygonRef poly) : poly(true, poly), poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
ClosestPolygonPoint() : poly_idx(NO_INDEX), point_idx(NO_INDEX) {};
Point p() const
{ // conformity with other classes
return location;
}
bool isValid() const
{
return point_idx != NO_INDEX;
}
};
/*!
@@ -48,7 +53,7 @@ struct PolygonsPointIndexSegmentLocator
{
std::pair<Point, Point> operator()(const PolygonsPointIndex& val) const
{
PolygonRef poly = (*val.polygons)[val.poly_idx];
ConstPolygonRef poly = (*val.polygons)[val.poly_idx];
Point start = poly[val.point_idx];
unsigned int next_point_idx = (val.point_idx + 1) % poly.size();
Point end = poly[next_point_idx];
@@ -56,6 +61,8 @@ struct PolygonsPointIndexSegmentLocator
}
};
typedef SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator> LocToLineGrid;
class PolygonUtils
{
public:
@@ -97,7 +104,7 @@ public:
* \param poly The polygon.
* \param point_idx The index of the point in the polygon.
*/
static Point getVertexInwardNormal(PolygonRef poly, unsigned int point_idx);
static Point getVertexInwardNormal(ConstPolygonRef poly, unsigned int point_idx);
/*!
* Get a point from the \p poly with a given \p offset.
@@ -107,7 +114,7 @@ public:
* \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);
static Point getBoundaryPointWithOffset(ConstPolygonRef poly, unsigned int point_idx, int64_t offset);
/*!
* Move a point away from the boundary by looking at the boundary normal of the nearest vert.
@@ -137,13 +144,20 @@ public:
* 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.
*
* \warning If \p loc_to_line_grid is used, it's best to have all and only \p polygons in there.
* If \p from is not closest to \p polygons this function may
* return a ClosestPolygonPoint on a polygon in \p loc_to_line_grid which is not in \p polygons.
*
* \param polygons The polygons onto which to move the point
* \param from[in,out] The point to move.
* \param distance The distance by which to move the point.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param loc_to_line_polygons All polygons with which the \p loc_to_line_grid has been created.
* \param loc_to_line_grid A SparseGrid mapping locations to line segments of \p polygons
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return The point on the polygon closest to \p from
*/
static ClosestPolygonPoint moveInside2(const Polygons& polygons, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const std::function<int(Point)>& penalty_function = no_penalty_function);
static ClosestPolygonPoint moveInside2(const Polygons& polygons, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* Moves the point \p from onto the nearest segment of \p polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance.
@@ -151,14 +165,20 @@ public:
* 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.
*
* \warning When a \p loc_to_line is given this function only considers nearby elements.
* Even when the penalty function favours elements farther away.
* Also using the \p loc_to_line_grid automatically considers \p all_polygons
*
* \param loc_to_line_polygons All polygons which are present in the \p loc_to_line_grid of which \p polygon is an element
* \param polygon The polygon onto which to move the point
* \param from[in,out] The point to move.
* \param distance The distance by which to move the point.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param loc_to_line_grid A SparseGrid mapping locations to line segments of \p polygon
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return The point on the polygon closest to \p from
*/
static ClosestPolygonPoint moveInside2(const PolygonRef polygon, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const std::function<int(Point)>& penalty_function = no_penalty_function);
static ClosestPolygonPoint moveInside2(const Polygons& loc_to_line_polygons, ConstPolygonRef polygon, Point& from, const int distance = 0, const int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* The opposite of moveInside.
@@ -210,14 +230,45 @@ public:
* but it might still be the case that we end up outside:
* when the closest point on the boundary is very close to another polygon
*
* \warning When using a \p loc_to_line_grid which contains more polygons than just \p polygons,
* the results is only correct if \p from is already closest to \p polygons, rather than other polygons in the \p loc_to_line_grid.
*
* \param polygons The polygons onto which to move the point
* \param from[in,out] The point to move.
* \param preferred_dist_inside The preferred distance from the boundary to the point
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param loc_to_line_polygons The original polygons with which the \p loc_to_line_grid has been created
* \param loc_to_line_grid A SparseGrid mapping locations to line segments of \p polygons
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return The point on the polygon closest to \p from
*/
static ClosestPolygonPoint ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const std::function<int(Point)>& penalty_function = no_penalty_function);
static ClosestPolygonPoint ensureInsideOrOutside(const Polygons& polygons, Point& from, int preferred_dist_inside, int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* 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.
*
* \warning May give false positives.
* Some checking is done to make sure we end up inside the polygon,
* but it might still be the case that we end up outside:
* when the closest point on the boundary is very close to another polygon
*
* \warning When using a \p loc_to_line_grid which contains more polygons than just \p polygons,
* the results is only correct if \p from is already closest to \p polygons, rather than other polygons in the \p loc_to_line_grid.
*
* \param polygons The polygons onto which to move the point
* \param from[in,out] The point to move.
* \param closest_polygon_point The point on \p polygons closest to \p from
* \param preferred_dist_inside The preferred distance from the boundary to the point
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param loc_to_line_polygons The original polygons with which the \p loc_to_line_grid has been created
* \param loc_to_line_grid A SparseGrid mapping locations to line segments of \p polygons
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return The point on the polygon closest to \p from
*/
static ClosestPolygonPoint ensureInsideOrOutside(const Polygons& polygons, Point& from, ClosestPolygonPoint& closest_polygon_point, int preferred_dist_inside, int64_t max_dist2 = std::numeric_limits<int64_t>::max(), const Polygons* loc_to_line_polygons = nullptr, const LocToLineGrid* loc_to_line_grid = nullptr, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* Find the two points in two polygons with the smallest distance.
@@ -247,7 +298,7 @@ public:
* \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, const PolygonRef polygon, int start_idx);
static ClosestPolygonPoint findNearestClosest(Point from, ConstPolygonRef polygon, int start_idx);
/*!
* Find the nearest closest point on a polygon from a given index walking in one direction along the polygon.
@@ -258,7 +309,7 @@ public:
* \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(const Point from, const PolygonRef polygon, int start_idx, int direction);
static ClosestPolygonPoint findNearestClosest(const Point from, ConstPolygonRef polygon, int start_idx, int direction);
/*!
* Find the point closest to \p from in all polygons in \p polygons.
@@ -276,7 +327,7 @@ public:
*
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
*/
static ClosestPolygonPoint findClosest(Point from, const PolygonRef polygon, const std::function<int(Point)>& penalty_function = no_penalty_function);
static ClosestPolygonPoint findClosest(Point from, ConstPolygonRef polygon, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* Find the nearest vertex to \p from in \p polys
@@ -292,7 +343,7 @@ public:
* \param poly The polygon in which to search
* \return The index to the nearest vertex on the polygon
*/
static unsigned int findNearestVert(const Point from, const PolygonRef poly);
static unsigned int findNearestVert(const Point from, ConstPolygonRef poly);
/*!
* Create a SparsePointGridInclusive mapping from locations to line segments occurring in the \p polygons
@@ -303,7 +354,7 @@ public:
* \param square_size The cell size used to bundle line segments (also used to chop up lines so that multiple cells contain the same long line)
* \return A bucket grid mapping spatial locations to poly-point indices into \p polygons
*/
static SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>* createLocToLineGrid(const Polygons& polygons, int square_size);
static LocToLineGrid* createLocToLineGrid(const Polygons& polygons, int square_size);
/*!
* Find the line segment closest to a given point \p from within a cell-block of a size defined in the SparsePointGridInclusive \p loc_to_line
@@ -317,7 +368,7 @@ public:
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return The nearest point on the polygon if the polygon was within a distance equal to the cell_size of the SparsePointGridInclusive
*/
static std::optional<ClosestPolygonPoint> findClose(Point from, const Polygons& polygons, const SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
static std::optional<ClosestPolygonPoint> findClose(Point from, const Polygons& polygons, const LocToLineGrid& loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* Find the line segment closest to any point on \p from within cell-blocks of a size defined in the SparsePointGridInclusive \p destination_loc_to_line
@@ -331,7 +382,23 @@ public:
* \param penalty_function A function returning a penalty term on the squared distance score of a candidate point.
* \return A collection of near crossing from the \p from polygon to the \p destination polygon. Each element in the sollection is a pair with as first a cpp in the \p from polygon and as second a cpp in the \p destination polygon.
*/
static std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findClose(const PolygonRef from, const Polygons& destination, const SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator>& destination_loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
static std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> findClose(ConstPolygonRef from, const Polygons& destination, const LocToLineGrid& destination_loc_to_line, const std::function<int(Point)>& penalty_function = no_penalty_function);
/*!
* Checks whether a given line segment collides with polygons as given in a loc_to_line grid.
*
* 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[in] from The start point
* \param[in] to The end point
* \param[in] loc_to_line A SparsePointGridInclusive mapping locations to starting vertices of line segmetns of the \p polygons
* \param[out] collision_result (optional) The polygons segment intersecting with the line segment
* \return whether the line segment collides with the boundary of the polygons
*/
static bool polygonCollidesWithLineSegment(const Point from, const Point to, const LocToLineGrid& loc_to_line, PolygonsPointIndex* collision_result = nullptr);
/*!
* 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.
@@ -342,7 +409,7 @@ public:
* \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);
static bool getNextPointWithDistance(Point from, int64_t dist, ConstPolygonRef poly, int start_idx, int poly_start_idx, GivenDistPoint& result);
@@ -366,7 +433,7 @@ public:
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(const PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
static bool polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix);
/*!
* Checks whether a given line segment collides with a given polygon(s).
@@ -382,7 +449,7 @@ public:
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(const PolygonRef poly, Point& startPoint, Point& endPoint);
static bool polygonCollidesWithLineSegment(ConstPolygonRef poly, const Point& startPoint, const Point& endPoint);
/*!
* Checks whether a given line segment collides with a given polygon(s).
@@ -404,7 +471,7 @@ public:
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(const Polygons& polys, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix);
static bool polygonCollidesWithLineSegment(const Polygons& polys, const Point& transformed_startPoint, const Point& transformed_endPoint, PointMatrix transformation_matrix);
/*!
* Checks whether a given line segment collides with a given polygon(s).
@@ -420,7 +487,7 @@ public:
* \return whether the line segment collides with the boundary of the
* polygon(s)
*/
static bool polygonCollidesWithlineSegment(const Polygons& polys, Point& startPoint, Point& endPoint);
static bool polygonCollidesWithLineSegment(const Polygons& polys, const Point& startPoint, const Point& endPoint);
private:
/*!
+35 -6
Ver Arquivo
@@ -1,3 +1,6 @@
//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_STRING_H
#define UTILS_STRING_H
@@ -5,6 +8,10 @@
#include <cstdio> // sprintf
#include <sstream> // ostringstream
#include <cinttypes> // PRId64
#include "logoutput.h"
namespace cura
{
@@ -29,8 +36,19 @@ static inline int stringcasecompare(const char* a, const char* b)
*/
static inline void writeInt2mm(const int64_t coord, std::ostream& ss)
{
char buffer[24];
int char_count = sprintf(buffer, "%ld", coord); // convert int to string
constexpr size_t buffer_size = 24;
char buffer[buffer_size];
int char_count = sprintf(buffer, "%d", int(coord)); // convert int to string
#ifdef DEBUG
if (char_count + 1 >= int(buffer_size)) // + 1 for the null character
{
logError("Cannot write %ld to buffer of size %i", coord, buffer_size);
}
if (char_count < 0)
{
logError("Encoding error while writing %ld", coord);
}
#endif // DEBUG
int end_pos = char_count; // the first character not to write any more
int trailing_zeros = 1;
while (trailing_zeros < 4 && buffer[char_count - trailing_zeros] == '0')
@@ -53,7 +71,7 @@ static inline void writeInt2mm(const int64_t coord, std::ostream& ss)
ss << '-';
start = 1;
}
ss << '.';
ss << "0.";
for (int nulls = char_count - start; nulls < 3; nulls++)
{ // fill up to 3 decimals with zeros
ss << '0';
@@ -103,10 +121,21 @@ struct MMtoStream
*/
static inline void writeDoubleToStream(const unsigned int precision, const double coord, std::ostream& ss)
{
char format[5] = "%.xf"; // write a float with [x] digits after the dot
char format[5] = "%.xF"; // write a float with [x] digits after the dot
format[2] = '0' + precision; // set [x]
char buffer[24];
int char_count = snprintf(buffer, 24, format, coord);
constexpr size_t buffer_size = 400;
char buffer[buffer_size];
int char_count = sprintf(buffer, format, coord);
#ifdef DEBUG
if (char_count + 1 >= int(buffer_size)) // + 1 for the null character
{
logError("Cannot write %f to buffer of size %i", coord, buffer_size);
}
if (char_count < 0)
{
logError("Encoding error while writing %f", coord);
}
#endif // DEBUG
if (char_count <= 0)
{
return;
+1 -1
Ver Arquivo
@@ -18,7 +18,7 @@ WallOverlapComputation::WallOverlapComputation(Polygons& polygons, int line_widt
}
float WallOverlapComputation::getFlow(Point& from, Point& to)
float WallOverlapComputation::getFlow(const Point& from, const Point& to)
{
using Point2LinkIt = PolygonProximityLinker::Point2Link::iterator;

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