Comparar commits

...

1333 Commits

Autor SHA1 Mensagem Data
Tim Kuipers c0495edf48 fix: turn off fans if fans are disabled (CURA-2603) 2016-10-11 16:42:56 +02:00
Tim Kuipers 1574292945 fix: forgot to write retractions with given precision (CURA-2619) 2016-10-11 16:14:01 +02:00
Tim Kuipers ed3f9f107c fix: only wipe on prime tower when enabled (CURA-2605) 2016-10-11 15:55:05 +02:00
Tim Kuipers 18bf08a1b5 fix: retrieve adhesion_type globally (CURA-2605) 2016-10-11 15:00:51 +02:00
Tim Kuipers f30c309249 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-10-10 16:08:31 +02:00
Tim Kuipers af3536b307 Merge remote-tracking branch 'remotes/origin/feature_remove_trailing_zeros' 2016-10-10 16:08:22 +02:00
Tim Kuipers 8aed4c9139 Merge pull request #399 from Renha/master
Extra dollars removed from readme.
2016-10-10 15:48:30 +02:00
Renha 03fa1c2d37 Extra dollars removed from readme.
There are no need to write $s twice.
2016-10-10 16:40:49 +03:00
Tim Kuipers 7582100fba fix: force prime tower wipe as extrusion move so that it's not z hopped (CURA-2522) 2016-10-06 13:42:02 +02:00
Tim Kuipers edd1d5c8dd fix: possible infinite loop in support interface generation (CURA-2545) 2016-10-05 17:35:16 +02:00
Tim Kuipers 5971604805 cleanup: less boolean trappy (CURA-2525) 2016-10-05 17:20:48 +02:00
Tim Kuipers a8ea6511bd fix: Polygon::smooth_outward can now remove whole hole polygons (CURA-2526) 2016-10-05 14:28:39 +02:00
Tim Kuipers 5c1083db0b fix: dont remove support for brim inside empty holes (CURA-2525) 2016-10-04 15:43:04 +02:00
Tim Kuipers f9b917f9d1 feat: Polygons::getEmptyHoles (CURA-2525) 2016-10-04 15:42:40 +02:00
Tim Kuipers eb065c1a07 fix: avoid obstacles and retract when moving to prime tower (CURA-2522) 2016-10-04 14:16:06 +02:00
Jack Ha 29978b8654 Fix testsuite by adding mock resolveOrValue 2016-10-04 13:23:21 +02:00
Tim Kuipers 9563aeb1c0 fix: prime tower back to LINEs because of CURA-2511 (CURA-2510) 2016-10-03 17:10:46 +02:00
Ghostkeeper fb7cd6a572 Fix indenting
Contributes to issue CURA-1962.
2016-09-30 16:26:39 +02:00
Tim Kuipers 3f02cf1f33 Merge branch 'feature_rotation_matrix' of github.com:Ultimaker/CuraEngine into feature_rotation_matrix 2016-09-30 16:21:14 +02:00
Tim Kuipers 7bdd5690f7 fix: mesh_rotation renamed to mesh_rotation_matrix and warning emitted if matrix is incorrect (CURA-2416) 2016-09-30 16:20:45 +02:00
Tim Kuipers 1de4107ea6 refactor: also use my own << operator for writing integers to stream (CURA-1962) 2016-09-30 15:30:59 +02:00
Tim Kuipers 6306321007 lil fixes (CURA-1962) 2016-09-30 15:26:55 +02:00
Tim Kuipers 7281e2187e feat: faster double to stream without trailing zeros (CURA-1962) 2016-09-30 15:26:14 +02:00
Ghostkeeper 5f99281478 Fix setting name mesh_rotation_matrix
This was not congruent with the setting name in command_line_settings.def.json

Contributes to issue CURA-2416.
2016-09-30 14:00:17 +02:00
Tim Kuipers 3d3c12a9d2 feat: get transformation matrix from settings (CURA-2416) 2016-09-30 12:29:08 +02:00
Ghostkeeper 739aedf8ad Merge branch 'feature_wipe_on_prime_tower' 2016-09-29 20:46:00 +02:00
Tim Kuipers 1bb79f850a fix: no more wipe after last printed prime tower; no z hop for wipe; wipe slower (CURA-2420) 2016-09-29 18:30:25 +02:00
Tim Kuipers 3d77a426dd fix: only wipe on single closest line of prime tower (CURA-2420)
otherwise the move from the switch location to the wipe tower already wiped the nozzle on the prime tower, because it moved through the prime tower toward the wipe location
2016-09-29 15:03:41 +02:00
Arjen Hiemstra 3aa3e4ef92 Merge branch 'feature_export_speedup'
* feature_export_speedup:
  lil writeInt2mm improvements (CURA-2472)
  fix: output integers directly to stream rather than converting to double first (CURA-2472)
2016-09-29 13:32:34 +02:00
Tim Kuipers eb5231e7a8 lil fix: forgotten return statement in hopefully unreachable code (CURA-2420) 2016-09-29 12:16:53 +02:00
Tim Kuipers 17a9764181 fix: retrieve support_enable per mesh (CURA-2492) 2016-09-29 11:57:09 +02:00
Tim Kuipers 86e7dbf2b1 lil writeInt2mm improvements (CURA-2472) 2016-09-29 10:00:36 +02:00
Tim Kuipers ccaf8538d8 fix: multVolumes: merge overlap better with model (CURA-2427)
when models where directly adjacent, the overlap area wouldn't be merged with the model at all places because of rounding errors
2016-09-29 09:46:44 +02:00
Tim Kuipers a248662077 lil doc (CURA-2420) 2016-09-28 18:53:56 +02:00
Tim Kuipers fc28698479 fix: tests for PolygonUtils::spreadDots (CURA-2420) 2016-09-28 18:41:59 +02:00
Tim Kuipers 263bb3099b bugfixes PolygonUtils::spreadDots, segmentLength and PolygonsPointIndex (CURA-2420) 2016-09-28 18:41:30 +02:00
Tim Kuipers 25b83c9dda factored out PolygonUtils::spreadDots from PrimeTower::generateWipeLocations (CURA-2420) 2016-09-28 17:54:54 +02:00
Tim Kuipers 7025e5cea4 indent only (CURA-2420) 2016-09-28 17:24:57 +02:00
Tim Kuipers 183fc38ca9 refactor PrimeTower::generateWipeLocations (CURA-2420) 2016-09-28 17:24:38 +02:00
Tim Kuipers f4461ca41a feat: simple fuctions to find the nearest vert in polygons (CURA-2420) 2016-09-28 17:20:12 +02:00
Tim Kuipers 5ba9026632 factored PolygonsPointIndex out of PolygonUtils (CURA-2420) 2016-09-28 17:19:29 +02:00
Tim Kuipers 01ec0b847f lil (CURA-2420) 2016-09-28 16:40:04 +02:00
Tim Kuipers 9fe8796844 fix: generateMultipleVolumesOverlap wasn't applied (CURA-2427) 2016-09-28 15:47:11 +02:00
Tim Kuipers c554ec4178 fix: register horizontal expansion in the AABB of a mesh (CURA-2427) 2016-09-28 15:16:05 +02:00
Tim Kuipers dce8a4629f fix: output integers directly to stream rather than converting to double first (CURA-2472) 2016-09-28 11:32:25 +02:00
Tim Kuipers 10af83137b prime tower: changed from lines to zigzag infill (CURA-2420) 2016-09-27 12:05:14 +02:00
Tim Kuipers d45d00b441 fix: wipe tower overlapped with brim (CURA-2420) 2016-09-27 11:56:31 +02:00
Tim Kuipers b2abcbd7b4 refactor: move last_prime_tower_poly_printed inside PrimeTower class (CURA-2420) 2016-09-27 11:29:50 +02:00
Tim Kuipers 9c15d18cfb unfeat: remove setting prime_tower_dir_outward (CURA-2420) 2016-09-27 11:22:59 +02:00
Tim Kuipers 7122335a55 prime tower: cleanup, const correctness and documentation (CURA-2420) 2016-09-27 11:21:01 +02:00
Tim Kuipers b5b05c1894 lil cleanup (CURA-1062) 2016-09-22 20:51:16 +02:00
Tim Kuipers 8d6dd5ae8b fix: dont draw skirt through ooze/draft shield (CURA-1062) 2016-09-22 20:08:18 +02:00
Tim Kuipers 433c90617e fix: always wipe nozzle diagonally on wipe tower (CURA-2420) 2016-09-22 16:50:24 +02:00
Tim Kuipers 2980b20346 cleanup & documentation of primetower (CURA-2420) 2016-09-22 16:06:53 +02:00
Tim Kuipers 5993b5f606 oops committed debug code (CURA-2420) 2016-09-22 15:45:50 +02:00
Tim Kuipers f7940498f0 feat: wipe nozzle before starting prime tower (CURA-2420)
wipe at different locations each layer, so that there won't be a buildup of material on one place, which might get the tower knocked over
2016-09-22 15:42:58 +02:00
Tim Kuipers 03acc7629c made some polygon functions const as they should be (CURA-2420) 2016-09-22 15:41:46 +02:00
Ghostkeeper f903d438f2 Merge branch 'bugfixes_adhesion' 2016-09-22 13:53:47 +02:00
Tim Kuipers 04dc8ac68b lil auxiliary functions (CURA-2420) 2016-09-22 13:34:53 +02:00
Tim Kuipers 1a2ba63c74 lil cleanup (CURA-2420) 2016-09-21 18:03:11 +02:00
Tim Kuipers d28d86c208 doc: better documentation of limit_to_extruder property in the proto file (CURA-2308) 2016-09-21 17:59:55 +02:00
Tim Kuipers afd91bfd2e cleanup: remove old concentric and spiral prime tower (CURA-2420)
I think these prime towers only printed half of the space, assuming the other extruder would print the other half
2016-09-21 17:18:21 +02:00
Tim Kuipers 4cd55f0907 refactor: move wipePoint inside wipeTower (CURA-2420) 2016-09-21 16:55:56 +02:00
Tim Kuipers f20320031f fix: precision was inconsistent (CURA-1955) 2016-09-21 16:07:38 +02:00
Tim Kuipers 44f7b92958 some cleanup and const correctness (CURA-1062) 2016-09-21 10:45:11 +02:00
Tim Kuipers 0b4eda9592 fix: brim was slow for high poly dual extrusion models (CURA-1062, CURA-2422) 2016-09-20 18:12:01 +02:00
Tim Kuipers c674a84f38 Merge branch 'master' into bugfixes_adhesion 2016-09-20 17:31:55 +02:00
Tim Kuipers 2660bd1bc1 Merge branch 'totalretribution-reverse_inset_order' 2016-09-20 13:43:31 +02:00
Tim Kuipers 618660c16f Merge branch 'reverse_inset_order' of https://github.com/totalretribution/CuraEngine into totalretribution-reverse_inset_order 2016-09-20 13:35:25 +02:00
Tim Kuipers 252a797f36 fix: shield-brim got odd number of brim lines, causing gaps (CURA-1062) 2016-09-20 11:37:26 +02:00
Tim Kuipers 2db70b4b5c fix: put helper parts of initial layer in separate layer so as to not apply the layer_0_z_overlap (CURA-1062) 2016-09-20 11:13:09 +02:00
Tim Kuipers 379a3d54f1 fix: brim for ooze/draft shield (CURA-1062) 2016-09-20 09:57:46 +02:00
Tim Kuipers 92fca17411 fix: always even number of brim lines between support and model (CURA-1062) 2016-09-20 09:57:23 +02:00
Ghostkeeper 87637c9ef7 Fix comparison between long and int64_t
On some machines, long isn't 64-bit.

Contributes to issue CURA-2296.
2016-09-20 09:18:44 +02:00
Tim Kuipers d2bbe41ff1 refactor: generateSkirtBrim ==> SkirtBrim::generate CURA-1062) 2016-09-19 18:34:24 +02:00
Tim Kuipers a70fdfb917 feat: introduce layers with helper objects in the airgap (CURA-1062) 2016-09-19 18:01:35 +02:00
Tim Kuipers ceff626d87 raft auciliary functions (CURA-1062) 2016-09-19 17:46:33 +02:00
Tim Kuipers 53c4552de7 removed unused CommandSocket::sendLayerInfo (CURA-1062) 2016-09-19 17:39:08 +02:00
Ghostkeeper a576dd8929 Fix literal long on 32-bit
On some machines, this literal is only 32 bits wide.

I've now made it always 64 bits wide.

Contributes to issue CURA-2296.
2016-09-19 17:11:24 +02:00
Tim Kuipers dcec2d2584 refactor: generateRaft ==> Raft::generate (CURA-1062) 2016-09-19 16:31:58 +02:00
Tim Kuipers 6c2d7b72cb cleanup: removed unused param (CURA-1062) 2016-09-19 16:03:35 +02:00
Tim Kuipers 228b13c96c fix: raft base came half the line width too far (CURA-1062) 2016-09-19 15:54:40 +02:00
Tim Kuipers 4607d0a8ad fix: raft goes inward from draft/ooze shield and starts half a line width outward (CURA-1062) 2016-09-19 15:50:09 +02:00
Tim Kuipers cec7d2bef4 fix: make ooze shield offset round (CURA-1062)
so that the ooze shield can never fall outside of the build plate
2016-09-19 14:49:57 +02:00
Tim Kuipers 1eee21a16c fix: removeSmallAreas works on floats (CURA-1062) 2016-09-19 14:48:49 +02:00
Ghostkeeper e52beb8239 Fix warning of unused variable
It was used only in an assert, so if you're skipping asserts, the variable was unused. I've now cast it into the void to make it 'used'.
2016-09-19 14:40:37 +02:00
Ghostkeeper 72f09d3fa0 Merge bugfix_jagged_support
Conflicts:
	src/gcodeExport.cpp
	src/support.cpp
	src/utils/LinearAlg2D.cpp
	src/utils/linearAlg2D.h
	src/utils/polygon.cpp
2016-09-19 14:34:17 +02:00
Tim Kuipers 9ba3e301d6 fix: don't place ooze shield through thin areas; don't diagonalize oozze shield for too hgih angle; remove small areas afterwards (CURA-1062) 2016-09-19 14:25:34 +02:00
Ghostkeeper 4bea7d8ba2 Use unsigned long string interpreter
Sadly, stoui doesn't exist in C++11. We do have stoul though, which comes closer to what we intend and is still more powerful than we need.

Contributes to issue CURA-2314.
2016-09-16 17:03:08 +02:00
Ghostkeeper b03a7bd9ee Interpret cool_fan_full_layer as a layer number
Because it is.

Contributes to issue CURA-2314.
2016-09-16 16:43:06 +02:00
Ghostkeeper 1f5071c739 Add setting interpretation as layer number
Layer numbers are one-based and not negative. This translates it to zero-based.

Contributes to issue CURA-2314.
2016-09-16 16:41:44 +02:00
TotalRetribution a165265c0a Simplified outer_inset_first based on BagelOrb suggestion. 2016-09-16 10:34:53 +01:00
Ghostkeeper 7ba5b5c0c8 Merge branch 'bugfix_jagged_support' of github.com:Ultimaker/CuraEngine into bugfix_jagged_support 2016-09-15 16:12:22 +02:00
Tim Kuipers adcd061f00 fix: smooth_outward fixes (CURA-2296) 2016-09-15 15:39:13 +02:00
Tim Kuipers f6df79a134 sanity checks (CURA-2296) 2016-09-15 15:19:27 +02:00
Tim Kuipers 955399765c fix: smooth_outward: continue if a step in both directions doesn't violate the shortcut_length constraint (CURA-2296) 2016-09-15 15:07:30 +02:00
Tim Kuipers b79043f772 fix: smooth_outward: no more asserts which are violated by rounding errors (CURA-2296) 2016-09-15 14:42:27 +02:00
Tim Kuipers 1380d76075 Merge branch 'bugfix_jagged_support' of github.com:Ultimaker/CuraEngine into bugfix_jagged_support 2016-09-15 13:43:23 +02:00
Tim Kuipers a2c5a73f09 fix: support: prefer more smoothing rather than less (CURA-2296) 2016-09-15 13:10:41 +02:00
Tim Kuipers 1bdf07ae88 fix: support smoothing based on whether the current mesh has an interface (CURA-2296)
before it looked at all meshes
2016-09-15 13:06:51 +02:00
TotalRetribution ca1d6082e6 In processInsets moved code in for loops to processInsetLoop function to reduce duplication caused by outer_inset_first. 2016-09-15 12:06:02 +01:00
Tim Kuipers 33eabda16f refactor: support: limited support_line_width to somputation for smoothing_distance (CURA-2296) 2016-09-15 13:02:39 +02:00
TotalRetribution da659759cf Merge remote-tracking branch 'origin/master' into reverse_inset_order 2016-09-15 12:00:53 +01:00
Tim Kuipers 5b99143761 lil doc (CURA-2296) 2016-09-15 12:59:05 +02:00
Tim Kuipers dc92c1376c lil refactor (CURA-2296) 2016-09-15 12:32:01 +02:00
Tim Kuipers beaf71dfe8 lil optimization: pass down the square of shortcut_length in smooth_outward (CURA-2296) 2016-09-15 12:30:33 +02:00
Tim Kuipers 3062c07763 fix: smooth_outward used outdated v02 var (CURA-2296) 2016-09-15 12:23:53 +02:00
Tim Kuipers 134b37da2e refactor: factored out complex case out of smooth_outward (CURA-2296) 2016-09-15 12:15:12 +02:00
Tim Kuipers af10ef571d refactor: factor out the simple case from smooth_outward (CURA-2296) 2016-09-15 11:54:47 +02:00
Tim Kuipers 92af4b76b7 doc: more comments for smooth_outward (CURA-2296) 2016-09-15 10:25:58 +02:00
Tim Kuipers d97cfa4152 refactor: factor out inner part of loop in smoot_outward (CURA-2296) 2016-09-15 09:50:53 +02:00
Tim Kuipers 9988c67397 fix: invalid assert removed (CURA-2296)
assertion didn't hold for lines with a very shallow angle
2016-09-15 09:32:24 +02:00
Tim Kuipers b7f77e9dca lil improvement (CURA-2296) 2016-09-15 09:11:31 +02:00
Tim Kuipers ca25ce076c lil comment (CURA-2317) 2016-09-14 18:12:30 +02:00
TotalRetribution 71bfafbc0f Fixed Indentation in FffGcodeWriter.cpp FffGcodeWriter::processInsets. 2016-09-14 14:53:52 +01:00
Ghostkeeper ec44e711fb Fix long literals
Turns out the 1 wasn't an 1 but a lowercase L. This is why people tend to use uppercase L for long.

Contributes to issue CURA-2296.
2016-09-14 13:48:02 +02:00
Ghostkeeper 8fd5c6d404 Fix wording of angle parameter documentation
Maximal is a local maximum. Maximum is the actual maximum, where there is nothing higher. The second is correct here.

Contributes to issue CURA-2296.
2016-09-14 13:39:42 +02:00
Ghostkeeper 9da278795f Fixed numerical literal typo
Contributes to issue CURA-2296.
2016-09-14 13:33:39 +02:00
Ghostkeeper 2050e71dda Fixed numerical literal typo
Contributes to issue CURA-2296.
2016-09-14 13:30:48 +02:00
Tim Kuipers df003d7649 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-09-14 10:47:17 +02:00
Tim Kuipers 281e647b82 refactor: global_inherits_stack ==> limit_to_extruder (CURA-2308) 2016-09-14 10:47:08 +02:00
Ghostkeeper 0771eafc60 Fix warning of comparison unsigned to signed
This now allows for higher amounts of draft shield layers, in theory, with the same memory and processing cost.
2016-09-13 17:20:14 +02:00
Ghostkeeper 0743994375 Add spaces after comma
As per the code style.

Contributes to issue CURA-2296.
2016-09-13 16:53:42 +02:00
Tim Kuipers 7b9e571474 fix: don't retrieve the machine name from the json document, but from the settings (CURA-2360) 2016-09-13 16:44:07 +02:00
Ghostkeeper 73506345ac Fix line direction mix-up in documentation
The point is before ab, or after ba.

Contributes to issue CURA-2296.
2016-09-13 16:39:52 +02:00
Tim Kuipers ff57c578db fix: copy first extruder if there's no definition for a latter one (CURA-2016) 2016-09-13 15:00:53 +02:00
TotalRetribution 0436916f4a Merge remote-tracking branch 'origin/master' into reverse_inset_order
Conflicts:
	src/FffGcodeWriter.cpp

Fixed Conflict with src/FffGcodeWriter.cpp
2016-09-13 10:55:31 +01:00
Tim Kuipers 4a392d1e79 fix: interface wasn't generated near the top of the print (CURA-2308) 2016-09-13 11:42:53 +02:00
Tim Kuipers eb277a1181 lil type fix (CURA-2016) 2016-09-13 11:38:09 +02:00
Tim Kuipers 7b972305b5 fix: exit when file couldn't load (CURA-2016) 2016-09-13 11:35:41 +02:00
Tim Kuipers 103b2ef7a8 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-09-13 11:18:23 +02:00
Tim Kuipers a4bb8ed287 fix: exit when json couldn't load (CURA-2016) 2016-09-13 11:15:54 +02:00
Tim Kuipers 8edce9d470 log errors when json files couldn't load (CURA-2016) 2016-09-13 11:08:46 +02:00
Tim Kuipers 54152aded7 lil cleanup (CURA-2016) 2016-09-13 10:35:58 +02:00
Tim Kuipers 771e840d07 lil cleanup (CURA-2016) 2016-09-13 10:26:55 +02:00
Tim Kuipers 1057b1e277 fix: load extruder json when and only when extruder is created (CURA-2016) 2016-09-13 10:25:54 +02:00
Tim Kuipers 26b78d68f4 fix: don't overload extruder settings over user supplied (CURA-2016) 2016-09-12 18:30:39 +02:00
Tim Kuipers 2583c2e594 Revert "fix: command line slicing overwrote child settings with parent settings"
This reverts commit 9c2fb686e6.
2016-09-12 18:29:35 +02:00
Tim Kuipers 9c2fb686e6 fix: command line slicing overwrote child settings with parent settings 2016-09-12 18:08:08 +02:00
Tim Kuipers 7396deb65b fix: jerk gcode for repetier firmware 2016-09-12 11:07:53 +02:00
Tim Kuipers 32f234eb87 Merge branch '2.3' 2016-09-12 10:49:15 +02:00
Tim Kuipers adbf0354e6 refactor: getDraftShieldHeight==>getDraftShieldLayerCount (CURA-2098) 2016-09-12 10:49:00 +02:00
Tim Kuipers 5e7acc8b62 fix: draft shield is computed over all layers used now (CURA-2098) 2016-09-12 10:47:13 +02:00
Tim Kuipers b1044cbe24 lil innocent assert change 2016-09-09 17:19:16 +02:00
Tim Kuipers 91a5589413 Merge branch '2.3' 2016-09-09 17:04:50 +02:00
Tim Kuipers dc97b5f312 fix: add brim around support and around model (CURA-2317) 2016-09-09 12:30:04 +02:00
Tim Kuipers b494b7b86d fix: made secondary skirt generate even when the primary is empty; retrieved minimal_length per extruder (CURA-2308) 2016-09-09 12:10:38 +02:00
Tim Kuipers 970789fb4b lil renames (CURA-2308) 2016-09-09 11:45:18 +02:00
Tim Kuipers f4e19dd217 lil rename 2016-09-08 19:47:56 +02:00
Tim Kuipers 700ce8ef3b fix: retrieve interface_enable per mesh (CURA-2296) 2016-09-08 19:47:26 +02:00
Tim Kuipers df3426e8bb Merge branch '2.3' 2016-09-08 17:31:02 +02:00
Tim Kuipers fd73470580 fix: carveMultipleVolumes again! (CURA-1917) 2016-09-08 17:30:35 +02:00
Tim Kuipers 959664c080 Merge branch '2.3' 2016-09-08 17:17:53 +02:00
Tim Kuipers 3e9b42a2a2 fix: only print ooze shield up to the layer where the last extruder switch will happen (CURA-2139) 2016-09-08 17:17:44 +02:00
Tim Kuipers eb74c5ce88 Merge branch '2.3' 2016-09-08 16:24:47 +02:00
Tim Kuipers 51b840caee cleanup: made some stuff const in support.cpp (CURA-2052) 2016-09-08 16:24:34 +02:00
Tim Kuipers d695344f1b cleanup: removed debugging code (CURA-2296)
and removed acciodental bracket, which f*cks things up when you try to build previous commits
2016-09-08 15:44:52 +02:00
Tim Kuipers 0e9a7154ca removal: made support_area_smoothing hardcoded (CURA-2296) 2016-09-08 15:39:20 +02:00
Tim Kuipers 72e4bf8069 cleanup: some doc, parameter name changes, lil bugfix (CURA-2296) 2016-09-08 15:35:18 +02:00
Tim Kuipers acf26ba2f8 fix: made LinearAlg2D::getDist2FromLine more robust against rounding errors (CURA-2296) 2016-09-08 15:15:28 +02:00
Tim Kuipers 347f5c6238 fix: made LinearAlg2D::getPointOnLineWithDist more robust against rounding errors (CURA-2296) 2016-09-08 14:55:27 +02:00
Tim Kuipers aebe1ef46c fix: smooth_outward half blocked case was too naive (CURA-2296)
In the case which loks like \/ the sides may be longer than the shortcut on top, while there might be a shortcut halfway through which is long enough
2016-09-08 13:48:06 +02:00
Tim Kuipers 4f073ab23f fix: smooth_outward takes care of degenerate verts (CURA-2296) 2016-09-08 11:51:12 +02:00
Tim Kuipers 62dd1c1575 fix: smooth_outward now handles separately when v02 dist is about the required dist (CURA-2296)
This is to avoid rounding errors which conclude that no shortcut is possible
Now also in the general case
2016-09-08 11:18:50 +02:00
Tim Kuipers 04df283704 indent only (CURA-2296) 2016-09-08 10:46:47 +02:00
Tim Kuipers 695e5dd982 fix: smooth_outward now handles separately when v02 dist is about the required dist (CURA-2296)
This is to avoid rounding errors which conclude that no shortcut is possible
2016-09-08 10:45:18 +02:00
Tim Kuipers 26757ef802 fix: smooth_outward used degrees instead of radians (CURA-2296) 2016-09-07 19:40:01 +02:00
Tim Kuipers baf8f49360 feat: Polygon::smooth_outward (CURA-2296) 2016-09-07 19:23:27 +02:00
Tim Kuipers 4b20528a34 feat: LinearAlg2D::getPointOnLineWithDist(.) (CURA-2296) 2016-09-07 15:41:05 +02:00
Ghostkeeper 487b990f46 Merge branch '2.3' 2016-09-07 15:31:01 +02:00
Ghostkeeper 27d940191e Merge branch 'bugfix_polygon_oversimplification' into 2.3
Conflicts:
	src/utils/ListPolyIt.cpp
	src/utils/ListPolyIt.h
2016-09-07 15:29:05 +02:00
Ghostkeeper 7c48706e80 Fix compiler warning with multiline comment
Because the line ended in a backslash, it would take the next line as a comment too. The next line was already a comment, so it didn't matter, but this period is now at the end to prevent the compiler warning.

Contributes to issue CURA-1966.
2016-09-07 14:51:53 +02:00
Tim Kuipers 09a3ec4790 feat: LinearAlg2D::pointIsLeftOfLine(.) (CURA-2296) 2016-09-07 13:34:52 +02:00
Tim Kuipers b1e602f9f2 lil refactor: argument reorder in PolygonRef::smooth(.) (CURA-1966) 2016-09-07 09:21:11 +02:00
Tim Kuipers f98836c279 cleanup/optimization: PolygonRef::smooth has no more code duplication and values aren't recomputed any more (CURA-1966) 2016-09-07 09:17:43 +02:00
Tim Kuipers b54a0d3b91 fix: revamped Polygon smooth(.) (CURA-1966) 2016-09-06 17:43:27 +02:00
Jack Ha a3f2d08f13 Merge branch '2.3' 2016-09-06 16:53:22 +02:00
Ghostkeeper 2b6c70d19c Remove unnecessary brackets
Contributes to issue CURA-2052.
2016-09-06 15:14:51 +02:00
Tim Kuipers b79f362196 refactor: moves smooth(.) from Polygon.h to Polygon.cpp (CURA-1966) 2016-09-06 14:25:52 +02:00
Tim Kuipers 535bb8cf66 Merge branch '2.3' 2016-09-06 11:14:53 +02:00
Tim Kuipers eb2d4d3ad1 fix: test SparseLineGrid through origin and visualize correct 2016-09-06 11:12:35 +02:00
Tim Kuipers a8152bb727 Merge branch '2.3' into markwal-negativesparselinegrid 2016-09-06 10:55:11 +02:00
Tim Kuipers 3e0a2ca48a Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-09-06 10:54:25 +02:00
Tim Kuipers 2dc4411702 fix: help didn't get outputted 2016-09-06 10:54:18 +02:00
Mark Walker 0d38105a1a Simplify since we don't need the locals outside of
inner block any more
2016-09-05 11:30:40 -07:00
Mark Walker c179d16a9e sign to nonzero_sign and remove corner crossing fix 2016-09-05 11:14:03 -07:00
Mark Walker 76ac083eb3 Add some variable comments 2016-09-05 10:37:10 -07:00
Mark Walker da998384f0 SparseGridLine for negative coordinates
and also the TODO for precisely crossing cell corners
2016-09-05 10:37:10 -07:00
Tim Kuipers 13f7e0d7e4 fix: give support interface a different color (CURA-2049) 2016-09-05 18:01:21 +02:00
Tim Kuipers 1f721a2513 fix: expand support interface so that it's never too thin to be printed (CURA-2052) 2016-09-05 17:50:37 +02:00
Tim Kuipers 4df0a27cb2 fix: support interface skip layer flaw (CURA-2052)
we should divide up the remaining layers after we have checked the top or bottom most
2016-09-05 17:49:34 +02:00
Tim Kuipers 0c6aca19aa fix: interface also checks in-between layers for model (CURA-2052) 2016-09-05 17:05:17 +02:00
Aldo Hoeben bd8c501dc3 Update Cura.proto
Add polygon type for Support Interface

CURA-2049
2016-09-05 16:33:45 +02:00
Tim Kuipers b36acb07c5 fix: don't put skirt under support (CURA-2264) 2016-09-05 16:14:38 +02:00
Tim Kuipers 492bf72ffd fix: wire printing: processStartingCode copied from FffGcodeWriter and replaced setting retrieval to naive approach (CURA-1555) 2016-09-05 15:38:18 +02:00
Tim Kuipers f7ef517f01 fix: div by zero in LinePolygonsCrossings::lineSegmentCollidesWithBoundary() (CURA-2237) 2016-09-05 13:50:06 +02:00
Tim Kuipers 7a015ea146 fix: combing could cross exactly between two consectuive line segments of the boundary (CURA-2237) 2016-09-01 19:15:35 +02:00
Tim Kuipers 48aaebc20d fix: only set support.generates when it actually generated support (CURA-1571) 2016-09-01 18:03:02 +02:00
Tim Kuipers 209f770cf4 fix: don't drop on buildplate when there's support under the model (CURA-1571) 2016-09-01 18:02:01 +02:00
Tim Kuipers ec6c1216f2 fix: wireprint skirt extrusion was 10x too big (CURA-1555) 2016-09-01 15:04:29 +02:00
Tim Kuipers c638ca822b fix: wireprint flow now calculated as mm3 instead of E steps (CURA-1555) 2016-09-01 15:03:59 +02:00
Tim Kuipers 9007e3ff50 refactor: extrusion_per_mm ==> extrusion_mm3_per_mm (CURA-1555) 2016-09-01 15:01:25 +02:00
Tim Kuipers 32ce5e5a44 fix: wireprint don't print skirt at z0 (CURA-1555) 2016-09-01 14:57:29 +02:00
Tim Kuipers 825882100f fix: wire printing pointer bug (CURA-1555) 2016-09-01 13:47:32 +02:00
Tim Kuipers a47aa1e4fd cleanup: changed some logError to log or logWarning and cleaned up the messages 2016-09-01 13:06:01 +02:00
Tim Kuipers f5f1886175 feat: outputing correct gcode header after command line slicing 2016-09-01 12:02:27 +02:00
Tim Kuipers 26d05dc5e6 Merge pull request #385 from markwal/fixconstexprwarning
Fix constexpr function warning for C++14
2016-08-31 17:16:22 +02:00
Mark Walker 2715599791 Fix constexpr function warning for C++14
See https://akrzemi1.wordpress.com/2013/06/20/constexpr-function-is-not-const/
2016-08-28 20:51:36 -07:00
Jaime van Kessel 3c6b153e60 Removed double indication as this caused mac build to fail 2016-08-19 14:58:43 +02:00
Ghostkeeper 9bfdeb4ccd Move per-path offset to use it only on skirt
This is the only place where an outside offset is done and the inside polygons don't matter, so it is the only place where the union can safely be performed, since the union treats holes as normal polygons.

Contributes to issue CURA-2088.
2016-08-19 11:11:10 +02:00
Ghostkeeper 3aaddb08ba Move approxConvexHull implementation to CPP file
Where it damn well belongs, the little runt.

Contributes to issue CURA-2088.
2016-08-19 11:01:20 +02:00
Ghostkeeper 752985c9b2 Remove debug include
Because this include is at the top of the file, I didn't see it was still there. Sorry.

Contributes to issue CURA-2088.
2016-08-18 14:22:22 +02:00
Ghostkeeper dcb8a03a73 Offset polygons one path at a time
This prevents an infinite loop in the offset code in Clipper if the polygons overlap.

Contributes to issue CURA-2088.
2016-08-18 14:16:57 +02:00
Ghostkeeper 028c408be3 Move Polygons::Offset to CPP file
We should really do this with all of these functions. In this case, I required this in order to be able to import SVG for a bit of debugging.

Contributes to issue CURA-2088.
2016-08-18 13:21:27 +02:00
Ghostkeeper 822a2d55f2 Rebase bugfix_overlap_compensation onto master
Contributes to issue CURA-1911.
2016-08-17 17:09:21 +02:00
Ghostkeeper 3e283a375e Revert "lil feat: LinearAlg2D::projectPointOnLine(.) (CURA-1911)"
This reverts commit 7f08d37408eb9d010a24095f3f9b6facbe862c2e.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 635ef25030 Optimize if-statement structure
Also more readable, in my opinion. It's just a bunch of cases and a base case, with lazy evaluation in between.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 9a9855c0a5 Swap ifs so that we use else-if instead of recompute
Saves some computation time.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 83e52f6e53 Make projections constant
These variables aren't changed. This might allow for better optimisations by the compiler.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 9d59dde760 Improve overlap estimation for stair-case
In the case of stairs, there is no overlap either. The approximation now takes that into account at hardly any additional computational cost.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 9b40d6053c Switch from and to back
Since these parameters are now renamed, this is clearer.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper ba05f42c91 Remove intermediary variable
The intermediary variable was supposed to make the variable name more clear. Instead, I just renamed the input parameter, removing the weird construct.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 5684a0cbb4 Remove commented code
As per our code style.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 0647fa21fe Switch to and from other parameters
For consistency.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper cb5467d2b3 Fix typo in documentation
Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Ghostkeeper 24ec8bb254 Fix spelling in documentation
Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00
Tim Kuipers 8069aba0b5 fix: proximity: don't disregard potential linking between a point and a consecutive line segment (CURA-1911)
short line segments might need linking from front to end when the previous and next line segment are parallel
2016-08-17 16:43:09 +02:00
Tim Kuipers 591cd105b5 lil TODO idea (CURA-1911) 2016-08-17 16:43:09 +02:00
Tim Kuipers 13e5e786bc fix: proximity: don't disregard potential linking between consecutive line segments (CURA-1911)
the end point of the new line segment might still need to be projected onto the other line segment
short line segments might need linking from front to end when the previous and next line segment are parallel
2016-08-17 16:43:09 +02:00
Tim Kuipers 6f0ef96c5e fix: small fixes for overlap compensation (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers ed636f9a44 lil assert change 2016-08-17 16:43:08 +02:00
Tim Kuipers 8f50db989c fix: remove degenerate verts which result in multiple verts sharing the same location (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers f4e22ae7a0 lil feat: LinearAlg2D::projectPointOnLine(.) (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers cbc6e6f270 fix: PolygonProximityLinker::addProximityEnding don't add to the same set we are looping over (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers 07b8165d47 fix: getApproxOverlapArea had arguments ordered wrong (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers e2409148cc fix: compute less overlap when line segments are not opposite each other (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers a3129bf1f6 feat: LinearAlg2D::pointIsProjectedBeyondLine (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers f191497234 fix: overlap compensation: move direction info down to getApproxOverlapArea (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers ef9c477ee2 lil cleanup & doc (CURA-1911) 2016-08-17 16:43:08 +02:00
Tim Kuipers bdcaa6c9ac lil refactor (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 046310efcb more doc of PolygonProximityLinker::addNewPolyPoint (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 74133ae7d7 rename PolygonProximityLinker::findProximatePoints a_from ==> a_point (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 20dff1fe8b Revert "lil doc fix (CURA-1911)"
This reverts commit 1651e04f2cd1cfd69c8c92d397abfba53cb5dcc9.
2016-08-17 16:43:07 +02:00
Tim Kuipers c7be6e147b fix: was taking std::max of double and float (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 574739e193 lil doc fix (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers b3c0f0d2da lil rewrite (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 33be01596c lil doc of wall overlap (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 2cbd6d1c8a lil rename in wall overlap (CURA-1911) 2016-08-17 16:43:07 +02:00
Tim Kuipers 510c328127 lil refactor: user unordered_map/set.find(.) rather than .count(.) (CURA-1911) 2016-08-17 16:43:06 +02:00
Ghostkeeper 9413aceaf2 Add space after comma
Thus haveth the code style spoken.

Contributes to issue CURA-1911.
2016-08-17 16:43:06 +02:00
Ghostkeeper aee1887b57 Add spaces around assignment operators
As the code style declares.

Contributes to issue CURA-1911.
2016-08-17 16:43:06 +02:00
Ghostkeeper b72917305c Add missing brackets
To comply to the code style.

Contributes to issue CURA-1911.
2016-08-17 16:43:06 +02:00
Ghostkeeper 7d61f00b3a Fix documentation grammar
It was hard to read due to the point A vs. the indefinite article a.

Contributes to issue CURA-1911.
2016-08-17 16:43:06 +02:00
Tim Kuipers 247f5d3cde fix: removed debugging member from WallOverlapComputation (CURA-1911) 2016-08-17 16:43:06 +02:00
Tim Kuipers 21202c2e8c fix: don't compensate for overlap if the two lines are going in the same direction (CURA-1911) 2016-08-17 16:43:06 +02:00
Tim Kuipers 6973e99cd1 cleanup: removed old slow overlap computation (CURA-1911) 2016-08-17 16:43:06 +02:00
Tim Kuipers 8cf1f1646b safety: check for duplicate points in input polygon (CURA-1911) 2016-08-17 16:43:06 +02:00
Tim Kuipers 2a1fc8245a safety: check for duplicate points in input polygon (CURA-1911) 2016-08-17 16:43:06 +02:00
Tim Kuipers a1a3931f68 feat: improved slicing speed by letting overlap compensation use SparseLineGrid (CURA-1911)
also iterate over the newly introduced points again, because they might need links which don't get introduced when we already passed that point in the poly
also prevert newly introduced points to again introduce new points, cause otherwise we might introduce a lot of new points in a corner (visual explanation in code)
2016-08-17 16:43:05 +02:00
Tim Kuipers e3dd8f0bdf forgotten import (CURA-1911) 2016-08-17 16:43:05 +02:00
Tim Kuipers 3a06f49fba fix: fixes for SparseLineGrid line to pixel algorithm (CURA-1911)
sometimes a line wouldn't be added to the grid at all
2016-08-17 16:43:05 +02:00
Tim Kuipers 65e90d5de0 feat: ListPolyIt operator!= and hash function object (CURA-1911) 2016-08-17 16:43:05 +02:00
Tim Kuipers e33c441f6d feat: Polygons::pointCount (CURA-1911) 2016-08-17 16:43:05 +02:00
Tim Kuipers 418de5d822 refactor: use actual SparseLineGrid rather than the hacked locToLineGrid (CURA-1911) 2016-08-17 16:43:05 +02:00
Tim Kuipers a6b45b683e fix: no more overlap areas are skipped because neighboring overlap areas were already processed (CURA-1911) 2016-08-17 16:43:05 +02:00
Tim Kuipers eda2c987ff feat: symmetric pair utility class (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers a71c18a146 safety: debug check for line grids (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 424e3edb4d fix: SparseLineGrid computes cells based on print coords rather than grid coords (CURA-1911)
also a fix for horizontal lines, which caused division by zero.
2016-08-17 16:43:04 +02:00
Tim Kuipers ad1c442c9a feat: SparseLineGrid (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 65ced4a4bb feat: SparseGrid now exposes more functions for grid to print space coords to inheriting classes (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 242e8118e3 refactor: split off SparseGrid from SparsePointGrid (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers f720cc1f0b refactor: SparseGridInvasive ==> SparsePointGrid (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 721d7d799f refactor: SparseGrid ==> SparsePointGridInclusive (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 26c3a35150 fix: moved SparseGridElem to SparseGrid.h (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers c1d097f7fe lil rename (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers fedcfe582e safety: asserts in PolygonProximityLinker (CURA-1911) 2016-08-17 16:43:04 +02:00
Tim Kuipers 1ffafd4b17 debug fix: don't visualize SVG upside down (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 45c4e2e0a1 fix: proximitylinker: don't add points twice (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 6ae7fdac5c fix: proximitylinker: don't add points on the same location as other points (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers bf2123b82c feat/refactor: PolygonProximityLinker::addCornerLink (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 81881a876d fix: link points ifthey are proximate even though the line segments are connected (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 34859670ef refactor: bit of renaming (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 4038d39e85 fix: process sharp corners before normal proximite points (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 999a131ecf fix: ListPolyIt assignment overwrite referred polygon (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 2bcffaa0ab fix: overlap comensation is now also activated for sharp corners (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 7f584c2e4f refactor: merge proximity_point_links_endings into proximity_point_links (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers ba045e803b indent change only (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 1fd2c66ec7 fix: don't create ending link when other side continues to overlap with the same point (CURA-1911)
also inverted some logic; indent change follows
2016-08-17 16:43:03 +02:00
Tim Kuipers be7ea53325 fix: account for overlap areas ending in a pointy end (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 8045fb49ff refactor: let PolygonProximityLinker::findProximatePoints use ListPolyIt instead of separate iterators and references (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers 1504e38c8c fix: proximity was only recorded between points and line segments further on in the polygon (CURA-1911) 2016-08-17 16:43:03 +02:00
Tim Kuipers b646e94880 refactor: renaming in PolygonProximityLinker::findProximatePoints (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 929719ebdd refactor: findProximatePoints actual linking functionality moved to its own function (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 905d788c3c fix: findProximatePoints used wrong first segment (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 0dba144c37 doc: PolygonProximityLinker overlap ==> proximity (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers f7ba6979da refactor: moved SparseGridInvasive to its own file (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers ae832cba68 fix: overlap links got introduced twice sometimes (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 6bdc8dae51 refactor: factored out ProximityPointLink into ints own class file (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 9a23163564 copyright 2016-08-17 16:43:02 +02:00
Tim Kuipers fcc207ad0d fix: store whether an overlap link is passed once outside of the link (CURA-1911)
The link was stored at multiple places (copies), so the information was not shared between all copies.
2016-08-17 16:43:02 +02:00
Tim Kuipers 44df11bc2c fix: PolygonProximityLinker::getLink now returns pointer instead of optionally a copy (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 2458a9a580 lil optimization for wall overlap compensation (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers ba92bf9693 cleanup: PolygonProximityLinker::isLinked(.) makes wallOverlap more simple (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 8c1ecf1200 cleanup: simplified WallOverlapComputation::handlePotentialOverlap (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 4fdcf4ed29 cleanup: removed code duplication and add more documentation (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers cde315109a fix: overlap compensation now also compensates segments overlapping with points (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers 50ca8d7473 fix: let overlap compensation account for segments overlapping with multiple other segments (CURA-1911)
Make PolygONProximityLinker::Point2Link be a multimap, mapping points to multiple links
Change getLink(.) to getLinks(.)
Expose Point2Link so that outside classes can reason about which link is which

wallOverlap
Compute the actual overlapping area, since the ratio cannot easily be computed when multiple overlaps are involved
Compute the overlap area for segments for which both ends are linked to the same point
2016-08-17 16:43:02 +02:00
Tim Kuipers 9b8b3d150c cleanup: the list_polygons are used after the contructor of PolygonProximityLinker (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers f4fd6ab0f4 cleanup: lil doc and trailing whitespace (CURA-1911) 2016-08-17 16:43:02 +02:00
Tim Kuipers b593331c17 cleanup: removed outcommented debug code (CURA-1640) 2016-08-17 16:43:02 +02:00
Tim Kuipers afb6237cbf dox fix: updated doc of WallOverlap (CURA-1640) 2016-08-17 16:43:02 +02:00
Tim Kuipers a716b88621 dox fix: updated doc of PolygonProximityLinker (CURA-1640) 2016-08-17 16:43:02 +02:00
Tim Kuipers 0bc384c807 cleanup: removed unused commented code (CURA-1640) 2016-08-17 16:43:02 +02:00
Tim Kuipers a2e4f2a1cb fix: accidentally removed line (CURA-654) 2016-08-17 16:43:01 +02:00
Tim Kuipers d3c62049eb refactor: made WallOverlap use PolygonProximityLinker (CURA-654) 2016-08-17 16:43:00 +02:00
Tim Kuipers e773029266 refactor+fix: moved ProximityLinkAttributes into (mutable) ProximityLink + fixed iterator invalidation issues (CURA-654)
the [passed] attribute needs to be mutable: it doesn't change the hash of the object and needs to be changed after it's been in the set

fixed iterator invalidation issues by simply copying items from the set into the map, rather than referring to them
2016-08-17 16:39:35 +02:00
Tim Kuipers c42b3322cd removed debug function 2016-08-17 16:39:35 +02:00
Tim Kuipers b652db33c1 refactor: rename WallOverlap ==> Proximity (CURA-654) 2016-08-17 16:39:35 +02:00
Tim Kuipers 596082dc7c cleanup PolygonProximityLinker and use ListPolyIt (CURA-654) 2016-08-17 16:39:35 +02:00
Tim Kuipers 411300e0e1 refactor: factor out ListPolyIt class (CURA-654) 2016-08-17 16:39:35 +02:00
Tim Kuipers b44116c4e5 feat: PolygonProximityLinker class (CURA-654) 2016-08-17 16:39:35 +02:00
Ghostkeeper bf0cd02928 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-08-11 16:37:53 +02:00
Ghostkeeper 3b4c54839d Merge feature_brim_under_support
Conflicts:
	src/SkirtBrim.cpp
	src/gcodeExport.cpp
2016-08-11 16:35:27 +02:00
Tim Kuipers b6cb7cd271 fix: don't limit speeds to 1 mm/s when retrieving a setting (CURA-1049) 2016-08-11 16:29:25 +02:00
Tim Kuipers ee447a9191 merge conflict fix (CUYRA-1956) 2016-08-11 16:22:31 +02:00
Ghostkeeper 9d755211cd Update reference to support skin
This new code was introduced by the brim-only-outside feature and gave no merge conflict, but was not up-to-date with the support roof rename to support skin.

Contributes to issue CURA-1413.
2016-08-11 15:58:46 +02:00
Tim Kuipers db83fcd57e fix: also generate brim for parts which flly lie inside other parts (CURA-1413) 2016-08-11 15:38:17 +02:00
Tim Kuipers 2ccaef10b9 feat: Polygons::removeEmptyHoles() (CURA-1413) 2016-08-11 15:38:17 +02:00
Tim Kuipers d004c77106 feat: brim only for outside polygons (CURA-1413) 2016-08-11 15:38:17 +02:00
Tim Kuipers 2bddfcd42a feat: Polygons::getOutsidePolygons() (CURA-1413) 2016-08-11 15:38:17 +02:00
Tim Kuipers 4cd1b5066c fix: only generate skirt polygons for used extruders (CURA-1647)
also cleaned up generateSkirtBrim a bit
also applied code conventions
inadvertently also causes support not to be removed by unprinted skirt
removed skirts_or_brims which didn't need to be a vector (only last elem was used)
2016-08-11 15:38:17 +02:00
Tim Kuipers e0959e4aa0 feat: ExtruderTrain::is_used (CURA-1647) 2016-08-11 15:38:17 +02:00
Tim Kuipers a29dcb33da fix: brim removed support rather than going around it (CURA-1647) 2016-08-11 15:24:20 +02:00
Tim Kuipers 152cf5835f Merge branch 'master' into feature_brim_under_support 2016-08-11 15:19:20 +02:00
Tim Kuipers eb065aa415 lil 2016-08-11 14:53:28 +02:00
Tim Kuipers 9430432af7 fix: support simplifies based on line width (CURA-1966) 2016-08-10 14:17:48 +02:00
Tim Kuipers 252c2cae28 assert fix: default jedi profile has speeds up to 200 2016-08-10 13:12:27 +02:00
Aldo Hoeben 9ee30bba12 Merge pull request #379 from Ultimaker/master-CURA-1724
CURA-1724: Updating README.md
2016-08-10 12:45:03 +02:00
Tim Kuipers 3afef5d29f fix: PolygonRef::simplify early catch for duplicate points in polygons (CURA-1966) 2016-08-10 12:01:48 +02:00
Tim Kuipers 056240d55c fix: PolygonRef::simplify prev wasn't recorded correctly (CURA-1966) 2016-08-10 11:59:28 +02:00
Tim Kuipers 10b43fc9f8 lil: AABB for single polygon (CURA-1966) 2016-08-10 11:37:44 +02:00
Tim Kuipers f83fbfe143 fix: PolygonRef::simplify only removes verts if BOTH line segments are too small now (CURA-1966) 2016-08-09 17:46:10 +02:00
Thomas Karl Pietrowski bcfe400c18 Correcting formatting 2016-08-09 17:38:37 +02:00
Tim Kuipers b67b51ce76 fix: overhauled PolygonRef::simplify (CURA-1966)
polygons would be simplified such that previously removed points violated the allowed error constraint further on in the algorithm
2016-08-09 17:37:10 +02:00
Tim Kuipers 83aeb36bcf feat: ListPolyIt convert functions for single (list)polygon (CURA-1966) 2016-08-09 17:34:00 +02:00
Thomas Karl Pietrowski e75cdf3f7c Removing hack for skipping build of protobuf using gtest
Can't find anything except of gmock in https://github.com/google/protobuf/blob/e8ae137c96444ea313485ed1118c5e43b2099cf1/autogen.sh that mentions gtest.
For about a year gmock has been moved to gtest, but 3.0.0 is still using gmock from it's old repository.
Additionally as the lines mentioned don't fit to the content of automake.sh, I just removed this part.
2016-08-09 17:21:21 +02:00
Thomas Karl Pietrowski ecd11212f4 Referencing to download page 2016-08-09 17:14:07 +02:00
Thomas Karl Pietrowski 5763b1704f Update README.md 2016-08-09 17:01:51 +02:00
Jack Ha f14d67934d Added some mock locals functions in runtests. CURA-1828. 2016-08-09 15:28:57 +02:00
Tim Kuipers 9ef35e2b14 copied ListPolyIt from bugfix_overlap_compensation branch (CURA-1966) 2016-08-09 14:18:34 +02:00
Tim Kuipers fadfaa5a80 removed debug code (CURA-1956) 2016-08-09 11:42:17 +02:00
Tim Kuipers fc962709b0 fix: clipper offset bug testing (CURA-1956) 2016-08-09 11:16:48 +02:00
Tim Kuipers a848b8c71d fix: UPDATE CLIPPER (CURA-1956)
this was loooooooong overdue
2016-08-09 11:15:35 +02:00
Tim Kuipers dc05c2749a feat: write max z feedrate when overriden by user value (CURA-1049) 2016-08-08 17:01:48 +02:00
Tim Kuipers cea312a23a rename: zPos ==> current_layer_z (CURA-1049) 2016-08-08 14:52:14 +02:00
Tim Kuipers deee23d1f8 fix: generate support interface correctly also inside a model (CURA-2074) 2016-08-08 13:48:54 +02:00
Tim Kuipers d097cfa78f rename: generateSupportRoofs ==> generateSupportInterface (CURA-2074) 2016-08-08 13:38:45 +02:00
Tim Kuipers 09c1df7ea6 Merge branch 'markwal-mergeinfillchanges' 2016-08-08 09:53:39 +02:00
Mark Walker 7f9e034dbb Propagate rename pressure->flow all the way down 2016-08-04 11:14:56 -07:00
Mark Walker 05b5a0b45c Rename pressure to flow and make it per extruder 2016-08-04 10:46:25 -07:00
Mark Walker ca1a076db3 nozzle_size doesn't change in the loop 2016-08-04 10:31:59 -07:00
Mark Walker 36c2bd81d0 Add setting to control autospeed for mergeinfill
Also:
- use travel speed for travel moves and extrusion speeds for
extrusion moves
- get the nozzle size from settings
2016-08-04 10:31:59 -07:00
Tim Kuipers d44c8eb067 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-08-04 12:02:44 +02:00
Tim Kuipers 2b81ce1dd5 fix: don't switch to adhesion extruder for skirt when printing raft (CURA-2013) 2016-08-04 12:02:29 +02:00
Tim Kuipers 73df32881a fix: support bottoms now don't check below zero any more (CURA-1013)
taking the max of unsigned int zero and another has no use!
The other value probably wrapped around to unsigned int max_value already
2016-08-02 12:45:05 +02:00
Tim Kuipers 01357eacc4 Merge branch 'master' into feature_support_bottoms 2016-08-02 12:40:05 +02:00
Ghostkeeper fc7c55768a Don't apply first layer speeds on raft layers
This only applied to travel speeds, since raft print speeds are determined by other settings, support is not on the raft layers, and meshes are not on the raft layers.

Contributes to issue CURA-1507.
2016-08-01 13:41:36 +02:00
Ghostkeeper fea300cfc3 Remove unnecessary static cast from int to int
It's already an int.

Contributes to issue CURA-1507.
2016-08-01 13:30:46 +02:00
Arjen Hiemstra 4f4a5dd1e7 Merge branch 'feature_support_settings_from_support_extruder'
* feature_support_settings_from_support_extruder:
  Remove debug statement
  Set settingInheritBase on every mesh in the meshgroup
  Be more robust against wrong input from Arcus
  Distribute global_inherits_stack across meshgroups
  Register SettingExtruder message type
  Sync from frontend: global_inherits_stack message
  feat: setting retrieval inheritance overrides (CURA-1953)
2016-08-01 13:01:18 +02:00
Ghostkeeper 02fe987035 Remove debug statement
Oops. Sorry.

Contributes to issue CURA-2011.
2016-08-01 12:08:46 +02:00
Ghostkeeper 9e7b6212b0 Set settingInheritBase on every mesh in the meshgroup
Also changed the nesting of the for loops around to make it a bit more efficient.

Contributes to issue CURA-2011.
2016-08-01 11:41:44 +02:00
Ghostkeeper 94a3a6b642 Abort thread after it has completed
Otherwise it stays sleeping for 60s. This produced too many threads on OSX.

Contributes to issue CURA-1828.
2016-08-01 11:10:27 +02:00
Tim Kuipers d7eef238ce cleanup: removed dead code (CURA-1647) 2016-07-31 22:12:01 +02:00
Tim Kuipers 3cbfe3b037 fix: retract after each meshgroup (CURA-1829)
also fixes (CURA-1379)
2016-07-31 19:07:55 +02:00
Ghostkeeper 9b8917e763 Be more robust against wrong input from Arcus
A simple range check against the extruder list.

Contributes to issues CURA-2011 and CURA-1953.
2016-07-29 17:38:00 +02:00
Ghostkeeper a5b7b474a6 Distribute global_inherits_stack across meshgroups
Every meshgroup gets the same global_inherits_stack values.

Contributes to issues CURA-2011 and CURA-1953.
2016-07-29 17:18:47 +02:00
Ghostkeeper 839fc30c91 Register SettingExtruder message type
So that we can read it.

Contributes to issue CURA-2011.
2016-07-29 16:29:15 +02:00
Ghostkeeper b8a6888d94 Sync from frontend: global_inherits_stack message
The front-end added the global_inherits_stack SettingExtruder message. We should add it too to be able to read the message. This is a copy from the same file in Cura's front-end.

Contributes to issue CURA-2011.
2016-07-29 16:25:59 +02:00
Jaime van Kessel 14cb8eeabd Removed anciend debug output
This statement of a time before time slowed down the frontend.
2016-07-29 16:17:06 +02:00
Tim Kuipers f9f79fa0e8 fix: only generate skirt polygons for used extruders (CURA-1647)
also cleaned up generateSkirtBrim a bit
also applied code conventions
inadvertently also causes support not to be removed by unprinted skirt
removed skirts_or_brims which didn't need to be a vector (only last elem was used)
2016-07-28 23:20:34 +02:00
Tim Kuipers 691efb3b6e feat: ExtruderTrain::is_used (CURA-1647) 2016-07-28 22:58:45 +02:00
Tim Kuipers 93be9431b7 fix: brim removed support rather than going around it (CURA-1647) 2016-07-28 21:29:46 +02:00
Ghostkeeper 6c757f3fcf Rename draft_shield_height to draft_shield_layers
This was considered better.

Contributes to issue CURA-1295.
2016-07-28 20:06:35 +02:00
Tim Kuipers ae66df7b0a Merge branch 'master' into feature_first_layer_travel_speed 2016-07-28 16:48:48 +02:00
Tim Kuipers d447d9d711 fix: process brim for each meshgroup again (CURA-1721) 2016-07-28 14:22:57 +02:00
Tim Kuipers b30f1c1a2e secret fix: change gcode type comment back to SKIRT 2016-07-28 14:16:08 +02:00
Tim Kuipers 2ff1c876b3 secret fix: change gcode type comment back to SKIRT 2016-07-28 14:14:02 +02:00
Tim Kuipers 6db1097faa API change: support_roof_... ==> support_interface... (CURA-1013) 2016-07-28 14:05:28 +02:00
Tim Kuipers 86cbe1797b Merge branch 'master' into feature_support_bottoms 2016-07-28 13:53:47 +02:00
Tim Kuipers 65f0ba9673 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-28 13:52:25 +02:00
Tim Kuipers 7d6df9746e fix: always generate skin when layer number is almost at top (CURA-1837)
dont try to compare with non-existent layer number which is too high
2016-07-28 13:52:18 +02:00
Ghostkeeper 2d2196648f Make draft shield limit work with small models
Problem was that the draft shield height limit was measured in number of layers, but it was compared with the height of the first layer to determine if we should skip adding draft shield layers, and the first layer height was measured in microns.

Contributes to issue CURA-1295.
2016-07-28 13:45:18 +02:00
Ghostkeeper 6992bb6e03 Code style: Indentation and brackets
Contributes to issue CURA-1509.
2016-07-28 11:44:49 +02:00
Tim Kuipers dee2f82d87 Merge pull request #374 from Ultimaker/bugfix_slicer_initializer
Properly initialize member variables of classes in slicer.h
2016-07-28 10:54:15 +02:00
Tim Kuipers bec685e004 fix: initialize slicer members to invalid values 2016-07-28 10:51:19 +02:00
Tim Kuipers d38ccb7691 fix: changed round_divide to work with unsigned ints (CURA-1013)
the math was wrong for negative numbers
2016-07-27 18:31:22 +02:00
Tim Kuipers e5036763fe fix: round support z distance up (CURA-1013) 2016-07-27 18:26:04 +02:00
Ghostkeeper 865f97c5d7 Repair draft shield height limitation part 2
Renamed DraftShieldLimitation to DraftShieldHeightLimitation. Also applied the limit to FffGcodeWriter and made the draft shield function more logical and make it only check for the limit if the draft shield height is limited.

Contributes to issue CURA-1295.
2016-07-27 17:48:53 +02:00
Ghostkeeper 0b4b04f31a Don't generate skirt if draft shield is full
If draft shield limitation is set to full but the height is set to 0, don't generate a skirt.

Contributes to issue CURA-1295.
2016-07-27 17:48:53 +02:00
Ghostkeeper 39fea42b92 Fix draft shield limitation setting
The engine now listens to the draft shield limitation. If it's set to full, we use the full height of the print to limit the draft shield rather than the height limit setting.

Contributes to issue CURA-1295.
2016-07-27 17:48:53 +02:00
Ghostkeeper f810356bdb Add draft shield height limitation enum
This enum will be needed to determine what height we should use for the draft shield.

Contributes to issue CURA-1278.
2016-07-27 17:48:53 +02:00
Ghostkeeper 4c41acd946 Re-enable skirt if draft shield disabled but height is not 0
It used to only look at the height.

Contributes to issue CURA-1295.
2016-07-27 17:48:52 +02:00
Ghostkeeper af135fff49 Take draft shield height of 0 if draft shield is disabled
This fixes the boolean setting again.

Contributes to issue CURA-1295.
2016-07-27 17:48:52 +02:00
Tim Kuipers ed4457a3f4 Merge branch 'master' into feature_support_bottoms 2016-07-27 17:40:11 +02:00
Tim Kuipers 7ea9b14a33 fix: retrieve globalish support settings from support extruder (CURA-2003) 2016-07-27 17:10:07 +02:00
Tim Kuipers 957c56c6b2 feat: setting retrieval inheritance overrides (CURA-1953) 2016-07-27 16:28:11 +02:00
Arjen Hiemstra 0a689a0da8 Properly initialize member variables of classes in slicer.h
I get crashes because SlicerSegment::endVertex is used uninitialized.
2016-07-27 15:36:58 +02:00
Ghostkeeper 39efcf5680 Add more asserts for isOutsideTest
This also tests other cases where the point happens to be on other edge cases for other sides of the triangle.

Contributes to issue CURA-1981.
2016-07-27 15:25:37 +02:00
Ghostkeeper 0bc373e935 Remove whitespace at end of line
As per our code style rules.

Contributes to issue CURA-1981.
2016-07-27 15:19:31 +02:00
Ghostkeeper 0c82c00e15 Improve documentation for generateSkirtBrim
Contributes to issue CURA-1678.
2016-07-27 11:56:56 +02:00
Tim Kuipers 1859de69d4 fix: round support_z_distance to nearest layer_height multiple, rather than always rounding up (CURA-1013)
this caused the number of roof/bottom layers to be incorrect (CURA-647)
2016-07-27 11:37:46 +02:00
Tim Kuipers e2fc79889b fix rename: .._support_skin options back to .._support_roof (CURA-1013)
We shouldn't change the interface so easily. This makes the json as it was a couple of ocmmits ago
2016-07-27 11:22:42 +02:00
Ghostkeeper b31646f10d Improve documentation of completeConfigs
Contributes to issue CURA-1507.
2016-07-27 11:18:39 +02:00
Tim Kuipers 10a8265674 Merge branch 'master' into feature_support_bottoms 2016-07-27 11:15:15 +02:00
Tim Kuipers 1445472530 fix: use round_divide where neccesary (CURA-1013) 2016-07-27 11:11:22 +02:00
Tim Kuipers 37f7e18269 fix: inline function definition in header (CURA-1013)
inline functions must always be defined in the header
2016-07-27 11:10:42 +02:00
Simon Edwards a854dc692d Added a lot more logging which kicks in when the -vv flag is given.
Contributes to CURA-1509 Cura in slicing loop, Arcus Error (8)
2016-07-27 11:00:28 +02:00
Tim Kuipers 9cbecf5782 fix: z_distance_top/bottom was computed in micron rather than in layers (CURA-1013) 2016-07-27 10:56:26 +02:00
Tim Kuipers b5575093d0 replace square function by math::square (CURA-1013) 2016-07-27 10:52:51 +02:00
Tim Kuipers 368747b213 feat: math file (CURA-1013)
for simple mathematical constructs
2016-07-27 10:52:05 +02:00
Tim Kuipers 64d3ee9728 rename: support_skin_.. options back to support_roof_... (CURA-1013)
We shouldn't change the interface so easily. This makes the json as it was a couple of ocmmits ago
2016-07-27 10:17:29 +02:00
Tim Kuipers 0ba0188bcd rename: support roof ==> support skin (CURA-1013)
the areas now include both roofs and skins
2016-07-26 18:44:40 +02:00
Tim Kuipers b8691c776e fix: (un)signed int syntax mistake (CUTA-1013) 2016-07-26 18:31:41 +02:00
Tim Kuipers afde126052 refactor: roofs ==> interface (CURA-1013)
the support interface contains areas which are roof and/or bottom
2016-07-26 18:31:14 +02:00
Tim Kuipers 5c22649d7b feat: support bottoms; bugfix: support roof height (CURA-647, CURA-1013) 2016-07-26 18:24:18 +02:00
Tim Kuipers 1e0f2ec7c1 lil refactor: switched two lines (CURA-1972) 2016-07-26 17:20:52 +02:00
Tim Kuipers c0d59627eb removed debug.h
I didn't really know how to write c++ at the time and it only gave difficult to solve problems up till now

(CURA-1792)
2016-07-26 15:58:25 +02:00
Tim Kuipers 836686b90c Merge branch 'slicer-speedup-clean' of https://github.com/scottlenser/CuraEngine 2016-07-26 12:48:21 +02:00
Tim Kuipers 4d7dd3adce fix: reflection graph improvements 2016-07-26 12:45:07 +02:00
Tim Kuipers 14013e657a removed dead code
code was probably used in the past to send layer data in a premature stage
2016-07-26 12:44:48 +02:00
Tim Kuipers 2a1cb236c8 reflection feat: visualize global-only mode 2016-07-26 11:47:07 +02:00
Tim Kuipers 0d17a056a2 lil reflection script fix 2016-07-26 11:22:34 +02:00
Tim Kuipers 722456ba64 lil fix: try-catch for debug code to find clipper bug 2016-07-26 10:29:35 +02:00
Scott Lenser ce1fe33b2e Slicer: Added better documentation on SlicerLayer::Terminus::Index. 2016-07-25 21:40:42 -04:00
Tim Kuipers df3c77d0ce code conventions for commandSocket (CURA-1272) 2016-07-25 17:43:03 +02:00
Tim Kuipers 39e1c5ffd9 fix: set command socket current location when starting printing a meshgroup (CURA-1272) 2016-07-25 17:34:06 +02:00
Tim Kuipers af31f141d8 Merge branch 'layerview_dev' of https://github.com/Johan3DV/CuraEngine into Johan3DV-layerview_dev 2016-07-25 15:53:33 +02:00
Tim Kuipers 5d7687228c test: isinside (CURA-1981) 2016-07-25 11:41:50 +02:00
Tim Kuipers ffb4cdaacd refactor: split off PolygonTest from PolygonUtilsTest (CURA-1981) 2016-07-25 11:35:29 +02:00
Scott Lenser ce10cdecc7 Moved some reference symbols in Slicer to match convention. 2016-07-22 17:53:03 -04:00
Scott Lenser d7806994be Slicer: renamed StitchGridValPointAccess to StitchGridValLocator. 2016-07-22 17:49:32 -04:00
Scott Lenser 6d5762bc59 Slicer: Fixed bug where polylines stitching wouldn't consider joining at start points. 2016-07-22 17:48:12 -04:00
Scott Lenser f96f189d0a Slicer: Improved case when joining polylines via start points. 2016-07-22 17:34:03 -04:00
Scott Lenser d9605a4a53 Slicer: Moved code out of if/else that was common to both cases. 2016-07-22 17:31:46 -04:00
Scott Lenser 0238d1822c Slicer: Comment and formatting improvements. 2016-07-22 17:30:42 -04:00
Scott Lenser d1f38c2be0 Slicer: Documented newer private functions in SlicerLayer. 2016-07-22 16:45:47 -04:00
Scott Lenser 0df427bc7a Slicer: Moved terminus tracking logic into a class. 2016-07-22 15:42:41 -04:00
Scott Lenser 6328aa7210 Slicer: Moved more of connectOpenPolylinesImpl into private functions. 2016-07-22 14:53:54 -04:00
Scott Lenser 2d761096b8 Spacing changes in slicer. 2016-07-22 14:37:12 -04:00
Scott Lenser 1a8b4c6655 Slicer: split off part of connectPolylinesImpl into new function.
Split off the finding possible stitches part into a new private function.
2016-07-22 14:33:55 -04:00
Scott Lenser cdb451b679 Slicer: Moved Terminus and PossibleStitch classes into SlicerLayer.
This moves them from the SlicerLayer::connectPolylinesImpl() function
to the SlicerLayer class.  This results in the declaration movign to
the header file.
2016-07-22 14:24:33 -04:00
Scott Lenser 7aa157c955 Slicer: Made terminus into a class.
This hides the implementation of a polyline index and a flag as an
unsigned int.  This also makes some of the uses more clear because
of the accessor function names.
2016-07-22 14:18:15 -04:00
Scott Lenser 52f71823f4 Removed trailing whitespace in slicer. 2016-07-22 13:31:46 -04:00
Scott Lenser 7522ca3ddc SparseGrid: changed name of PointAccess to Locator. 2016-07-22 13:27:26 -04:00
Scott Lenser b502a74a28 Slicer: switched storage for segments ending in vertex.
This switches the storage for segments ending in a vertex to a vertex *
instead of copying the connected faces of the vertex into a copy
of the vector.
2016-07-22 13:19:43 -04:00
Scott Lenser 3f131e40b9 Added spaces (especially around binary operators) in SparseGrid. 2016-07-22 13:10:09 -04:00
Scott Lenser 9b1a8fab73 Expanded doxygen function comment for SlicerLayer::connectOpenPolylinesImpl. 2016-07-22 12:44:04 -04:00
Scott Lenser 7e5538b65e Improved variable name in slicer. 2016-07-22 12:35:08 -04:00
Scott Lenser 1f60e35bd2 Slicer: Fixed usage of INVALID_TERMINUS value not going through constant. 2016-07-22 12:30:48 -04:00
Scott Lenser 70b3da26b9 Simplified SparseGrid usage in pathOrderOptimizer.
Now using the getNearbyVals() API call which is what the function
really wanted.
2016-07-22 12:26:42 -04:00
Scott Lenser ecdfbd89ed Merge remote-tracking branch 'origin/master' into slicer-speedup-clean 2016-07-22 12:22:15 -04:00
Ghostkeeper 134e52b15f Rename setting skirt_minimal_length to skirt_brim_minimal_length
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 17:03:21 +02:00
Ghostkeeper 5722a3e086 Rename setting skirt_line_width to skirt_brim_line_width
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:55:27 +02:00
Ghostkeeper e6adbd2f1d Rename jerk_skirt to jerk_skirt_brim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:52:24 +02:00
Ghostkeeper 533d00cc3a Rename setting acceleration_skirt to acceleration_skirt_brim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:47:47 +02:00
Ghostkeeper 586e0031d5 Rename skirt_speed to skirt_brim_speed
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:46:44 +02:00
Ghostkeeper 3ab6beb239 Update documentation
Contributes to issue CURA-1678.
2016-07-22 16:35:17 +02:00
Ghostkeeper 1da383d0d0 Make count unsigned
It can't be negative anyway.

Contributes to issue CURA-1678.
2016-07-22 16:33:52 +02:00
Ghostkeeper 479742cea3 Rename skirt_primary_extruder to skirt_brim_primary_extruder in generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:32:42 +02:00
Ghostkeeper e2c206e8ee Rename skirtNr to skirt_brim_number in generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:30:40 +02:00
Ghostkeeper f58c506c6a Rename skirt_sent to skirt_brim_sent in processPlatformAdhesion
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:27:58 +02:00
Ghostkeeper da2a329db4 Rename skirts to skirts_or_brims in generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:26:25 +02:00
Ghostkeeper ecb7ba8904 Rename skirt_polygons to skirt_brim_polygons in generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:21:33 +02:00
Ghostkeeper a84bb21d10 Rename skirt_polygons to skirt_brim_polygons in generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:20:43 +02:00
Ghostkeeper d41c2c6ac2 Rename primary_extruder_skirt_line_width to primary_extruder_skirt_brim_line_width
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 16:18:57 +02:00
Ghostkeeper 73f9b09951 Change feature type comment in g-code SKIRT to SKIRTBRIM
Because it is used not only for skirt but also for brim. This essentially means an API break, though Cura doesn't rely on this. Printers should not rely on this as per the g-code spec. The only things that rely on this are the postprocessing scripts, but it should be a trivial change for them.

Contributes to issue CURA-1678.
2016-07-22 16:16:51 +02:00
Ghostkeeper ac0b15eb93 Rename PrintFeature::Skirt to SkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:48:32 +02:00
Ghostkeeper 27ab0658df Rename skirt.cpp/h to SkirtBrim.cpp/h
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:44:36 +02:00
Ghostkeeper 759eb0ab90 Rename generateSkirt to generateSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:40:35 +02:00
Ghostkeeper 2099302a3b Update skirt/brim documentation
Contributes to issue CURA-1678.
2016-07-22 15:35:50 +02:00
Ghostkeeper 8365cb070d Rename skirt_is_processed to skirt_brim_is_processed
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:32:02 +02:00
Ghostkeeper 4121b3da1e Rename processSkirt to proceesSkirtBrim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:28:36 +02:00
Ghostkeeper 931df895c6 Rename skirt to skirt_brim in processSkirt
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:25:14 +02:00
Ghostkeeper 5b20d02c11 Rename SliceDataStorage::skirt to skirt_brim
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:18:47 +02:00
Ghostkeeper a702fa711d Rename initializeSkirtConfigs to initializeSkirtBrimConfigs
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:06:30 +02:00
Ghostkeeper eeb741bfb6 Rename SliceDataStorage::skirt_config to skirt_brim_config
Because it concerns not only the skirt, but also the brim.

Contributes to issue CURA-1678.
2016-07-22 15:01:21 +02:00
Ghostkeeper 5942bb3d4c Only generate support roof below mesh
Support roof takes quite a lot of time to print, so only generate it below the actual mesh, not where the support area is X/Y expanded or where multiple pieces of support are joined and such.

Contributes to issue CURA-647.
2016-07-22 14:40:20 +02:00
Ghostkeeper dd3007a61e Load support roof height and layer height from mesh
Don't have to pass it through via parameters.

Contributes to issue CURA-647.
2016-07-22 14:23:37 +02:00
Ghostkeeper 0e0119a3e6 Make parameters const and unsigned
Negative is not possible anyway. And these are not changed.

Contributes to issue CURA-647.
2016-07-22 13:49:43 +02:00
Ghostkeeper ef38ed3688 Rename variables according to code style
We use lowercase with underscores.

Contributes to issue CURA-647.
2016-07-22 13:47:17 +02:00
Ghostkeeper 142464ebeb Round support Z-distance up instead of down
The z-distance is rounded to the next layer. This guarantees that there is at least the specified Z-distance.

Contributes to issue CURA-1283.
2016-07-22 13:31:26 +02:00
Ghostkeeper 1d54240c32 Make retraction config take extruder of current plan
Rather than taking the extruder of the current g-code, which is for an entire layer, take the retraction settings of the current extruder plan. This allows for different retraction settings on the same layer rather than just taking the retraction settings that layer begins with.

Contributes to issue CURA-1972.
2016-07-22 11:52:07 +02:00
Johan K 25384faeed Merge branch 'master' of https://github.com/Ultimaker/CuraEngine into layerview_dev 2016-07-22 10:36:34 +02:00
Johan K 610685e13b Removing layer_nr argument from CommandSocket::send* functions. 2016-07-22 03:08:59 +02:00
Johan K ae59b2b8d8 Clean up the interface functions for sending data to the layer view in GUI.
Functions are now static and internally check in the command socket is instansiated to make the using code cleaner.
The sendLine function has been renamed to sendLineTo and only take the destination point to match how data is used and written to gcode.
CURA-1272
2016-07-22 02:29:42 +02:00
Ghostkeeper fe184f9177 Always make tests select from arrays
In some cases it was selecting characters from a string and passed those as a string. That's wrong!

Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Ghostkeeper bce51f4807 Add setting types polygon and polygons
It just evaluates the default. It doesn't try to edit these.

Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Ghostkeeper e371c43395 Don't evaluate default_value as fallback
Just take the value itself without evaluating it. Otherwise enum values accidentally get evaluated as a variable.

Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Ghostkeeper b3f80cec83 Repair evaluation functions
The names of these properties have changed a bit. Also, we're now getting the normal value first and defaulting back to default_value if there is no inheritance functions.

Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Ghostkeeper e34f2ffc67 Remove commented code
Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Ghostkeeper 0f0bd3c32c Remove redundant parentheses
As per the code style.

Contributes to issue CURA-1758.
2016-07-21 18:27:49 +02:00
Tim Kuipers 71beb1cc98 fix: no support for infill meshes (CURA-833) 2016-07-21 15:45:07 +02:00
Tim Kuipers bc56e73e36 Merge branch 'feature_remove_extruder_width' 2016-07-21 15:32:40 +02:00
Ghostkeeper ea364c4cd8 Obtain combined infill line width per layer
If the line width is different per combined layer this would get the correct one, and it is also more robust if there are 0 layers in the vector.

Contributes to issue CURA-736.
2016-07-21 13:54:01 +02:00
Ghostkeeper d581957308 Rename primary_skirt_line_width to primary_extruder_skirt_line_width
This was deemed better by the reviewer.

Contributes to issue CURA-736.
2016-07-21 13:36:25 +02:00
Ghostkeeper 11c611c9e4 Restore support for negative conical support angles
Negative conical support angles are also allowed. Only when the support angle is exactly zero, it is nonsensical to generate conical support.

Contributes to issue CURA-736.
2016-07-21 13:32:23 +02:00
Tim Kuipers 5ab937ba03 indent fix only (CURA-833)
only indentation and a lot of bs diff
2016-07-21 11:27:06 +02:00
Tim Kuipers aa5c9d37fd fix: command line slicing was broken (CURA-833) 2016-07-21 11:26:19 +02:00
Tim Kuipers 9de3edee39 debug fix: better debug output of clipper bug 2016-07-21 11:10:43 +02:00
Ghostkeeper 21fa313feb Move support line width into detect overhang points
It's the only place where it's used. This makes the stack trace slimmer, potentially making the program faster. Also, it moves the dependency on the setting closer to where the setting is used.

Contributes to issue CURA-736.
2016-07-21 09:31:00 +02:00
Ghostkeeper 6fb61a3c79 Make a few settings const
While we're at it...

Contributes to issue CURA-736.
2016-07-21 09:24:57 +02:00
Ghostkeeper bc2efcfc39 Rename extrusionWidth to line_width
This line width is the width of the lines in the wireframe mode.

Contributes to issue CURA-736.
2016-07-21 09:19:59 +02:00
Ghostkeeper cb40a72eda Rename extrusionWidth to line_width
This is the width of all wireframe lines.

Contributes to issue CURA-736.
2016-07-20 17:14:26 +02:00
Ghostkeeper 096e8f7675 Remove old code
This prime tower is long unused. The queue import also seems to be unused, since compiling gives no error and a CTRL+F has no results either.

Contributes to issue CURA-736.
2016-07-20 17:10:17 +02:00
Ghostkeeper 87e02722e7 Rename primary_extrusion_width to primary_skirt_line_width
Sometimes it is the brim line width. But this convention is held elsewhere throughout the code too.

Contributes to issue CURA-736.
2016-07-20 17:07:24 +02:00
Ghostkeeper d15387905b Rename innermost_wall_extrusion_width to innermost_wall_line_width
Neighbouring variables are also renamed, such as extrusionWidth that was actually the wall line width.

Contributes to issue CURA-736.
2016-07-20 16:58:50 +02:00
Ghostkeeper 5280c9df10 Correct skin-wall to skin overlap
The skin-wall now has the line width of the inner walls.

Contributes to issue CURA-736.
2016-07-20 16:28:13 +02:00
Ghostkeeper 021c7ac0bf Use inner wall config to extrude skin border lines
This makes the line width with which we're extruding equal to the line width with which we did the insets for these lines.

Contributes to issue CURA-736.
2016-07-20 16:25:00 +02:00
Ghostkeeper 3825e200a6 Make skin insets use inner wall line width
We deemed that the inner wall line width is more akin to the line width you're seeking for the border line of skins, which attaches slin lines that end on the border with infill. More akin than skin line width anyway.

Contributes to issue CURA-736.
2016-07-20 15:39:22 +02:00
Ghostkeeper 6022e44dee Rename extrusion_width to support_line_width
Also made it const.

Contributes to issue CURA-736.
2016-07-20 15:11:51 +02:00
Ghostkeeper b0054e1b39 Move getting skin line width into skin generator
This makes the stack lighter, because the context switch to the functions doesn't need to copy the value over to the stack. Also removes the reference to old terminology: extrusion_width, and makes the variable const.

Contributes to issue CURA-736.
2016-07-20 15:09:51 +02:00
Ghostkeeper 7a1ee0429e Move getting infill line width into infill functions
This makes the stack lighter, because the context switch to the functions doesn't need to copy the value over to the stack. The local variable can be an order of magnitude faster even though it needs to look-up the value from the mesh one additional time. Also removes the reference to old terminology: extrusion_width, and makes the variable const.

Contributes to issue CURA-736.
2016-07-20 15:03:05 +02:00
Johan K fddaa58d67 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine into layerview_dev 2016-07-20 14:49:24 +02:00
Ghostkeeper 0ff87b785f Split speed_layer_0 into two settings
There are now distinct settings for speed_print_layer_0 (the extrusion moves) and speed_travel_layer_0 (the non-extrusion moves).

Contributes to issue CURA-1507.
2016-07-20 10:54:55 +02:00
Ghostkeeper 71df0e2586 Initial layer travel speed uses initial layer config
It uses the same gradient as the rest of the speeds. This is per extruder though.

Contributes to issue CURA-1507.
2016-07-19 14:49:00 +02:00
Ghostkeeper 53c8cc5e78 Document initialLayersSetup()
Better structured, really.

Contributes to issue CURA-1507.
2016-07-19 14:41:00 +02:00
Tim Kuipers aa87a720c0 refactor: code conventions (CURA-1856) 2016-07-19 13:42:25 +02:00
Tim Kuipers 6e171f795c Merge branch 'scottlenser-add-convex-hull' 2016-07-19 13:32:22 +02:00
Tim Kuipers e76cda3ea8 Merge branch 'add-convex-hull' of https://github.com/scottlenser/CuraEngine into scottlenser-add-convex-hull 2016-07-19 13:23:58 +02:00
Ghostkeeper 8794688c48 Merge branch 'feature_tetrahedral_infill' 2016-07-19 09:28:25 +02:00
Tim Kuipers 957c82715d fix: use print_layer_count everywhere after infill meshes have been generated/confined (CURA-833) 2016-07-18 18:06:37 +02:00
Tim Kuipers c0813b2c77 fix: send layer info after processing infill meshes (CURA-833) 2016-07-18 18:04:28 +02:00
Tim Kuipers 3042483f73 update mesh.layer_nr_max_filled_layer during processing infill meshes (CURA-833) 2016-07-18 18:00:52 +02:00
Tim Kuipers 3abc718240 fix: reserve enough layers in the vector (speedup) (CURA-833) 2016-07-18 18:00:09 +02:00
Tim Kuipers a52208b15e refactor: rename layer_count ==> slice_layer_count because infill meshes may be sliced at layers which aren't printed (CURA-833) 2016-07-18 17:58:53 +02:00
Ghostkeeper edd44de9e3 Spelling fixes
Contributes to issue CURA-1925.
2016-07-18 17:17:59 +02:00
Tim Kuipers d4b1c70790 fix: limit infill meshes to the own infill area of other meshes rather than their basic infill area (CURA-833)
second order infill mehses could overlap with first order infill meshes before
2016-07-18 15:54:40 +02:00
Tim Kuipers 534e265fd2 refactor: factored out a new function getOwnInfillArea whic returns the infill_area_own or the infill_area (CURA-833) 2016-07-18 15:53:27 +02:00
Tim Kuipers 120de96bb5 fix: optional assignment used wrong constructor (CURA-833) 2016-07-18 14:47:13 +02:00
Tim Kuipers a611739ff7 Merge branch 'master' into feature_gradual_infill 2016-07-18 12:20:16 +02:00
Tim Kuipers 791e436222 fix: only first infill mesh part got removed from infill of normal mesh (for gradual infill branch) (CURA-833) 2016-07-18 12:17:10 +02:00
Tim Kuipers d16f52e776 fix: only first infill mehs part got removed from infill of normal mesh (CURA-833) 2016-07-18 12:14:45 +02:00
Tim Kuipers a99020e995 Merge branch 'master' into feature_gradual_infill 2016-07-18 11:57:50 +02:00
Tim Kuipers 5237b1bb01 fix: made raft top layers into zigzag (CURA-1637)
previous commit was actually for the middle layers instead of the top layers
2016-07-15 15:34:25 +02:00
Tim Kuipers 7e0d44cfcb fix: combing thought it was never inside when printing the raft (CURA-1637) 2016-07-15 15:31:11 +02:00
Tim Kuipers 453b3ebbc5 fix: made raft top layers into zigzag (CURA-1637) 2016-07-15 15:10:07 +02:00
Tim Kuipers 473b925814 feat/fix: changed mat GUID to string and retrieve it from settings (CURA-1836) 2016-07-15 14:27:57 +02:00
Tim Kuipers 4199fcf8ac feat/fix: changed mat GUID to string and retrieve it from settings (CURA-1836) 2016-07-15 14:27:24 +02:00
Tim Kuipers 203f04571c fix: delete instance when de-assigning an optional value (CURA-836) 2016-07-15 13:45:45 +02:00
Tim Kuipers 186472c26e feat: tetrahedral infill (CURA-1925) 2016-07-15 12:35:48 +02:00
Tim Kuipers dc2b505274 fix: combination of gradual infill and infill mehses didn't work (CURA-836)
The part.infill_area should remain the whole initial infill area for combing
During the generation of the part.infill_area_per_combine_per_density it refered to part.infill_area, rather than the infill-mesh-limited infill
I had to introduce an intermediate result - part.infill_area_own - which records the area where that mesh should print its infill pattern
In case there are no infill meshes the part.infill_area_own remains a nullptr, so that we don't waste resources.
After it's used and incorporated into infill_area_per_combine_per_density, its destroyed.
2016-07-14 17:57:16 +02:00
Tim Kuipers 9adc0a128c fix: optional::operator=(nullptr) didn't work (CURA-836) 2016-07-14 17:51:12 +02:00
Tim Kuipers e6a5eccf03 Merge branch 'master' into feature_gradual_infill 2016-07-14 16:29:28 +02:00
Johan K 71ea91b61b Implement adding extruder number to path compiler 2016-07-14 12:13:14 +02:00
Tim Kuipers 00d6a55836 lil: less whitespace at end of lines 2016-07-14 12:12:25 +02:00
Tim Kuipers 4308c74451 fix: multiVolumesOverlap failed when two meshes are directly adjacent in the X direction (CURA-833)
when there was no X which covered both models
2016-07-14 12:05:17 +02:00
Tim Kuipers 757df8c7c8 fix: let combing not see infill meshes (CURA-833) 2016-07-14 11:39:38 +02:00
Tim Kuipers ff5cc9399d refactor: lil rename (CURA-833) 2016-07-14 11:35:46 +02:00
Johan K ddf7bbe06e Changed the data type of the layer visualization data from int64 to float.
Also added extruder number and point dimensionality to the message.
2016-07-14 11:00:39 +02:00
Tim Kuipers e34608442a doc: processInfillMesh (CURA-833) 2016-07-14 10:50:37 +02:00
Tim Kuipers c0446f81e0 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-13 17:30:13 +02:00
Ghostkeeper 27ac98caa5 Code style: Whitespace around binary operators
Contributes to issue CURA-833.
2016-07-13 15:47:37 +02:00
Ghostkeeper ebcfad8200 Codestyle: Whitespace around binary operators
Contributes to issue CURA-833.
2016-07-13 15:45:52 +02:00
Ghostkeeper 69665a0d4a Codestyle: Space after comma and no whitespace at end of line
Contributes to issue CURA-833.
2016-07-13 15:13:21 +02:00
Johan K a50d6fe5a9 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine into layerview_dev 2016-07-13 14:52:11 +02:00
Ghostkeeper bed556a5e6 Codestyle: Brackets after if-statements
Instead of making new brackets after every if-statement, I opted for making it into or-clauses.

Contributes to issue CURA-833.
2016-07-13 14:48:53 +02:00
Tim Kuipers a86c5b1105 doc: documented ExtruderPlan 2016-07-13 14:34:05 +02:00
Tim Kuipers 3db1c2580f Merge branch 'feature_gradual_infill' of github.com:Ultimaker/CuraEngine into feature_gradual_infill 2016-07-13 14:19:22 +02:00
Tim Kuipers 2925af497c lil doc 2016-07-13 14:17:07 +02:00
Johan K 0bd5ae9e01 Added documentaion
More documentation for PathCompiler and SliceDataStruct.
2016-07-13 14:03:46 +02:00
Tim Kuipers eff7f0e326 fix: always retract after priming (CURA-1816) 2016-07-13 11:09:12 +02:00
Scott Lenser a6ad786443 Cleaned up size handling in convexHull. 2016-07-12 10:08:35 -04:00
Scott Lenser ce85280971 Merge remote-tracking branch 'origin/master' into slicer-speedup-clean 2016-07-12 09:13:41 -04:00
Tim Kuipers a7bc153db5 Merge branch 'feature_overhang_dropdown' 2016-07-12 11:25:20 +02:00
Tim Kuipers 3d98bb7e74 Merge branch 'feature_firmware_travel_settings' 2016-07-12 11:06:56 +02:00
Tim Kuipers cc632cd201 Merge branch 'master' of github.com:Ultimaker/CuraEngine into feature_fan_control_per_extruder 2016-07-12 11:02:31 +02:00
Tim Kuipers b0ee1ca57d Merge branch 'scottlenser-slenser-cleanup-pr-A' 2016-07-12 10:35:10 +02:00
Tim Kuipers e7992fc17a fix: USE_CPU_TIME iff in debug mode (CURA-1856) 2016-07-12 10:28:02 +02:00
Tim Kuipers 3f2162d76c doc: better documentation and function naming (CURA-1856) 2016-07-12 10:23:48 +02:00
Johan K 019116cd33 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine into layerview_dev 2016-07-12 01:03:19 +02:00
Johan K b0f725c1e5 Added extruder nr to PathSegment definition in Cura.proto 2016-07-12 01:02:24 +02:00
Tim Kuipers 17c125d395 fixed header guard of WallsComputation 2016-07-11 22:29:17 +02:00
Tim Kuipers d0d0532cb8 Merge branch 'slenser-cleanup-pr-A' of https://github.com/scottlenser/CuraEngine into scottlenser-slenser-cleanup-pr-A 2016-07-11 18:35:31 +02:00
Tim Kuipers 563be4980d doc: visual explanation of gradual infill shift and line dist (CURA-836) 2016-07-11 18:10:29 +02:00
Tim Kuipers 4a91663bb4 fix: gradual infill steps are skipped through regularly now (CURA-836)
whenb computing gradual infill, we skip layersto compute which regions count as top. These skips are now such that
you end exactly one gradual infill step above.
2016-07-11 17:44:30 +02:00
Tim Kuipers f486d6be6f bugfix: sqrt doesn't return a constexpr (CURA-833) 2016-07-11 16:34:21 +02:00
Tim Kuipers 945711f10a bugfix: sqrt doesn't return a constexpr (CURA-833) 2016-07-11 16:27:36 +02:00
Tim Kuipers 0ae670ce0c Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-11 16:16:07 +02:00
Tim Kuipers 2db4730f00 lil: removed done TODO CURA-833 2016-07-11 16:04:25 +02:00
Jaime van Kessel 09b36ecdd8 Merge branch 'feature_cubic_isometric_infill' of github.com:Ultimaker/CuraEngine 2016-07-11 14:41:08 +02:00
Tim Kuipers 5e83d4252d fix: handle inactive temp of second nozzle on first layer (CURA-1508) 2016-07-11 13:37:08 +02:00
Tim Kuipers a69270e212 fix/refactor: factored writePrimeTrain out of startExtruder so that we can enforce printing temperature before priming (CURA-1508) 2016-07-11 13:14:48 +02:00
Ghostkeeper d197c7f3d7 Fix spelling in documentation
Contributes to issue CURA-836 and a few others probably.
2016-07-11 10:26:35 +02:00
Tim Kuipers c82a8c5a47 fix: warning msg bug: was string instead of Cstring (CURA-1568) 2016-07-11 10:22:42 +02:00
Scott Lenser 288f053abd Updated mentions of BucketGrid in comments to SparseGrid.
Some of the comments had mentions of BucketGrid were the code had
already been updated to change BucketGrid2D uses to SparseGrid.
I didn't notice them before because of the different spelling.
2016-07-09 12:48:13 -04:00
Scott Lenser f73586190e Fixed warning message about unused variable. 2016-07-08 19:53:03 -04:00
Scott Lenser 0fd0dd6869 Removed BucketGrid2D which has been replaced by SparseGrid. 2016-07-08 19:24:14 -04:00
Scott Lenser dee0ae12d6 Converted polygonUtils to SparseGrid from BucketGrid2D.
This also converts Comb which relies on polygonUtils (Comb just stores
the type from polygonUtils).
2016-07-08 19:21:44 -04:00
Scott Lenser c2571d41af Converted pathOrderOptimizer to SparseGrid from BucketGrid2D. 2016-07-08 18:39:45 -04:00
Scott Lenser c8fd58376b Converted BucketGrid2D test to SparseGrid test. 2016-07-08 17:53:44 -04:00
Scott Lenser bce0a8a3a8 Added parameters to SparseGrid constructor to allow more control of map.
This adds functionality that was present in BucketGrid2D's constructors.
2016-07-08 16:59:26 -04:00
Scott Lenser 25c2f7086d Moved SparseGrid::processNearby into public interface. 2016-07-08 16:44:49 -04:00
Scott Lenser 941f4e3a66 Added getNearest() function to SparseGrid.
Refactored so that getNearest and getNearby can share a lot of the
implementation.
2016-07-08 16:42:15 -04:00
Scott Lenser 6d8d453df9 Added missing doxygen comment for SparseGrid. 2016-07-08 15:35:33 -04:00
Scott Lenser 90c212b52e Split SparseGrid into an invasive and non-invasive version.
The invansive version requires that the stored type being able to
provide a Point location on demand.  This is a general version in
case the type being stored already has a location in it.  The
non-invasive version is more convenient if the type to be stored
does not already contain a Point location.
2016-07-08 15:32:32 -04:00
Scott Lenser db79eb7ece Added convexHull function for Polygons.
This renames the previous version which is an approximate convex hull
to approxConvexHull.
2016-07-08 13:29:50 -04:00
Scott Lenser 9afcbddf29 Added rounding to coordinates from slicing of mesh. 2016-07-08 13:26:21 -04:00
Scott Lenser c4c221145e More const correctness. 2016-07-08 13:24:56 -04:00
Scott Lenser 1956fc00d3 Added error message if inherited JSON file can't be found. 2016-07-08 13:16:56 -04:00
Scott Lenser 8ac6c3b597 Changed signature of Mesh::getFaceIdxWithPoints to avoid redundant calculation. 2016-07-08 13:16:00 -04:00
Scott Lenser 76b3c8757d Fixed bug which sent multiple finished slice messages.
The slicing finished message is already sent in CommandSocket::connect().
This confused the GUI since it is only expecting 1 finished slice message.
2016-07-08 13:11:52 -04:00
Scott Lenser c6b6688170 Added code to use cpu time instead of wall clock time for timings.
This is controlled by a #define on which timings to use.
2016-07-08 13:10:18 -04:00
Scott Lenser d2c80127ef Fixed bug in print out about number of layers. 2016-07-08 13:09:19 -04:00
Scott Lenser 3ada6f266c Switched command_line_settings to version 2 from version 1. 2016-07-08 13:03:08 -04:00
Scott Lenser 4b5553019e Fixed bug in bridge generation.
It was calculating the angle to use for infill but then not using it.
This makes it use the calculated angle.  This makes the bridge
generation go from basically completely non-functional to just mostly
non-functional.
2016-07-08 12:59:41 -04:00
Scott Lenser 4fd499f7a1 Correctness improvements to SlicerLayer::makeBasicPolygonLoop.
From the slicing, we know which edge generated the end point of the
segment.  We then know that the face that shares this edge is the only
candidate to have generated the next segment.  This avoids checking
the other 2 segments.

Improved logic used in SlicerLayer::getNextSegmentIdx().  The previous
version only considered candidate faces for extending the current
polygon loop by looking at faces that shared an edge.  This works for
many models, but if the slice height exactly intersects vertices of
the mesh, then the segment that would extend the loop is not typically
found.  We know whether we hit a vertex with the slice from the
slicing calculation.  There is a slight cost to checking for equality
to see if we hit a vertex.  This can very slightly slow down normal
models but substantially increase speed on bad case models.  This
extends the logic to use the faces connected to the vertex that the
segment ended on to try to extend the polygon loop.  Theoretically,
this adds a slight performance penalty to makeBasicPolygonLoops when
vertices were hit by the slice.  However, in very limited testing,
this extra time is not measurable.  Adding this improved correctness
makes a big difference in the number of polygon loops successfully
constructed.  In a bad case model, the number of polygons found
increased from 0 to 19 and the number of open polylines for the next
stage of processing went from 6357 to 0.  This made the
connectOpenPolylines() go from taking .048 seconds to 0.  The overall
effect on this model was to reduce the slicing time from 0.084 to 0.04
seconds (this is a small model for testing so total times are low).
2016-07-08 12:50:03 -04:00
Scott Lenser 28aedf57c5 Sped up SlicerLayer::connectOpenPolylines and SlicerLayer::stitch.
Added use of spatial data structure in SlicerLayer::stitch().  Added
SparseGrid spatial data structure.  This is similar to BucketGrid2D
but a bit more general and without the possibility of duplication in
returned points.  SparseGrid does not offer a findNearestObject
function like BucketGrid2D does, although one could be added if
desired.

Sped up SlicerLayer::stit() further by only finding nearby points
once.  The possible stitch points are calculated once and remembered.
They are put into a priority queue.  A bidirectional map (via vectors)
is used to keep track of the transforms that happen to the polygons to
map from the original polyline ends to the new polyline ends.

Combined stitch and connectOpenPolylines implementations.
connectOpenPolylines basically does the same thing as stitch except
has a smaller search radius and doesn't allow for inverting edges.  I
expressed these as parameters to the stitch implementation and then
reused the implementation.  It is now much faster.
connectOpenPolylines has gone from O(n^2) to O(n) and stitch has gone
from O(n^3) to O(n).  connectOpenPolylines has also gotten more
correct.  With the original version,if polys 0 -> 10 -> 5 formed a
chain, then 0 -> 10 would be merged into 0 but the 10 -> 5 would never
be used.  0 -> 10 would be merged into 0, but this new 0 would not be
considered for merging with 5 since 10 > 5.  It also wouldn't be
considered for merging 10 with 5 because 10 has already been cleared
out.
2016-07-08 12:34:47 -04:00
Scott Lenser 28e6a54961 Added timer for time to load mesh. 2016-07-08 12:06:06 -04:00
Scott Lenser b1b49f16e0 Added timers in slicer.
This gives the breakdown between intersecting planes with the model
and the conversion of the resulting segments into polygons.
2016-07-08 12:02:17 -04:00
Scott Lenser 6b72d52c65 Added new spatial data structure SparseGrid.
This class is intended to be used for speeding up fixed radius
nearby neighbor searches.
2016-07-08 11:48:41 -04:00
Scott Lenser 07d416691a Add move version of adding Polygon to Polygons. 2016-07-08 11:43:15 -04:00
Scott Lenser 960c56a9fa Added a convenience typedef for the type of a coordinate in a Point. 2016-07-08 11:33:44 -04:00
Scott Lenser a18b05f347 Fix to make bridging work much better. 2016-07-08 14:22:45 +02:00
Tim Kuipers 4d46fbea8f fix: conical overhang wouldn't do anything for an angle of zero (CURA-1412)
It should then drop any overhang down: \ becomes L
2016-07-08 00:00:48 +02:00
Tim Kuipers 5620abfd95 fix: make infill mesh AABB collision detection take into account horizontal expansion (CURA-833) 2016-07-07 22:17:41 +02:00
Tim Kuipers 3d307112de fix: infill meshes couldn't handle zero infill meshes (CURA-833)
zero infill meshes didn't generate any infill areas, while infill mehses assumed there to be infill areas_per_combine
2016-07-07 22:07:39 +02:00
Tim Kuipers 2c5639097e fix: got a warning about an uninitializes unused variable 2016-07-07 20:47:44 +02:00
Tim Kuipers cbf5d962b7 fix: catch temp flow graph reading input errors (CURA-1792) 2016-07-07 20:42:18 +02:00
Tim Kuipers 744264481a ignore callgrind files 2016-07-07 20:09:52 +02:00
Tim Kuipers 2cd22bdfd5 Merge branch 'master' into feature_fan_control_per_extruder 2016-07-07 17:33:35 +02:00
Tim Kuipers c259d90def fix: extruder switched to old extruder instead of new one (CURA-1816 and CURA-1508) 2016-07-07 17:33:21 +02:00
Tim Kuipers a7b3d53872 Merge branch 'master' into feature_fan_control_per_extruder 2016-07-07 16:50:21 +02:00
Tim Kuipers 57219beb98 fix: nozzle switch was never written (CURA-1508 CURA-1816) 2016-07-07 16:49:31 +02:00
Tim Kuipers 8f0c3c9a49 Merge branch 'feature_fan_control_per_extruder' of github.com:Ultimaker/CuraEngine into feature_fan_control_per_extruder 2016-07-07 16:47:57 +02:00
Tim Kuipers 6adfdd7ad4 Merge branch 'master' into feature_fan_control_per_extruder 2016-07-07 16:42:44 +02:00
Tim Kuipers 59b516702e Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-07 15:19:08 +02:00
Tim Kuipers 78c155b1ab fix: only perform prime gcode for griffin (CURA-1816 CURA-1508) 2016-07-07 15:18:25 +02:00
Tim Kuipers f39a3e62e7 fix: only prime the initial extruder at the start of a print (CURA-1508) 2016-07-07 15:14:24 +02:00
Tim Kuipers 00bcbe7192 fix: get prime location from settings and use travel speed to get there when switching to a yet unused extruder (CURA-1816 and CURA-1508) 2016-07-07 15:13:37 +02:00
Tim Kuipers ab850577b5 fix: initial planned extruder was always set to zero (CURA-1816) 2016-07-07 15:10:45 +02:00
Tim Kuipers 307551b1db fix: skirt assumed extruder 0 for primary adhesion extruder rather than adhesion_extruder_nr (CURA-1816) 2016-07-07 13:33:25 +02:00
Tim Kuipers 9a09cdc18a feat/refactor: factored out startExtruder from GCodeExport::switchExtruder (CURA-1816) 2016-07-07 13:25:28 +02:00
Tim Kuipers dfcc993034 refactor: replaced old SliceDataStorage::getExtrudersUsed(.) with GcodeExport::getExtruderIsUsed (CURA-1816) 2016-07-07 13:24:10 +02:00
Ghostkeeper 65df6439fa Codestyle: Spaces around operators
Contributes to issue CURA-1792.
2016-07-07 11:43:22 +02:00
Jaime van Kessel 43a51f5757 Changed extruder_nr to int (instead of uint)
CURA-1687
2016-07-06 15:31:38 +02:00
Tim Kuipers 96a2ac56c5 fix: don't give warnings for known parent settings (secretly on CURA-1792)
the frontend sends all known settings (and categories) via the command socket even though it should know which settings will be used by the engine.
This is because the json file is the interface ofthe engine and so the frontend sends everything in the interface.
2016-07-06 15:15:26 +02:00
Ghostkeeper 63cd06ca02 Fix typo
Contributes to issue CURA-1568.
2016-07-06 14:59:00 +02:00
Tim Kuipers 39da22630f bugfix: PolygonUtils::findClose(... BucketGrid2D) return pointer to heap resulting in mem leaks (CURA-1792)
now solved with optional
2016-07-06 14:47:19 +02:00
Ghostkeeper 3528b9eca7 Remove TODO
We don't need to do this. Discussed with Tim and Paul.

Contributes to issue CURA-1568.
2016-07-06 14:42:33 +02:00
Tim Kuipers 8d7827aa87 fix: parsing of temp flow graph was buggy ==> rewrite (CURA-1792) 2016-07-06 14:35:38 +02:00
Tim Kuipers c73084bbd1 fix: instantiate command socket after loading the json file (CURA-1792)| 2016-07-06 14:31:24 +02:00
Tim Kuipers bf372cb3aa fix: merge conflic resolution for fan speeds per extruder (CURA-1568)
The ExtruderPlan needed access to the retraction_config for that extruder
2016-07-05 17:44:04 +02:00
Tim Kuipers 0b8c30616a Merge branch 'master' into feature_overhang_dropdown 2016-07-05 15:22:22 +02:00
Tim Kuipers a7720bcfbf Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-04 17:20:14 +02:00
Tim Kuipers bf19300def fix: raft outline now uses rounded offset (CURA-1835) 2016-07-04 17:19:59 +02:00
Jaime van Kessel 9a34e09806 Only send material estimates for extruders a machine has.
CURA-1687
2016-07-04 16:51:43 +02:00
Jaime van Kessel 62a90f86c6 Merge branch 'master' of github.com:Ultimaker/CuraEngine into SendMaterialEstimatesOfBothExtrudersToFrontend 2016-07-04 16:17:47 +02:00
Tim Kuipers f3b6042243 fix: fixing output stream precision when outputting to standard out 2016-07-04 13:54:48 +02:00
Tim Kuipers 795d68d142 fix runtest.py: new setting attributes after settings rework (CURA-1828) 2016-07-04 12:26:10 +02:00
Tim Kuipers c8e9091764 Merge branch 'feature_retraction_settings_per_extruder' 2016-07-04 12:02:26 +02:00
Tim Kuipers 4b43c96cbc Merge branch 'feature_zhop_over_other_material' 2016-07-04 11:59:55 +02:00
Tim Kuipers 4ba0ae104e Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-04 11:55:55 +02:00
Tim Kuipers f99c8b86cc fix runtest.py: new setting attributes after settings rework (CURA-1828) 2016-07-04 11:55:32 +02:00
Tim Kuipers 5c7862287f fix runtest.py: setting category structure after settings rework (CURA-1828) 2016-07-04 11:55:02 +02:00
Tim Kuipers a2c8311c68 fix/feat: gradual infill: pass settings through from frontend and choose better hardcoded values (CURA-836) 2016-07-01 22:27:25 +02:00
Tim Kuipers 018f952a67 fix: linear based infill scaline algorithm was broken (CURA-836) 2016-07-01 21:39:01 +02:00
Tim Kuipers b05928fb52 bugfix: infill shifting could result in sigaborts (CURA-836, CURA-1723) 2016-07-01 21:38:53 +02:00
Tim Kuipers 8b5f2f8ff8 fix/feat: gradual infill now also working for infil thickness aka infill combine (CURA-836) 2016-07-01 21:36:40 +02:00
Tim Kuipers 50d92ff91e fix: linear based infill scaline algorithm was broken (CURA-836) 2016-07-01 17:41:01 +02:00
Jaime van Kessel e3c0439575 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-07-01 17:22:48 +02:00
Jaime van Kessel a0f624992d Merge branch 'feature_time_estimates_per_layer' of github.com:Ultimaker/CuraEngine 2016-07-01 17:09:42 +02:00
Tim Kuipers a41a0285f0 lil: removed unused var (CURA-836) 2016-07-01 16:51:55 +02:00
Tim Kuipers dbeab1ec05 fix: gradual infill logic was the wrong way around (CURA-836) 2016-07-01 16:51:25 +02:00
Tim Kuipers b81b956965 bugfix: infill shifting could result in sigaborts (CURA-836, CURA-1723) 2016-07-01 14:54:06 +02:00
Tim Kuipers db2462882c feat: Infill no accepts extra parameter for etra_infill_shift (CURA-836) 2016-07-01 14:09:34 +02:00
Tim Kuipers cc0f0918d3 feat/fix: generate infill lines for gradual infill (CURA-836) 2016-07-01 13:59:43 +02:00
Tim Kuipers 3d93407b61 feat/fix: SkinInfillAreaComputation::generateGradualInfill (CURA-836) 2016-07-01 13:37:02 +02:00
Tim Kuipers ead062757a refactor: part.infill_area_per_combine ==> part.infill_area_per_combine_per_density (CURA-836)
from vector of polygons to vector of vectors of polygons
2016-07-01 11:47:17 +02:00
Tim Kuipers 35b1a42cf3 fix: moved cubic shift computation into a static double (CURA-1723) 2016-07-01 01:37:04 +02:00
Tim Kuipers 420508099a feat: cubic isometric infill computation (CURA-1723) 2016-07-01 00:36:43 +02:00
Tim Kuipers 7d1586ec4c refactor: pass down z height into Infill computation (CURA-1723) 2016-07-01 00:33:09 +02:00
Tim Kuipers cc40088b3e feat: shift linear based infill types (CURA-1723) 2016-06-30 23:47:11 +02:00
Tim Kuipers e28f64d36d fix: removed unused safe_outline_offset (remove overlapping wall parts) (CURA-1723) 2016-06-30 23:10:51 +02:00
Tim Kuipers 0357102399 feat: dont heat up unused extruder (CURA-1508) 2016-06-30 18:01:10 +02:00
Tim Kuipers bdd1a5c4ed bugfix: bs init of extruder_attr.is_used (CURA-1508) 2016-06-30 17:38:44 +02:00
Tim Kuipers d9b47a8c96 refactor: lil rename n ==> extruder_nr (CURA-1508) 2016-06-30 17:27:45 +02:00
Tim Kuipers 507aa1c9d9 fix: initialize gcodeExport::extr_attr::is_used (CURA-1508) 2016-06-30 17:26:44 +02:00
Tim Kuipers c2a0dd952c feat: gcodeExport::extr_attr::is_used (CURA-1508) 2016-06-30 17:26:24 +02:00
Tim Kuipers edc4e3e57b refactor: introduce cpp file for ExtruderTrain (CURA-1508) 2016-06-30 17:07:52 +02:00
Tim Kuipers cbf7960e08 lil codestyle fix 2016-06-30 10:55:36 +02:00
Javier Lechuga 74e52455fa SendMaterialEstimatesOfBothExtrudersToFrontend. Including sendPrintTimeMaterialEstimates in protobuf scheme. CURA-1687 2016-06-30 09:49:46 +02:00
Javier Lechuga fc7a60b34a SendMaterialEstimatesOfBothExtrudersToFrontend. First commit not ready yet. CURA-1687 2016-06-30 09:49:45 +02:00
Tim Kuipers 6fa82e044b fix: skin areas were always wrong (CURA-1773) 2016-06-29 17:59:05 +02:00
Tim Kuipers 8dbfb70c67 fix: retrieve machine_min_cool_heat_time_window per extruder (CURA-1783) 2016-06-29 14:58:37 +02:00
Tim Kuipers 513d1347ca feat: retrieve minimal cooling+heating time window from settings (CURA-1783) 2016-06-29 14:44:43 +02:00
Tim Kuipers 97d895b0a3 feat: don't allow unused extruder to cool down when time window is too small (CURA-1783) 2016-06-29 14:34:41 +02:00
Ghostkeeper ac1087dda3 Merge branch 'materials_test'
Conflicts:
	src/gcodePlanner.cpp
2016-06-29 14:26:15 +02:00
Tim Kuipers f23a91f0aa refactor: rename time_to_start_warmup_earlier_to_be_extra_sure_we_dont_have_to_wait ==> extra_preheat_time (CURA-1731) 2016-06-29 12:50:28 +02:00
Tim Kuipers dd7fb1941f fix: time within path where preheat commands were inserted was inverted (CURA-1731) 2016-06-29 10:22:32 +02:00
Tim Kuipers 54f7a45f65 small codestyle fixes (CURA-1506) 2016-06-28 18:48:22 +02:00
Tim Kuipers 7d7ca8ae4a refactor: inversed logic of and calls to LayerPlanBuffer::insertPreheatCommand (CURA-1731) 2016-06-28 18:41:50 +02:00
Tim Kuipers 9ab0839b4a indent only (CURA-1731) 2016-06-28 18:33:24 +02:00
Tim Kuipers 844bfd34ab refactor+bugfix: simplified LayerPlanBuffer by passing around a vector of extruder plans rather than layers AND fixed a bug in handleStandbyTemp (CURA-1731)
handleStandbyTemp set the standby temp of the wrong extruder plan...
2016-06-28 18:32:59 +02:00
Tim Kuipers 7a87848562 refactor: layer plan buffer: bubble up special case for very first extruder plan of a meshgroup (CURA-1731) 2016-06-28 18:28:28 +02:00
Tim Kuipers 69a62a74ca fix: removed superfluous if case (CURA-1731) 2016-06-28 17:59:30 +02:00
Tim Kuipers 162f39e3fb fix: the standby temperatures were reset for all previous extruder plans with the same extruder in the buffer (CURA-1731) 2016-06-28 17:45:29 +02:00
Tim Kuipers 7fa436de51 fix: adjust lowest temperature when there's no time to heat to the required temperature (CURA-1731) 2016-06-28 17:44:55 +02:00
Tim Kuipers 1ef7ae46b2 lil: includes fixed for optional (CURA-1731) 2016-06-28 16:40:55 +02:00
Tim Kuipers 8b9695b913 doc: optional documentation (CURA-1731) 2016-06-28 16:39:34 +02:00
Tim Kuipers 5879a3cdaa fix: set extruder temps at ExtruderPlan start fixed instead of via TempInsert (CURA-1731) 2016-06-28 16:33:54 +02:00
Tim Kuipers 2236b3afeb feat: crude implementation of c++17::std::optional (CURA-1731) 2016-06-28 16:14:35 +02:00
Tim Kuipers ed283a978b feat: let Preheat compute from_temperature along with heating_time and bubble the result up (CURA-1731) 2016-06-28 14:03:48 +02:00
Tim Kuipers 9f44d8362a fix: start cooldown in new extruder plan instead of old to combat snowballing effect (CURA-1731)
snowballing: when we start cooling T0 before we require the temp of T1 to be reached, due to small discrepancy between the computed and actual heatup times, the time it takes to actually reach the required temp of T1 - while computed to be zero - causes T0 to drop in temperature in that time. This in turn will mean it will also take longer to heat up again, creating the same problem in the next extruder switch from T1 to T0, thereby snowballing into layer switches taking longer and longer.
2016-06-28 13:59:29 +02:00
Ghostkeeper 7e12817974 Codestyle: Spaces after comma
Contributes to issue CURA-1506.
2016-06-28 13:05:34 +02:00
Tim Kuipers 93bf338ff2 fix: start heating one second earlier (CURA-1731) 2016-06-28 12:14:42 +02:00
Jaime van Kessel 48c6b2a058 Merge branch 'feature_zhop_over_other_material' of github.com:Ultimaker/CuraEngine 2016-06-28 11:35:22 +02:00
Tim Kuipers e132db6142 codestyle: brackets (CURA-1506) 2016-06-27 18:09:51 +02:00
Tim Kuipers a0536af848 fixup: more consts in PolygonUtils::moveInside2 (CURA-1506) 2016-06-27 18:05:31 +02:00
Tim Kuipers 8e8c7fcfc9 doc: more documentation in combing (CURA-1506) 2016-06-27 18:01:45 +02:00
Tim Kuipers 6d672cc840 refactor: over_unavoidable_obstacles_makes_combing_fail ==> fail_on_unavoidable_obstacles (CURA-1506) 2016-06-27 17:49:01 +02:00
Tim Kuipers 9bf8dbbc96 fix: pass down penalty_function through PolygonUtils::ensureInside and moveInside2 (CURA-1506) 2016-06-27 17:42:25 +02:00
Tim Kuipers c1d4ba89d2 lil: removed unused code in unit test (CURA-1506) 2016-06-27 17:19:39 +02:00
Tim Kuipers 58269b363c Merge branch 'feature_zhop_over_other_material' of https://github.com/Ultimaker/CuraEngine into feature_zhop_over_other_material 2016-06-27 17:13:36 +02:00
Ghostkeeper a2f6e62795 Codestyle: Brackets around indented blocks
Contributes to issue CURA-1506.
2016-06-27 16:42:48 +02:00
Tim Kuipers 1e1a7e4f95 refactor/fix: removed retraction_config from GCodePathConfig and removed all last_retraction_config logic to revert to just using the retraction config of the extruder (CURA-303) 2016-06-27 15:02:05 +02:00
Ghostkeeper b035b47526 Spelling fix in documentation
Contributes to issue CURA-1506.
2016-06-27 14:47:14 +02:00
Ghostkeeper 53eda52041 Remove trailing whitespace
Contributes to issue CURA-1506.
2016-06-27 14:37:40 +02:00
Tim Kuipers 52331d0c83 fix: skin areas were missing when alternate extra perimeter was enabled (CURA-1773) 2016-06-27 10:43:55 +02:00
Tim Kuipers 5cc85d3bf6 fix: no more configurable switch_extruder_retraction_hop (CURA-1506 CURA-1061)
making the height configurable is quite difficult with how retraction currently works and there is not a lot of added value
2016-06-24 15:25:00 +02:00
Tim Kuipers 7cf21e9926 fix unit test: GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest() (CURA-1061) 2016-06-24 15:04:10 +02:00
Tim Kuipers 26d506a0f4 refactor/fix: move extruder switch retraction config to sliceDataStorage so that extruder switch z hop can be applied (CURA-1061) 2016-06-24 15:03:43 +02:00
Tim Kuipers 607c21ee46 fix: extruder switch zhop not performed on layer start anymore (CURA-1061)
only when the layer starts with a different extruder as the previous ended with
2016-06-24 14:18:55 +02:00
Tim Kuipers 402ede10d4 fix: perform zhops on very first travel of extruder plan. (CURA-1061)
still buggy: Now it does a zhop also on the start of a layer
2016-06-24 14:04:36 +02:00
Tim Kuipers bdd86ab9aa fix: made max_crossing_dist2 smaller cause other material was being crossed (CURA-1506) 2016-06-23 23:48:01 +02:00
Tim Kuipers ab3b1c2320 removed assert which doesn't seem to be true given the updated description of PolygonUtils::ensureInsideOrOutside(.) (CURA-1506) 2016-06-23 23:27:53 +02:00
Tim Kuipers 85e2ecc87e doc: warning for polygonUtils::ensureInsideOrOutside(.) that it may give false positives (CURA-1506) 2016-06-23 23:26:14 +02:00
Tim Kuipers 82846c0cc2 debug: PolygonUtils::ensureInsideOrOutside(.) does a polygon offset which sometimes seems to fail (CURA-1506)
sometimes it results in too small polygons which clipper itself and we cannot handle.
sometimes very small polygons are offsetted to polygons which lie outise the original polygon.
2016-06-23 23:25:20 +02:00
Tim Kuipers 09015e188a lil SVG const correctness 2016-06-23 23:06:23 +02:00
Tim Kuipers 36dbcfc667 check: assert to see whether ensureInsideOrOutside really moved inside (CURA-1506) 2016-06-23 22:56:34 +02:00
Tim Kuipers 5db83799d2 fix: PolygonUtils::ensureInsideOrOutside(.) checks is_inside to see if polygon offset failed (CURA-1506) 2016-06-23 22:55:54 +02:00
Tim Kuipers 9cb04d9e7a SVG fix for font size 2016-06-23 22:46:47 +02:00
Tim Kuipers 1c966cb6a5 deleted ancient test.cpp 2016-06-23 22:45:05 +02:00
Tim Kuipers f4d7695700 fix: replaced PolygonRef::inside(.) by Clipper function (CURA-1506) 2016-06-23 22:44:18 +02:00
Tim Kuipers 2288d2da49 fix: combing fails instead of crashes when combing fails due to too thin parts (CURA-1506) 2016-06-23 17:14:57 +02:00
Tim Kuipers 5d5a02739c fix: moveInside failed when distance was more than max_dist, but it was already inside anyway (CURA-1506) 2016-06-23 17:11:06 +02:00
Tim Kuipers 9248c17ace fix: combing findCrossingInOrMid can fail when part is too small (now doesn't crash anymore) (CURA-1506) 2016-06-23 16:46:35 +02:00
Tim Kuipers 7417e7d674 fix: Comb::moveInside forgot to set the inside_poly output parameter (CURA-1506) 2016-06-23 16:45:09 +02:00
Tim Kuipers 58228ff9a6 fix: PolygonUtils::findClose etc functions are now safe wrt ClosePolygonPoints which are not found (CURA-1506) 2016-06-23 16:18:44 +02:00
Tim Kuipers 329199af15 feat/fix: combing: findCrossingInOrMid now ensures to be inside (CURA-1506) 2016-06-23 16:17:39 +02:00
Tim Kuipers 2aa67efa20 refactor: changes ClosestPolygonPoint::point_idx and poly_idx to _unsigned_ int (CURA-1506)
NO_INDEX is an unsigned int and we don't need to waste all negative numbers which are invalid anyway
2016-06-23 15:47:37 +02:00
Tim Kuipers 7b79debf9a feat/fix: combing: moveInside now ensures to be inside, or changes startInside / endInside (CURA-1506) 2016-06-23 15:46:24 +02:00
Tim Kuipers 1314f00713 test: unit tests for PolygonUtils::ensureInside and for moveInside2 (CURA-1506) 2016-06-23 15:36:57 +02:00
Tim Kuipers a407ea6b61 fix: PolygonUtils::ensureInsideOrOutside passed output parameter by value (CURA-1506)| 2016-06-23 15:13:01 +02:00
Tim Kuipers 02e22b9442 small fixes in PolygonUtils (CURA-1506) 2016-06-23 14:47:17 +02:00
Tim Kuipers 5062a985fe feat: PolygonUtils::ensureInsideOrOutside more rigorous than a simple moveInside (CURA-1506) 2016-06-23 14:46:41 +02:00
Tim Kuipers 5294ea508c refactor: const correctness in PolygonUtils (CURA-1506) 2016-06-23 14:45:27 +02:00
Tim Kuipers 286a6edaa4 feat: PolygonUtils::moveInside2 a more simple version of moveInside and also for single polygons (CURA-1506) 2016-06-23 14:43:49 +02:00
Tim Kuipers 6af8a8eb85 feat: polygon::offset(.) (CURA-1506) 2016-06-23 14:36:21 +02:00
Tim Kuipers ee38f51af8 refactor: ClosestPolygonPoint::pos ==> point_idx (CURA-1506) 2016-06-23 12:09:22 +02:00
Tim Kuipers 7428a47344 unit test: polygon offset on holes works the same as offset on outer boundary (CURA-1506) 2016-06-23 12:07:17 +02:00
Tim Kuipers b859327bbd refactor/feat: ClosestPolygonPoint now also includes the poly index of the polygon which it refers to (CURA-1506) 2016-06-23 12:06:21 +02:00
Tim Kuipers 0a02a140fb fix: combing: better findInOrMid for tiny segments (CURA-1506) 2016-06-22 18:01:15 +02:00
Tim Kuipers 5d2d1fe6f3 lil fix: combing: made a small crossing more important than a large detour, cause otherwise sometimes combing fails because of it (CURA-1506) 2016-06-22 17:41:12 +02:00
Tim Kuipers ed2cdfa67a lil debug svg constness 2016-06-22 17:17:43 +02:00
Tim Kuipers 6d22cceac5 fix: Polygonutils::findClose only checked vertices of from-poly, now also checks points on line segments (CURA-1506) 2016-06-22 17:17:12 +02:00
Tim Kuipers 8a9f7dbf2b fix: use last_retraction_config for retractions (CURA-1506) 2016-06-22 17:16:32 +02:00
Ghostkeeper aa5bc6385b Merge branch 'materials_test' 2016-06-22 15:32:22 +02:00
Ghostkeeper 764d9868c7 Take max of double+double, not int+double
Not properly overloaded by max, I think.

Contributes to issue CURA-1717.
2016-06-22 15:32:07 +02:00
Ghostkeeper 5153022d28 Merge branch 'materials_test' 2016-06-22 15:28:42 +02:00
Ghostkeeper 7d87d2baea Improve time estimate when accelerating wrong way
When accelerating the wrong way, the discriminant of this parabolic is negative. Instead of returning a time estimate of 0, return the extremum of the parabola.

Contributes to issue CURA-1717.
2016-06-22 15:24:58 +02:00
Tim Kuipers 14518007a4 refactor: GCodePlanner::last_retraction_config ==> last_planned_retraction_config (CURA-1506 CURA-958) 2016-06-22 14:38:05 +02:00
Tim Kuipers 64f69d7629 fix: writeRetraction refered to last *planned* retraction config AND removed functionality to make a retraction into the extruder switch retraction (CURA-1506 CURA-958)
The only function using the last_retraction_config was the function which owuld convert a normal retraction into a nozzle switch retraction, instead of first performing the normal retraction and then the nozzle switch retraction. That functionality had to be removed anyway, because performing the nozzle switch retraction on the print can leave more scarring.
2016-06-22 14:34:42 +02:00
Tim Kuipers c93facc43c fix combing: moveInside always returned false even if we found a point inside (CURA-1506) 2016-06-21 18:09:49 +02:00
Tim Kuipers c2144cfe1c fix: PolygonUtils::moveInside tackled the corner case wrong when the point was exactly on the last point of a polygon (CURA-1506) 2016-06-21 17:32:55 +02:00
Tim Kuipers ca2b4af6f7 fix combing: move inside from previous move inside try so we can get out of the tip of a narrow angled piece (CURA-1506)
also I fixed that moveInside always made inside true, when it was false before.
2016-06-21 17:31:58 +02:00
Tim Kuipers 4d429eb719 fix: combing: better move inside strategy (CURA-1506) 2016-06-21 16:53:41 +02:00
Tim Kuipers e76109697e refactor: move combings moveInside into its own function (CURA-1506) 2016-06-21 16:01:17 +02:00
Tim Kuipers 213b67528a lil debug fix to SVG 2016-06-21 14:13:59 +02:00
Tim Kuipers 162d9aa0f7 fix: combing used offset_outside instead of distance between inside and outside at some places (CURA-1506) 2016-06-21 14:13:33 +02:00
Tim Kuipers 1c8f859885 fix: max_crossing_dist only accounted for the outside_boundary offset, not the inside_boundary_offset (CURA-1506) 2016-06-20 18:02:41 +02:00
Tim Kuipers db4c9b63f8 fix: combing: find better crossing when crossing diagonally between vertices (CURA-1506) 2016-06-20 18:02:02 +02:00
Javier Lechuga cc349cd046 bugfix_SegfaultDeletedCString 2016-06-20 17:34:57 +02:00
Tim Kuipers 6a925b73d6 lil doc 2016-06-20 17:25:23 +02:00
Tim Kuipers 2290a0927d refactor: moved Comb::findBestCrossing into Crossing (CURA-1506)
also used the same close_to for finding the crossing with the least detour, which is technically a change, but practically does the same thing.
2016-06-20 17:14:25 +02:00
Tim Kuipers 297a22280d refactor: combing crossings calculated factored out to new class Crossing (CURA-1506) 2016-06-20 17:11:25 +02:00
Javier Lechuga cd98c620af Merge branch 'bugfix_wallcount0' 2016-06-20 16:22:49 +02:00
Tim Kuipers e9647413f8 lil fix: polygon::PartsView::asseblePart is now const (CURA-1506) 2016-06-20 15:41:51 +02:00
Javier Lechuga 998ed5a76c bugfix: wall line count 0. CURA-1467 2016-06-20 14:39:23 +02:00
Tim Kuipers 2d62e67a24 refactor: moved combing classes into separate files (CURA-1506) 2016-06-20 13:18:46 +02:00
Tim Kuipers 69d2ab837f refactor: moved Comb into dir pathPlanning (CURa-1506) 2016-06-20 12:23:48 +02:00
Ghostkeeper ab8f84aa2d Merge branch 'materials_test' 2016-06-17 17:57:04 +02:00
Ghostkeeper 72c0a8391b Fix time estimates with accelerations
This was giving NaN values in the time estimates for the edge case where the discriminant of this quadratic was actually 0.0, but due to floating point rounding errors ended up below 0.

Contributes to issue CURA-1717.
2016-06-17 17:56:30 +02:00
Tim Kuipers 4b9a79df4d fix: combing: scanline crossings edge case for line segments ending exactly on the scanline (CURA-1506) 2016-06-17 17:51:01 +02:00
Tim Kuipers f7decf86a2 fix: combing can now fail if moving too far over in_between (CURA-1506) 2016-06-17 15:25:39 +02:00
Tim Kuipers 61b735ce89 fix: combing used pointer to local variable (CURA-1506) 2016-06-17 15:21:44 +02:00
Tim Kuipers c4b78fdfd1 fix: perform z hop always when not combing (CURA-1506)
whether to perform a z hop only when the comb move would collide only influences when the combing calculation decides to fail
2016-06-17 14:38:39 +02:00
Tim Kuipers 96769cb90c fix: combing: early calculation stopping for z-hops (CURA-1506) 2016-06-17 14:32:42 +02:00
Tim Kuipers 0ee60d2d3a fix: return whether the combing crosses an inavoidable boundary, aka being locked in/out (CURA-1506) 2016-06-17 14:01:05 +02:00
Tim Kuipers 9cbbdad56f lil: made clear where endInside and startInside are finally computed (CURA-1506) 2016-06-17 13:58:18 +02:00
Tim Kuipers 4a18f5d03d fix: combing: optimized and fixed going via hole polygon when endpoint is inside of the part containing the start point (CURA-1506) 2016-06-17 13:56:57 +02:00
Tim Kuipers 9b76a2e820 fix: combing/avoid always went to outside of part even when the endpoint was inside of a hole in the start part (CURA-1506) 2016-06-17 12:36:19 +02:00
Tim Kuipers 5a1897d631 feat: SVG::writePolygons and better border handling 2016-06-17 12:31:54 +02:00
Tim Kuipers 9d83fe08d8 doc: more documentation on combing and scanline crossings (CURA-1506) 2016-06-17 12:30:44 +02:00
Tim Kuipers 428198b4f9 feat: combing: find crossing from in to out closer to end point (CURA-1506) 2016-06-16 16:44:53 +02:00
Tim Kuipers e92bf03293 feat: combing: prefer crossings closer to start/end point a bit more (CURA-1506) 2016-06-16 16:33:13 +02:00
Tim Kuipers 9fe77a5761 test: PolygonUtils::findClose with penalty_function (CURA-1506) 2016-06-16 16:19:21 +02:00
Tim Kuipers fa07addba2 feat: PolygonUtils::findClose now support a penalty_function (CURA-1506) 2016-06-16 16:19:00 +02:00
Tim Kuipers ba4e4cc38f fix: PolygonUtils::findClose would return nothing when first arbitrary point was best (CURA-1506) 2016-06-16 16:17:45 +02:00
Tim Kuipers e2a47e1619 fix: pass by value bug and doc (CURA-1506) 2016-06-16 15:53:24 +02:00
Tim Kuipers e107626026 unit test: Polygonutils::findClosest with penalty function (CURA-1506) 2016-06-16 15:36:35 +02:00
Tim Kuipers 527b1f9b13 feat: added optional penalty function to PolygonUtils::findClosest (CURA-1506) 2016-06-16 15:27:10 +02:00
Tim Kuipers 6abc896bab feat: perform z-hop only when combing fails (CURA-1506) 2016-06-16 15:00:20 +02:00
Tim Kuipers f2de0fd5df refactor/feat: introduced getLastPlannedExtruderTrainSettings and replaced code with that function and with getExtruder() (CURA-1506) 2016-06-16 14:59:45 +02:00
Johan K c202d1b47a Changed the message used to send the final slice result to the front-end to LayerOptimized
Corrected the name of the data from polygon to path segment and left the old message for future visualization of the earlier stages.
2016-06-16 00:10:43 +02:00
Tim Kuipers d4349818b7 fix: reset acceleration and jerk back to default firmware values afterwards (CURA-1443) 2016-06-16 00:02:17 +02:00
Tim Kuipers ce80ceb165 lil doc typo 2016-06-15 23:21:27 +02:00
Tim Kuipers d19310ee27 refactor: removed superfluous/old writeTypeComment(string ..) (CURA-1350) 2016-06-15 23:20:30 +02:00
Tim Kuipers 63e6a249e3 feat: write time estimates in comments every layer (CURA-1350) 2016-06-15 23:17:50 +02:00
Johan K 0b05457fac Adding polygon packing feature and change the structure of the layer data sent to the front-end to speed up the layer view 2016-06-14 18:20:01 +02:00
Tim Kuipers 63356dca41 feat: set firmware values from configuration (CURA-1646) 2016-06-14 17:05:14 +02:00
awhiemstra 4bce36550f Merge pull request #353 from Johan3DV/master_polyutil_ref
Adding some references
2016-06-14 10:35:40 +02:00
Johan K 3ca1e6673f Adding some missing references 2016-06-13 22:07:31 +02:00
Ghostkeeper b2a4ee1647 Merge pull request #350 from thopiekar/master-windows-work
Ignore generated Makefile and archives of code
2016-06-13 00:35:31 +02:00
Thomas Karl Pietrowski 81ef194164 Merge remote-tracking branch 'refs/remotes/Ultimaker/master' into master-windows-work 2016-06-11 12:40:24 +02:00
Thomas Karl Pietrowski 2c698c8ba0 Ignore generated Makefile and archives of code 2016-06-11 12:40:20 +02:00
Jaime van Kessel 54e63de3a8 Merge branch 'feature_accelerations_and_jerk_per_feature_settigns_rework' of github.com:Ultimaker/CuraEngine 2016-06-10 15:36:52 +02:00
Tim Kuipers 0186d35372 fix: retrieved extruder_count instead of machine_extruder_count (CURA-1560) 2016-06-10 15:21:14 +02:00
Tim Kuipers c018a44c38 fix: retrieved extruder_count instead of machine_extruder_count (CURA-1560) 2016-06-10 13:13:13 +02:00
Tim Kuipers 0c62d1ab65 Merge branch 'master' into feature_accelerations_and_jerk_per_feature_settigns_rework 2016-06-10 12:19:41 +02:00
Tim Kuipers ff1fca976e bugfix: global settigns were never loaded (CURA-1682) 2016-06-09 19:03:52 +02:00
Tim Kuipers a7ad42eb1f fix: bugfix in comandSocket Extruder.settings (CURA-1682) 2016-06-09 13:10:30 +02:00
Tim Kuipers fb526ffc6d lil doc (CURA-1682) 2016-06-09 12:13:08 +02:00
Tim Kuipers d806c23d59 feat: handle extruder train settings (CURA-1682) 2016-06-09 12:07:24 +02:00
Tim Kuipers 28b8b41475 Added extruder to proto message
CURA-1681
CURA-1682
2016-06-09 12:00:35 +02:00
Tim Kuipers 457e609091 refactor/bugfix: inline handleSettingList (CURA-1682)
Two commits above introduced a bug where I passed thw wrong objects to handleSettingList
2016-06-09 11:40:50 +02:00
Tim Kuipers ffcd076f3e doc: commented proto file (CURA-1682 CURA-1681) 2016-06-09 11:39:11 +02:00
Tim Kuipers 01951ac513 feat: global settings are now passed via the same message as the objects (CURA-1682) 2016-06-09 11:08:36 +02:00
Tim Kuipers a8debfde40 refactor: comandSocket::handleSettingList now takes a settings base (CURA-1682) 2016-06-09 11:06:51 +02:00
Tim Kuipers 3194e30772 Updated proto file
Objects & global settings are now sent in one message.
    Also added support for extruder settings

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

this caused a crash when Ignore small z gaps was disabled and bottom was smaller than top thickness
2016-03-30 10:22:29 +02:00
sean041 a5bd599ec7 Fix typo. downSkinCount -> upSkinCount
This typo causes flaky crash when downSkinCount < upSkinCount.
2016-03-26 08:07:36 +08:00
Ghostkeeper 7548c41d7b Simplify test case
This test case now reduces to the normal test cases since there is only one option again. We can re-use the function that was made for normal test cases then.

Contributes to issue CURA-579.
2016-03-25 16:41:08 +01:00
Tim Kuipers cd033ef6ab doc: autobrief for first line (CURA-537) 2016-03-24 17:50:12 +01:00
Tim Kuipers e909af9abd documentation for fffProcessor (CURA-537) 2016-03-24 17:34:02 +01:00
Tim Kuipers dd5fbf14e4 document FffGcodeWriter (CURA-537) 2016-03-24 16:59:09 +01:00
Ghostkeeper 0985b97c54 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-03-23 16:08:58 +01:00
Tim Kuipers 06521eef8b lil 2016-03-23 12:32:44 +01:00
Tim Kuipers 47a6f0dc36 fix: made it possible to include warning and error functions in the graph; changed the way you should call the tool 2016-03-23 11:50:39 +01:00
Tim Kuipers ce4d34adb2 unit test fix: moveInside(polys, points, dist) may leave inside points as they are (CURA-579) 2016-03-21 17:51:38 +01:00
Tim Kuipers d42be2a22c removed old unused code 2016-03-21 17:29:22 +01:00
Tim Kuipers dbcbcae2e3 codestyle: closestHere ==> closest_here (CURA-893) 2016-03-21 17:26:04 +01:00
Tim Kuipers ff9cb24d99 refactor: auto ==> ClipperLib::Path (CURA-893) 2016-03-21 17:23:19 +01:00
Tim Kuipers b8ff36651e fix: tests failed due to changed GcodePlanner constructor (CURA-893)
combing changed from boolean to an enum
boolean is_inside_mesh was added
2016-03-21 17:16:24 +01:00
Tim Kuipers 98a78e1844 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2016-03-21 12:30:52 +01:00
Tim Kuipers 37c8ad3061 lil (CURA-1217) 2016-03-21 12:28:56 +01:00
Tim Kuipers d2187fedbc optimization: removed superluous recalculation of line direction in LineOrderOptimizer (CURA-1170) 2016-03-21 12:17:20 +01:00
Tim Kuipers 5b0a50456f fix syntax mistake (CURA-1170) 2016-03-21 11:55:01 +01:00
Tim Kuipers 66f4b51a3e calculate incoming_perpundicular_normal in end stage of line order optimizer (CURA-1170) 2016-03-21 11:53:33 +01:00
Tim Kuipers 3ac6ee1b37 refactor: factor out getAngleScore from line order optimizer (CURA-1170) 2016-03-21 11:43:12 +01:00
Tim Kuipers 4d8b22a224 refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-21 11:36:08 +01:00
Tim Kuipers 3a0143ff4c refactor: small optimization of line order optimizer dot score (CURA-1170) 2016-03-21 11:33:26 +01:00
Tim Kuipers c6a4945469 refactor: clear up pathOrderOptimizer for lines (CURA-1170) 2016-03-21 11:21:08 +01:00
Tim Kuipers abc6514b6d refactor: expand complicated code in pathOrderOptimizer (CURA-1170) 2016-03-21 11:05:01 +01:00
Tim Kuipers dc26358747 refactor: rewrite line order optimizer dot score stuff (CURA-1170) 2016-03-21 10:52:23 +01:00
Tim Kuipers 3485e5a4ad refactor: simple renaming of incoming_perpendicular_normal (CURA-1170) 2016-03-21 10:34:55 +01:00
Tim Kuipers bd47fd2c67 removed unused old commented code 2016-03-21 10:24:42 +01:00
Tim Kuipers d7d957d8f7 Removed unused code 2016-03-19 16:17:02 +01:00
Ghostkeeper bec8c235ea Add edge-case tests for getAngleLeft
These test what happens when two or more points are equal. There is nothing about this in the function specification, so it allows any output, but at least it shouldn't give like a divide by zero error.

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

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

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

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

Contributes to issue CURA-590.
2016-03-09 12:09:03 +01:00
Ghostkeeper 5d592553a6 Add test directory for infill
To make it complete for future tests that might appear there.
2016-03-09 11:00:03 +01:00
Ghostkeeper 38fad10453 Move utils tests to subdirectory
This keeps the same directory structure in the tests directory as in the source directory, as is common.
2016-03-09 10:50:10 +01:00
Ghostkeeper 1c16c77d56 Add test cases for moveInside
One of the edge cases currently fails.

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

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

there is no progress information during processing insets and skin of a single mesh
2016-02-11 16:44:48 +01:00
Tim Kuipers dbdbec44cc refactor: split up slices2polygons in functions which work on each mesh separately (CURA-872) 2016-02-11 16:21:50 +01:00
soyer a9fdad71b4 SettingConfig’s parent is unused 2016-02-11 16:17:55 +01:00
soyer 9fb6a217a4 use the std::abs, it has a proper overload for the long long 2016-02-11 16:15:35 +01:00
soyer c480c96066 this is a function declaration, and this is useless here 2016-02-11 16:13:38 +01:00
Tim Kuipers 1bcb38dcb6 Merge pull request #303 from soyersoyer/master
fix build in the without arcus case
2016-02-11 15:22:34 +01:00
soyer 0e7b164532 without arcus the code can’t send SlicingFinished message, so do nothing. 2016-02-11 12:11:34 +01:00
soyer 0f6bdfd36e the static CommandSocket::instance should be set without arcus too 2016-02-11 12:08:22 +01:00
Tim Kuipers 4d79ea3e9e doc+refactor: fan speed calc more clear (CURA-863) 2016-02-10 17:00:57 +01:00
Tim Kuipers 199fa070d6 feat: findNearestObject with extra conditions (CURA-590) 2016-02-10 11:43:46 +01:00
Tim Kuipers ef1dece5d2 fix: BucketGrid2D: disregard bucket-collision items in findNearbyObjects (CURA-590) 2016-02-10 11:19:19 +01:00
Tim Kuipers 87e42fd9bd fix: BucketGrid2D: enhancement of hashing protocols (CURA-590) 2016-02-10 11:12:54 +01:00
Tim Kuipers 8d0a75779d doc; refactor: BucketGrid2D (CURA-590)
introduced typedef CellIdx to make clear which Point variables are used to signify a cell index
2016-02-10 11:11:47 +01:00
Tim Kuipers e882b23d76 Merge branch '2.1' 2016-02-09 17:08:34 +01:00
Tim Kuipers 4361dbf8fb better print time output 2016-02-03 18:02:34 +01:00
Tim Kuipers 81ae074b86 Merge branch '2.1' 2016-02-03 17:45:04 +01:00
Tim Kuipers 3171bd4dcb fix: infill overlap in mm (CURA-786) 2016-01-28 16:30:07 +01:00
Tim Kuipers 199007fa76 Merge branch '2.1' 2016-01-28 15:07:26 +01:00
Tim Kuipers 203eb05d7c bugfix: support overlapping with model for xy_distance=0 (CURA-795) 2016-01-28 13:22:23 +01:00
Tim Kuipers 9e75f8c70c fix: z distance used at overhang instead of xy-distance (CURA-795)
factored computeBasicAndFullOverhang out of generateSupportAreas
keep overhang areas in a rolling deque so that they are available for the current layer and the layer supportZDistanceTop above
implemented some polygon magic to ensure that the distance of support to overhang areas is less than from non-overhang areas
2016-01-28 13:12:24 +01:00
Tim Kuipers 93b3d2e46e refactor: made some functions const in sliceDataStorage (CURA-795) 2016-01-28 13:03:49 +01:00
Tim Kuipers d0858bbdb6 merge of 2.1 2016-01-06 15:03:29 +01:00
Tim Kuipers cc23d73532 refactor: FeatureType ==> PrintFeatureType (CURA-606) 2015-12-18 16:32:13 +01:00
Tim Kuipers 4c139d6441 refactor: PolygonType ==> FeatureType in PrintFeature.h (CURA-606) 2015-12-18 16:32:02 +01:00
Tim Kuipers 13a18549bf Merge branch '2.1' 2015-12-18 14:32:47 +01:00
Tim Kuipers bd2f66e2eb bugfix: child settings got duplicated in extruder trains... (CURA-494) 2015-12-14 15:29:26 +01:00
Tim Kuipers 267eb7aef0 bugfix: made engine work both both extruder trains being an array as it being an object (CURA-494) 2015-12-14 14:14:08 +01:00
Tim Kuipers 8cd48bf5a2 settingsRegistry reorganized and made more robust; more stuff will override if possible; extruder trains can now be overridden (CURA-494) 2015-12-09 16:27:31 +01:00
Tim Kuipers a766455ec8 lil (CURA-494) 2015-12-09 16:24:47 +01:00
Tim Kuipers af08b57799 makefile now copies command_line_settings.json to binary dir (CURA-566) 2015-12-09 09:37:29 +01:00
Tim Kuipers dbecb29dc8 command line settings json (CURA-566) 2015-12-09 09:06:29 +01:00
Tim Kuipers 7df0a34464 lil bugfix settingsRegistry (CURA-566) 2015-12-09 09:05:39 +01:00
Tim Kuipers 925d50fc5d removed prime_tower_distance (was already unused) (CURA-566) 2015-12-09 09:05:11 +01:00
Tim Kuipers e3163586af Merge of branch 2.1 2015-12-08 16:24:53 +01:00
Tim Kuipers f58441a6ad refactor: added braces for checks of command_socket (CURA-499) 2015-12-08 13:16:03 +01:00
Tim Kuipers 04b4b2c057 merge 2015-12-07 13:42:31 +01:00
Ghostkeeper 0f5fd8d6ca Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-12-04 16:47:16 +01:00
Ghostkeeper 645b06271d Remove support-specific test
Support is already tested somewhat in the default test model. This makes the testing a bit more efficient.

Contributes to issue CURA-349.
2015-12-04 16:46:59 +01:00
Tim Kuipers ef61337ef8 better doc (CURA-522) 2015-12-04 13:55:08 +01:00
Tim Kuipers b8d5474811 merge 2015-12-03 14:57:13 +01:00
Ghostkeeper 4c60339695 Merge pull request #281 from Rhoban/master
Adding ENABLE_ARCUS option in CMakeLists

This allows compiling CuraEngine for command-line use only.
2015-12-03 11:01:57 +01:00
Gregwar e1a594ad3e ENABLE_ARCUS: Adding message(STATUS) line 2015-12-03 10:22:28 +01:00
Ghostkeeper 2b3f22872e Fix codestyle and superfluous commented code
Contributes to CURA-359.
2015-12-02 13:32:48 +01:00
Ghostkeeper fa03c3823f Merge branch '2.1'
Merging recent fixes to master, mostly to be able to get Jenkins to report the updated tests.
2015-12-02 12:15:39 +01:00
Tim Kuipers 2cf0d40775 fix: reserve [extruder_count] extruder plans, so that it's quite unlikely for the vector to get resized (CURA-298) 2015-12-01 17:23:16 +01:00
Tim Kuipers d20df39d5f Merge branch '2.1' 2015-12-01 16:31:58 +01:00
Tim Kuipers 57bc411bc6 refactor: speed_iconic ==> speed_base, speed ==> speed_current (CURA-496) 2015-12-01 16:31:34 +01:00
Gregwar 0c30d27892 Adding ENABLE_ARCUS option in CMakeLists 2015-12-01 16:27:14 +01:00
Tim Kuipers 0c9562b273 rename: support_extruder_nr ==> support_infill_extruder_nr 2015-12-01 16:07:04 +01:00
Tim Kuipers 742eedda06 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine 2015-12-01 12:30:37 +01:00
Tim Kuipers bc6a139d1f lil optimization (CURA-480) 2015-12-01 10:59:13 +01:00
Tim Kuipers 9770e91961 bugfix: first extruder plan might be empty, so then it should be removed (CURA-480) 2015-12-01 10:57:58 +01:00
daid 9e3dfb5b12 Allow running of test with just the default settings. 2015-11-30 15:22:13 +01:00
Tim Kuipers c05c3ddaed bugfix: always use safest distance for combing when a single distance is used for inside combing (CURA-480) 2015-11-27 16:32:41 +01:00
Tim Kuipers 804c288353 storage.getExtrudersUsed (CURA-480) 2015-11-27 16:30:04 +01:00
TotalRetribution fb2dac0f96 Cleaned up White space in processInsets 2015-11-18 13:26:33 +00:00
TotalRetribution e2e9cf8ea8 Merge remote-tracking branch 'origin/master' into reverse_inset_order 2015-11-18 11:51:18 +00:00
TotalRetribution 57ec92a645 Merge remote-tracking branch 'origin/master' into reverse_inset_order 2015-11-13 11:13:26 +00:00
TotalRetribution 6a26649fe4 Merged and fixed Conflict with outer_inset_first option in src/FffGcodeWriter.cpp 2015-08-25 15:12:28 +01:00
TotalRetribution 5334fb881e Merge remote-tracking branch 'origin/master' into reverse_inset_order 2015-08-21 15:49:01 +01:00
TotalRetribution bc9d7aced8 Added: Option to print outer inset first. 2015-08-19 10:35:09 +01:00
TotalRetribution 5bb794dc91 Merge remote-tracking branch 'origin/master' into reverse_inset_order
Conflicts:
	src/fffProcessor.h
	src/settings.cpp
	src/settings.h
2015-08-19 10:32:04 +01:00
TotalRetribution 7d928703a5 Changed option name enableReverseInsetOrder to outerInsetFirst to better explain its function. 2015-04-07 09:59:55 +01:00
TotalRetribution 8961df4342 Add option to Reverse Inset Print Order 2015-03-27 13:36:16 +00:00
171 arquivos alterados com 17053 adições e 1932294 exclusões
+16
Ver Arquivo
@@ -11,11 +11,26 @@ NUL
build/*
*.pyc
*.exe
*.a
*.o
CuraEngine
_bin
_obj
## CMake files
cmake_install.cmake
CMakeCache.txt
CMakeFiles/
CPackSourceConfig.cmake
# Visual Studio files generated by CMake
*.vcxproj
*.vcxproj.filters
CuraEngine.sln
# Makefile generated by CMake
Makefile
## IDE project files.
CuraEngine.layout
CuraEngine.cbp
@@ -32,3 +47,4 @@ documentation/latex/*
## Test results.
tests/output.xml
callgrind.out.*
+68 -14
Ver Arquivo
@@ -2,7 +2,14 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
find_package(Arcus REQUIRED)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -21,9 +28,11 @@ set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
option(BUILD_TESTS OFF)
# Add a compiler flag to check the output for insane values if we are in debug mode.
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
if(CMAKE_BUILD_TYPE MATCHES DEBUG OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
add_definitions(-DUSE_CPU_TIME)
add_definitions(-DDEBUG)
endif()
# Add warnings
@@ -39,15 +48,17 @@ add_library(clipper STATIC libs/clipper/clipper.cpp)
set(engine_SRCS # Except main.cpp.
src/bridge.cpp
src/comb.cpp
src/commandSocket.cpp
src/ConicalOverhang.cpp
src/ExtruderTrain.cpp
src/FffGcodeWriter.cpp
src/FffPolygonGenerator.cpp
src/FffProcessor.cpp
src/gcodeExport.cpp
src/GCodePathConfig.cpp
src/gcodePlanner.cpp
src/infill.cpp
src/inset.cpp
src/WallsComputation.cpp
src/layerPart.cpp
src/LayerPlanBuffer.cpp
src/MergeInfillLines.cpp
@@ -56,16 +67,14 @@ set(engine_SRCS # Except main.cpp.
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/PrimeTower.cpp
src/Progress.cpp
src/raft.cpp
src/settingRegistry.cpp
src/settings.cpp
src/skin.cpp
src/skirt.cpp
src/SkirtBrim.cpp
src/sliceDataStorage.cpp
src/slicer.cpp
src/support.cpp
src/timeEstimate.cpp
src/WallsComputation.cpp
src/wallOverlap.cpp
src/Weaver.cpp
src/Wireframe2gcode.cpp
@@ -76,25 +85,55 @@ set(engine_SRCS # Except main.cpp.
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/pathPlanning/Comb.cpp
src/pathPlanning/LinePolygonsCrossings.cpp
src/progress/Progress.cpp
src/progress/ProgressStageEstimator.cpp
src/settings/SettingConfig.cpp
src/settings/SettingContainer.cpp
src/settings/SettingRegistry.cpp
src/settings/settings.cpp
src/utils/AABB.cpp
src/utils/AABB3D.cpp
src/utils/Date.cpp
src/utils/gettime.cpp
src/utils/LinearAlg2D.cpp
src/utils/ListPolyIt.cpp
src/utils/logoutput.cpp
src/utils/PolygonProximityLinker.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
src/utils/ProximityPointLink.cpp
)
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
set(engine_TEST
GCodePlannerTest
GCodePlannerTest
)
set(engine_TEST_INFILL
)
set(engine_TEST_UTILS
SparseGridTest
LinearAlg2DTest
PolygonUtilsTest
PolygonTest
StringTest
)
# Generating ProtoBuf protocol.
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
# Compiling CuraEngine itself.
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
target_link_libraries(_CuraEngine clipper Arcus)
target_link_libraries(_CuraEngine clipper)
if (ENABLE_ARCUS)
target_link_libraries(_CuraEngine Arcus)
endif ()
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
@@ -113,9 +152,24 @@ if (BUILD_TESTS)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_INFILL})
add_executable(${test} tests/main.cpp tests/infill/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_UTILS})
add_executable(${test} tests/main.cpp tests/utils/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
endif()
add_custom_command(TARGET CuraEngine POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:CuraEngine>)
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
include(CPackConfig.cmake)
+57 -14
Ver Arquivo
@@ -5,12 +5,21 @@ package cura.proto;
message ObjectList
{
repeated Object objects = 1;
repeated Setting settings = 2;
repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing)
}
message Slice
{
repeated ObjectList object_lists = 1;
repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another
SettingList global_settings = 2; // The global settings used for the whole print job
repeated Extruder extruders = 3; // The settings sent to each extruder object
repeated SettingExtruder limit_to_extruder = 4; // From which stack the setting would inherit if not defined per object
}
message Extruder
{
int32 id = 1;
SettingList settings = 2;
}
message Object
@@ -29,10 +38,10 @@ message Progress
message Layer {
int32 id = 1;
float height = 2;
float thickness = 3;
float height = 2; // Z position
float thickness = 3; // height of a single layer
repeated Polygon polygons = 4;
repeated Polygon polygons = 4; // layer data
}
message Polygon {
@@ -47,20 +56,48 @@ message Polygon {
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
SupportInterfaceType = 10;
}
Type type = 1;
bytes points = 2;
float line_width = 3;
Type type = 1; // Type of move
bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used)
float line_width = 3; // The width of the line being laid down
}
message LayerOptimized {
int32 id = 1;
float height = 2; // Z position
float thickness = 3; // height of a single layer
repeated PathSegment path_segment = 4; // layer data
}
message PathSegment {
int32 extruder = 1; // The extruder used for this path segment
enum PointType {
Point2D = 0;
Point3D = 1;
}
PointType point_type = 2;
bytes points = 3; // The points defining the line segments, bytes of float[2/3] array of length N+1
bytes line_type = 4; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path
bytes line_width = 5; // The widths of the line segments as bytes of a float array of length 1 or N
}
message GCodeLayer {
bytes data = 2;
}
message ObjectPrintTime {
message PrintTimeMaterialEstimates { // The print time for the whole print and material estimates for the extruder
float time = 1; // Total time estimate
repeated MaterialEstimates materialEstimates = 2; // materialEstimates data
}
message MaterialEstimates {
int64 id = 1;
float time = 2;
float material_amount = 3;
float material_amount = 2; // material used in the extruder
}
message SettingList {
@@ -68,13 +105,19 @@ message SettingList {
}
message Setting {
string name = 1;
string name = 1; // Internal key to signify a setting
bytes value = 2;
bytes value = 2; // The value of the setting
}
message SettingExtruder {
string name = 1; //The setting key.
int32 extruder = 2; //From which extruder stack the setting should inherit.
}
message GCodePrefix {
bytes data = 2;
bytes data = 2; //Header string to be prepended before the rest of the g-code sent from the engine.
}
message SlicingFinished {
+1 -1
Ver Arquivo
@@ -178,7 +178,7 @@ JAVADOC_AUTOBRIEF = NO
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
QT_AUTOBRIEF = YES
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+21 -14
Ver Arquivo
@@ -19,46 +19,53 @@ But in general it boils down to: You need to share the source of any CuraEngine
How to Install
==============
1. Clone the repository from https://github.com/Ultimaker/CuraEngine.git (the URL at the right hand side of this page).
2. Install Protobuf (see below)
2. Install Protobuf >= 3.0.0 (see below)
3. Install libArcus (see https://github.com/Ultimaker/libArcus)
In order to compile CuraEngine, either use CMake or start a project in your preferred IDE.
CMake compilation:
1. Navigate to the CuraEngine directory and execute the following commands
2. $ mkdir build && cd build
3. $ cmake ..
4. $ make
2. ```$ mkdir build && cd build```
3. ```$ cmake ..```
4. ```$ make```
Project files generation:
1. Navigate to the CuraEngine directory and execute the following commands
2. cmake . -G "CodeBlocks - Unix Makefiles"
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)
Installing Protobuf
-------------------
1. Be sure to have libtool installed.
2. Download protobuf from https://github.com/google/protobuf/ (download ZIP and unZIP at desired location, or clone the repo) The protocol buffer is used for communication between the CuraEngine and the GUI.
3. Before installing protobuf, change autogen.sh : comment line 18 to line 38 using '#'s. This removes the dependency on gtest-1.7.0.
4. Run autogen.sh from the protobuf directory:
$ ./autogen.sh
5. $ ./configure
6. $ make
7. $ make install # Requires superused priviliges.
8. (In case the shared library cannot be loaded, you can try "sudo ldconfig" on Linux systems)
2. Download protobuf from https://github.com/google/protobuf/releases (download ZIP and unZIP at desired location, or clone the repo). The protocol buffer is used for communication between the CuraEngine and the GUI.
3. Run ```autogen.sh``` from the protobuf directory:
```$ ./autogen.sh```
4. ```$ ./configure```
5. ```$ make```
6. ```# make install```
(Please note the ```#```. It indicates the need of superuser, as known as root, priviliges.)
7. (In case the shared library cannot be loaded, you can try ```sudo ldconfig``` on Linux systems)
Running
=======
Other than running CuraEngine from a frontend, such as Ultimaker/Cura, one can run CuraEngine from the command line.
For that one needs a settings JSON file, which can be found in the Ultimaker/Cura repository.
Note that the structure of the json files has changed since 2.1. In the corresponding branch of the Cura repository you can find how the json files used to be structured.
An example run for an UM2 machine looks as follows:
* Navigate to the CuraEngine directory and execute the following
```
./build/CuraEngine slice -v -j ../Cura/resources/machines/dual_extrusion_printer.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
./build/CuraEngine slice -v -j ../Cura/resources/definitions/dual_extrusion_printer.def.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
```
Run `CuraEngine help` for a general description of how to use the CuraEngine tool.
[Set the environment variable](https://help.ubuntu.com/community/EnvironmentVariables) CURA_ENGINE_SEARCH_PATH to the appropriate paths, delimited by a colon e.g.
```
CURA_ENGINE_SEARCH_PATH=/path/to/Cura/resources/definitions:/user/defined/path
```
Internals
=========
+19 -21
Ver Arquivo
@@ -1,26 +1,24 @@
The Clipper Library (including Delphi, C++ & C# source code, other accompanying
code, examples and documentation), hereafter called "the Software", has been
released under the following license, terms and conditions:
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the Software covered by this license to use, reproduce,
display, distribute, execute, and transmit the Software, and to prepare
derivative works of the Software, and to permit third-parties to whom the
Software is furnished to do so, all subject to the following:
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including the
above license grant, this restriction and the following disclaimer, must be
included in all copies of the Software, in whole or in part, and all derivative
works of the Software, unless such copies or derivative works are solely in the
form of machine-executable object code generated by a source language processor.
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+34 -3
Ver Arquivo
@@ -1,8 +1,39 @@
=====================================================================
Clipper Change Log
=====================================================================
v6.2.1 (31 October 2014) Rev 482
* Bugfix in ClipperOffset.Execute where the Polytree.IsHole property
was returning incorrect values with negative offsets
* Very minor improvement to join rounding in ClipperOffset
* Fixed CPP OpenGL demo.
v6.1.3 (19 January 2014)
v6.2.0 (17 October 2014) Rev 477
* Numerous minor bugfixes, too many to list.
(See revisions 454-475 in Sourceforge Repository)
* The ZFillFunction (custom callback function) has had its parameters
changed.
* Curves demo removed (temporarily).
* Deprecated functions have been removed.
v6.1.5 (26 February 2014) Rev 460
* Improved the joining of output polygons sharing a common edge
when those common edges are horizontal.
* Fixed a bug in ClipperOffset.AddPath() which would produce
incorrect solutions when open paths were added before closed paths.
* Minor code tidy and performance improvement
v6.1.4 (6 February 2014)
* Fixed bugs in MinkowskiSum
* Fixed minor bug when using Clipper.ForceSimplify.
* Modified use_xyz callback so that all 4 vertices around an
intersection point are now passed to the callback function.
v6.1.3a (22 January 2014) Rev 453
* Fixed buggy PointInPolygon function (C++ and C# only).
Note this bug only affected the newly exported function, the
internal PointInPolygon function used by Clipper was OK.
v6.1.3 (19 January 2014) Rev 452
* Fixed potential endless loop condition when adding open
paths to Clipper.
* Fixed missing implementation of SimplifyPolygon function
@@ -13,11 +44,11 @@ v6.1.3 (19 January 2014)
* Overloaded MinkowskiSum function to accommodate multi-contour
paths.
v6.1.2 (15 December 2013)
v6.1.2 (15 December 2013) Rev 444
* Fixed broken C++ header file.
* Minor improvement to joining polygons.
v6.1.1 (13 December 2013)
v6.1.1 (13 December 2013) Rev 441
* Fixed a couple of bugs affecting open paths that could
raise unhandled exceptions.
+388 -534
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+32 -35
Ver Arquivo
@@ -1,8 +1,8 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Version : 6.2.1 *
* Date : 31 October 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
@@ -34,7 +34,7 @@
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.1.3"
#define CLIPPER_VERSION "6.2.0"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
@@ -46,9 +46,8 @@
//use_lines: Enables line clipping. Adds a very minor cost to performance.
//#define use_lines
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <vector>
#include <set>
@@ -57,6 +56,7 @@
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
namespace ClipperLib {
@@ -69,11 +69,16 @@ enum PolyType { ptSubject, ptClip };
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
typedef unsigned int cUInt;
typedef int cInt;
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
#else
typedef signed long long cInt;
typedef unsigned long long cUInt;
typedef signed long long cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class
typedef unsigned long long ulong64;
#endif
struct IntPoint {
@@ -117,15 +122,12 @@ struct DoublePoint
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
@@ -134,6 +136,7 @@ class PolyNode
{
public:
PolyNode();
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
@@ -168,11 +171,6 @@ bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
@@ -183,8 +181,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
@@ -202,7 +199,7 @@ enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinima;
struct LocalMinimum;
struct Scanbeam;
struct OutPt;
struct OutRec;
@@ -213,7 +210,6 @@ typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
@@ -236,12 +232,14 @@ protected:
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
@@ -268,7 +266,7 @@ public:
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(TZFillCallback zFillFunc);
void ZFillFunction(ZFillCallback zFillFunc);
#endif
protected:
void Reset();
@@ -279,7 +277,8 @@ private:
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
@@ -289,7 +288,7 @@ private:
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
TZFillCallback m_ZFill; //custom callback
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
@@ -308,21 +307,19 @@ private:
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
@@ -344,7 +341,7 @@ private:
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e);
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
};
//------------------------------------------------------------------------------
+19
Ver Arquivo
@@ -0,0 +1,19 @@
find engine setting literals
cd ~/Development/CuraEngine/output/reflection/
~/bin/substitute.pl y 'while(/getSetting\w+\("(\w+)"\)/gsm) { print "$1\n"; }' ../../src/ | sort | uniq > engineSettingLiterals.txt
run setting inheritance reflection
cd ~/Development/CuraEngine
./build/CuraEngine analyse ../Cura/resources/definitions/fdmprinter.def.json meta/refl_ff.gv output/reflection/engineSettingLiterals.txt -piew
dot meta/refl_ff.gv -Tpng > meta/rafl_ff_dotted.png
green block = used in engine
red edge = inherit function only
black edge = parent-child relation
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 284 KiB

+58
Ver Arquivo
@@ -0,0 +1,58 @@
{
"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
}
}
}
}
}
+35
Ver Arquivo
@@ -0,0 +1,35 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#include "ConicalOverhang.h"
namespace cura {
void ConicalOverhang::apply(Slicer* slicer, double angle, int layer_thickness)
{
double tanAngle = tan(angle); // the XY-component of the angle
int max_dist_from_lower_layer = tanAngle * layer_thickness; // max dist which can be bridged
for (unsigned int layer_nr = slicer->layers.size() - 2; static_cast<int>(layer_nr) >= 0; layer_nr--)
{
SlicerLayer& layer = slicer->layers[layer_nr];
SlicerLayer& layer_above = slicer->layers[layer_nr + 1];
if (std::abs(max_dist_from_lower_layer) < 5)
{ // magically nothing happens when max_dist_from_lower_layer == 0
// below magic code solves that
int safe_dist = 20;
Polygons diff = layer_above.polygons.difference(layer.polygons.offset(-safe_dist));
layer.polygons = layer.polygons.unionPolygons(diff);
layer.polygons = layer.polygons.smooth(safe_dist);
layer.polygons.simplify(safe_dist, safe_dist * safe_dist / 4);
// somehow layer.polygons get really jagged lines with a lot of vertices
// without the above steps slicing goes really slow
}
else
{
layer.polygons = layer.polygons.unionPolygons(layer_above.polygons.offset(-max_dist_from_lower_layer));
}
}
}
}//namespace cura
+30
Ver Arquivo
@@ -0,0 +1,30 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef CONICAL_OVERHANG_H
#define CONICAL_OVERHANG_H
#include "slicer.h"
namespace cura {
/*!
* A class for changing the geometry of a model such that it is printable without support -
* Or at least with at least support as possible
*/
class ConicalOverhang
{
public:
/*!
* Change the slice data such that the model becomes more printable
*
* \param[in,out] slicer The slice data
* \param angle The maximum angle which can be printed without generating support (or at least generating least support)
* \param layer_thickness The general layer thickness
*/
static void apply(Slicer* slicer, double angle, int layer_thickness);
};
}//namespace cura
#endif // CONICAL_OVERHANG_H
+27
Ver Arquivo
@@ -0,0 +1,27 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ExtruderTrain.h"
namespace cura
{
int ExtruderTrain::getExtruderNr()
{
return extruder_nr;
}
ExtruderTrain::ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
: SettingsBase(settings)
, extruder_nr(extruder_nr)
{
}
bool ExtruderTrain::getIsUsed() const
{
return is_used;
}
void ExtruderTrain::setIsUsed(bool used)
{
is_used = used;
}
}//namespace cura
+10 -8
Ver Arquivo
@@ -1,7 +1,8 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef EXTRUDER_TRAIN_H
#define EXTRUDER_TRAIN_H
#include "settings.h"
#include "settings/settings.h"
namespace cura
{
@@ -9,14 +10,15 @@ namespace cura
class ExtruderTrain : public SettingsBase
{
int extruder_nr;
bool is_used = false; //!< whether this extruder train is (probably) used during printing the current meshgroup
public:
int getExtruderNr() { return extruder_nr; }
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
: SettingsBase(settings)
, extruder_nr(extruder_nr)
{ }
int getExtruderNr();
bool getIsUsed() const; //!< return whether this extruder train is (probably) used during printing the current meshgroup
void setIsUsed(bool used); //!< set whether this extruder train is (probably) used during printing the current meshgroup
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr);
};
}//namespace cura
+1 -1
Ver Arquivo
@@ -1,7 +1,7 @@
#ifndef FAN_SPEED_LAYER_TIME_H
#define FAN_SPEED_LAYER_TIME_H
#include "settings.h"
#include "settings/settings.h"
namespace cura
{
+545 -303
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+161 -83
Ver Arquivo
@@ -37,40 +37,57 @@ class FffGcodeWriter : public SettingsMessenger, NoCopy
{
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
private:
int max_object_height;
int meshgroup_number; //!< used for sequential printing of objects
LayerPlanBuffer layer_plan_buffer;
GCodeExport gcode;
std::ofstream output_file;
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
int max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
/*
* Buffer for all layer plans (of type GCodePlanner)
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
* The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used.
* Another reason is to perform Auto Temperature.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
FanSpeedLayerTimeSettings fan_speed_layer_time_settings;
LayerPlanBuffer layer_plan_buffer;
/*!
* The class holding the current state of the gcode being written.
*
* It holds information such as the last written position etc.
*/
GCodeExport gcode;
/*!
* The gcode file to write to when using CuraEngine as command line tool.
*/
std::ofstream output_file;
/*!
* Whether the skirt or brim polygons have been processed into planned paths
* for each extruder train.
*/
bool skirt_brim_is_processed[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)
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, layer_plan_buffer(this, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // TODO: make configurable
, current_extruder_planned(0) // changed somewhere early in FffGcodeWriter::writeGCode
, is_inside_mesh_layer_part(false)
{
meshgroup_number = 1;
max_object_height = 0;
}
void resetFileNumber()
{
meshgroup_number = 1;
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
output_file.open(filename);
@@ -81,84 +98,143 @@ public:
}
return false;
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
gcode.setOutputStream(stream);
}
double getTotalFilamentUsed(int e)
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode.getTotalFilamentUsed(e);
return gcode.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode.getTotalPrintTime();
}
/*!
* Write all the gcode for the current meshgroup.
* This is the primary function of this class.
*
* \param[in] storage The data storage from which to get the polygons to print and the areas to fill.
* \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process.
*/
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
private:
void setConfigFanSpeedLayerTime();
/*!
* Set the FffGcodeWriter::fan_speed_layer_time_settings by retrieving all settings from the global/per-meshgroup settings.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigFanSpeedLayerTime(SliceDataStorage& storage);
/*!
* Create and set the SliceDataStorage::coasting_config for each extruder.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigCoasting(SliceDataStorage& storage);
//Setup the retraction parameters.
/*!
* Set the retraction config globally, per extruder and per mesh.
*
* \param[out] storage The data storage to which to save the configurations
*/
void setConfigRetraction(SliceDataStorage& storage);
/*!
* initialize GcodePathConfig config parameters which don't change over all layers
* Initialize the GcodePathConfig config parameters which don't change over
* all layers, for each feature.
*
* The features are: skirt or brim, support and for each mesh: outer wall,
* inner walls, skin, infill (and combined infill).
*
* \param[out] storage The data storage to which to save the configurations.
*/
void initConfigs(SliceDataStorage& storage);
/*!
* Set temperatures and perform initial priming.
* \param storage Input: where the slice data is stored.
*
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
*
* \param[in] storage where the slice data is stored.
*/
void processStartingCode(SliceDataStorage& storage);
/*!
* Move up and over the just printed model to print the next model.
* \param storage Input: where the slice data is stored.
* Move up and over the already printed meshgroups to print the next meshgroup.
*
* \param[in] storage where the slice data is stored.
*/
void processNextMeshGroupCode(SliceDataStorage& storage);
/*!
* Add raft gcode.
* \param storage Input: where the slice data is stored.
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
*
* \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);
/*!
* Add a layer to the gcode.
* \param storage Input: where the slice data is stored.
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
*
* In case of negative layer numbers, create layers only containing the data from
* the helper parts (support etc) to fill up the gap between the raft and the model.
*
* \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.
* \param has_raft Whether a raft is used for this print.
*/
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
void processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers);
/*!
* Add the skirt to the gcode.
* \param storage Input: where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param extruder_nr The extrudewr train for which to process the skirt
* Add the skirt or the brim to the layer plan \p gcodeLayer.
*
* \param Storage where the slice data is stored.
* \param gcodeLayer The initial planning of the g-code of the layer.
* \param extruder_nr The extruder train for which to process the skirt or
* brim.
*/
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
void processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
/*!
* Adds the ooze shield to the print.
* \param storage Input: where the slice data is stored.
* Adds the ooze shield to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Adds the draft protection screen to the print.
* \param storage Input: where the slice data is stored.
* Adds the draft protection screen to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
@@ -166,16 +242,18 @@ private:
/*!
* Calculate in which order to print the meshes.
* \param storage Input: where the slice data is stored.
*
* \param[in] storage where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of mesh indices ordered on print order.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
/*!
* Add a single layer from a single mesh-volume to the GCode in mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -183,9 +261,10 @@ private:
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the open polylines from a single layer from a single mesh-volume to the GCode for mesh surface mode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh for which to add to the gcode.
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -193,9 +272,10 @@ private:
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add a single layer from a single mesh-volume to the GCode.
* \param storage Input: where the slice data is stored.
* \param mesh The mesh to add to the gcode.
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
@@ -203,35 +283,34 @@ private:
void addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer.
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Add normal sparse infill for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
@@ -242,34 +321,33 @@ private:
/*!
* Add the gcode of the top/bottom skin of the given part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param skin_overlap The distance by which the skin overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width);
void processSkin(cura::GCodePlanner& gcode_layer, cura::SliceMeshStorage* mesh, cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
/*!
* Add the support to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
* \param before_rest Whether the function has been called before adding the rest to the gcode, or after.
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, or after.
*/
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
/*!
* Add the support lines/walls to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support roofs to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
* Add the support 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.
*/
@@ -280,7 +358,7 @@ private:
*
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
*
* \param storage Input: where the slice data is stored.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr The extruder to which to switch
@@ -289,7 +367,7 @@ private:
/*!
* Add the prime tower gcode for the current layer.
* \param storage Input: where the slice data is stored.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param prev_extruder The current extruder with which we last printed.
+377 -188
Ver Arquivo
@@ -1,7 +1,9 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include <map> // multimap (ordered map allowing duplicate keys)
#include "utils/math.h"
#include "slicer.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
@@ -9,19 +11,23 @@
#include "support.h"
#include "multiVolumes.h"
#include "layerPart.h"
#include "inset.h"
#include "skirt.h"
#include "WallsComputation.h"
#include "SkirtBrim.h"
#include "skin.h"
#include "infill.h"
#include "raft.h"
#include "debug.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "ConicalOverhang.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
#include "progress/ProgressEstimatorLinear.h"
namespace cura
{
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (!sliceModel(meshgroup, timeKeeper, storage))
@@ -34,6 +40,22 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
return true;
}
unsigned int FffPolygonGenerator::getDraftShieldLayerCount(const unsigned int total_layers) const
{
if (!getSettingBoolean("draft_shield_enabled"))
{
return 0;
}
switch (getSettingAsDraftShieldHeightLimitation("draft_shield_height_limitation"))
{
default:
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);
}
}
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
@@ -43,27 +65,22 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
storage.model_size = storage.model_max - storage.model_min;
log("Slicing model...\n");
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
int initial_layer_thickness = getSettingInMicrons("layer_height_0");
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Initial layer height %i is disallowed.",initial_layer_thickness);
logError("Initial layer height %i is disallowed.\n", initial_layer_thickness);
return false;
}
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
int layer_thickness = getSettingInMicrons("layer_height");
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Layer height %i is disallowed.",layer_thickness);
logError("Layer height %i is disallowed.\n", layer_thickness);
return false;
}
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{
initial_layer_thickness = layer_thickness;
}
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
int slice_layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if (slice_layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
return true; //This is NOT an error state!
}
@@ -71,7 +88,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup->meshes[mesh_idx];
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, slice_layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
slicerList.push_back(slicer);
/*
for(SlicerLayer& layer : slicer->layers)
@@ -83,183 +100,364 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx]); // new mesh in storage had settings from the Mesh
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("conical_overhang_enabled"))
{
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
}
}
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
carveMultipleVolumes(slicerList);
generateMultipleVolumesOverlap(slicerList);
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];
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];
createLayerParts(meshStorage, slicerList[meshIdx], mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
createLayerParts(meshStorage, slicer, mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
bool has_raft = meshStorage.getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
//Add the raft offset to each layer.
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
meshStorage.getSettingInMicrons("layer_height_0")
getSettingInMicrons("layer_height_0")
- initial_slice_z;
if (has_raft)
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("adhesion_extruder_nr"));
layer.printZ +=
meshStorage.getSettingInMicrons("raft_base_thickness")
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap")
- meshStorage.getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
Raft::getTotalThickness(storage)
+ train->getSettingInMicrons("raft_airgap")
- train->getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
{
layer.printZ += meshStorage.getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
layer.printZ += train->getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
}
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
return true;
}
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
size_t total_layers = 0;
// compute layer count and remove first empty layers
// there is no separate progress stage for removeEmptyFisrtLayer (TODO)
unsigned int slice_layer_count = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
if (!mesh.getSettingBoolean("infill_mesh"))
{
slice_layer_count = std::max<unsigned int>(slice_layer_count, mesh.layers.size());
}
}
// handle meshes
std::vector<double> mesh_timings;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
mesh_timings.push_back(1.0); // TODO: have a more accurate estimate of the relative time it takes per mesh, based on the height and number of polygons
}
ProgressStageEstimator inset_skin_progress_estimate(mesh_timings);
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, &time_keeper);
std::vector<unsigned int> mesh_order;
{ // compute mesh order
std::multimap<int, unsigned int> order_to_mesh_indices;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
order_to_mesh_indices.emplace(storage.meshes[mesh_idx].getSettingAsIndex("infill_mesh_order"), mesh_idx);
}
for (std::pair<const int, unsigned int>& order_and_mesh_idx : order_to_mesh_indices)
{
mesh_order.push_back(order_and_mesh_idx.second);
}
}
for (unsigned int mesh_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);
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;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{ // find first mesh which has this layer
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (int(layer_nr) <= mesh.layer_nr_max_filled_layer)
{
layer = &mesh.layers[layer_nr];
print_layer_count = layer_nr + 1;
break;
}
}
if (layer != nullptr)
{
if (CommandSocket::isInstantiated())
{ // send layer info
CommandSocket::getInstance()->sendOptimizedLayerInfo(layer_nr, layer->printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
}
}
}
log("Layer count: %i\n", print_layer_count);
//layerparts2HTML(storage, "output/output.html");
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(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)
{
processInsets(storage, layer_number);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
}
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
if (total_layers < 1)
{
log("Stopping process because there are no layers.\n");
log("Stopping process because there are no non-empty layers.\n");
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, total_layers);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
}
ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr"));
CommandSocket::sendPolygons(PrintFeatureType::Infill, support, 100); // infill_extr->getSettingInMicrons("support_line_width"));
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
if (getSettingBoolean("magic_spiralize"))
{
for(SliceMeshStorage& mesh : storage.meshes)
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(storage, layer_number);
}
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
}
for(SliceMeshStorage& mesh : storage.meshes)
{
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
}
// handle helpers
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, total_layers);
storage.primeTower.generatePaths(storage, print_layer_count);
processOozeShield(storage, total_layers);
processDraftShield(storage, total_layers);
processPlatformAdhesion(storage);
logDebug("Processing ooze shield\n");
processOozeShield(storage);
logDebug("Processing draft shield\n");
processDraftShield(storage, print_layer_count);
logDebug("Processing platform adhesion\n");
processPlatformAdhesion(storage);
for(SliceMeshStorage& mesh : storage.meshes)
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzyWalls(mesh);
}
processDerivedWallsSkinInfill(mesh, print_layer_count);
}
}
void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int layer_nr)
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)
{
for(SliceMeshStorage& mesh : storage.meshes)
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh"))
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
processInfillMesh(storage, mesh_order_idx, mesh_order, total_layers);
}
// TODO: make progress more accurate!!
// note: estimated time for insets : skins = 22.953 : 48.858
std::vector<double> walls_vs_skin_timing({22.953, 48.858});
ProgressStageEstimator* mesh_inset_skin_progress_estimator = new ProgressStageEstimator(walls_vs_skin_timing);
inset_skin_progress_estimate.nextStage(mesh_inset_skin_progress_estimator); // the stage of this function call
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(total_layers);
mesh_inset_skin_progress_estimator->nextStage(inset_estimator);
// walls
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
logDebug("Processing insets for layer %i of %i\n", layer_number, total_layers);
processInsets(mesh, layer_number);
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(total_layers);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
bool process_infill = mesh.getSettingInMicrons("infill_line_distance") > 0;
if (!process_infill)
{ // do process infill anyway if it's modified by modifier meshes
for (unsigned int other_mesh_order_idx(mesh_order_idx + 1); other_mesh_order_idx < mesh_order.size(); ++other_mesh_order_idx)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (PolygonRef polyline : layer->openPolyLines)
unsigned int other_mesh_idx = mesh_order[other_mesh_order_idx];
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (other_mesh.getSettingBoolean("infill_mesh"))
{
Polygons segments;
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
AABB3D aabb = storage.meshgroup->meshes[mesh_idx].getAABB();
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx].getAABB();
if (aabb.hit(other_aabb))
{
PolygonRef segment = segments.newPoly();
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
process_infill = true;
}
}
}
}
// skin & infill
// Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
if (mesh.getSettingBoolean("magic_spiralize"))
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
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.
{
processSkinsAndInfill(mesh, layer_number, process_infill);
}
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers)
{
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
mesh.layer_nr_max_filled_layer = -1;
for (unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{
SliceLayer& layer = mesh.layers[layer_idx];
std::vector<PolygonsPart> new_parts;
for (unsigned int other_mesh_idx : mesh_order)
{ // limit the infill mesh's outline to within the infill of all meshes with lower order
if (other_mesh_idx == mesh_idx)
{
break; // all previous meshes have been processed
}
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (layer_idx >= other_mesh.layers.size())
{ // there can be no interaction between the infill mesh and this other non-infill mesh
continue;
}
SliceLayer& other_layer = other_mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
for (SliceLayerPart& other_part : other_layer.parts)
{ // limit the outline of each part of this infill mesh to the infill of parts of the other mesh with lower infill mesh order
if (!part.boundaryBox.hit(other_part.boundaryBox))
{ // early out
continue;
}
Polygons new_outline = part.outline.intersection(other_part.getOwnInfillArea());
if (new_outline.size() == 1)
{ // we don't have to call splitIntoParts, because a single polygon can only be a single part
PolygonsPart outline_part_here;
outline_part_here.add(new_outline[0]);
new_parts.push_back(outline_part_here);
}
else if (new_outline.size() > 1)
{ // we don't know whether it's a multitude of parts because of newly introduced holes, or because the polygon has been split up
std::vector<PolygonsPart> new_parts_here = new_outline.splitIntoParts();
for (PolygonsPart& new_part_here : new_parts_here)
{
new_parts.push_back(new_part_here);
}
}
// change the infill area of the non-infill mesh which is to be filled with e.g. lines
other_part.infill_area_own = other_part.getOwnInfillArea().difference(part.outline);
// note: don't change the part.infill_area, because we change the structure of that area, while the basic area in which infill is printed remains the same
// the infill area remains the same for combing
}
}
}
layer.parts.clear();
for (PolygonsPart& part : new_parts)
{
layer.parts.emplace_back();
layer.parts.back().outline = part;
layer.parts.back().boundaryBox.calculate(part);
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
mesh.layer_nr_max_filled_layer = layer_idx; // last set by the highest non-empty layer
}
}
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
// 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.
combineInfillLayers(mesh,combined_infill_layers);
// fuzzy skin
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzyWalls(mesh);
}
}
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable");
WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall);
walls_computation.generateInsets(layer);
}
}
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++)
{
@@ -299,113 +497,104 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
}
}
void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr)
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
{
for(SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
return;
}
const int wall_line_count = mesh.getSettingAsCount("wall_line_count");
const int innermost_wall_line_width = (wall_line_count == 1) ? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_line_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"));
if (process_infill)
{ // process infill when infill density > 0
// or when other infill meshes want to modify this infill
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
{
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
{
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
}
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
infill_skin_overlap = innermost_wall_line_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_line_width, infill_skin_overlap, wall_line_count);
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
return;
}
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
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++)
{
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist, ClipperLib::jtRound));
}
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
double angle = getSettingInAngleDegrees("ooze_shield_angle");
if (angle <= 89)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height"); // Allow for a 60deg angle in the oozeShield.
for (int layer_nr = 1; layer_nr <= storage.max_object_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--)
{
storage.oozeShield[layer_nr - 1] = storage.oozeShield[layer_nr - 1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
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++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
}
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
{
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
storage.oozeShield[layer_nr].removeSmallAreas(largest_printed_area);
}
}
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
{
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
int layer_height_0 = getSettingInMicrons("layer_height_0");
int layer_height = getSettingInMicrons("layer_height");
if (draft_shield_height < layer_height_0)
const unsigned int draft_shield_layers = getDraftShieldLayerCount(total_layers);
if (draft_shield_layers <= 0)
{
return;
}
unsigned int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
int layer_skip = 500 / layer_height + 1;
const int layer_height = getSettingInMicrons("layer_height");
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 < max_screen_layer; layer_nr += layer_skip)
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < draft_shield_layers; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist);
const int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
storage.draft_protection_shield = draft_shield.approxConvexHull(draft_shield_dist);
}
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
{
SettingsBaseVirtual* train = storage.meshgroup->getExtruderTrain(getSettingBoolean("adhesion_extruder_nr"));
switch(getSettingAsPlatformAdhesion("adhesion_type"))
{
case EPlatformAdhesion::SKIRT:
if (getSettingInMicrons("draft_shield_height") == 0)
{ // draft screen replaces skirt
generateSkirt(storage, getSettingInMicrons("skirt_gap"), getSettingAsCount("skirt_line_count"), getSettingInMicrons("skirt_minimal_length"));
{
constexpr bool outside_polygons_only = true;
SkirtBrim::generate(storage, train->getSettingInMicrons("skirt_gap"), train->getSettingAsCount("skirt_line_count"), outside_polygons_only);
}
break;
case EPlatformAdhesion::BRIM:
generateSkirt(storage, 0, getSettingAsCount("brim_line_count"), getSettingInMicrons("skirt_minimal_length"));
SkirtBrim::generate(storage, 0, train->getSettingAsCount("brim_line_count"), train->getSettingBoolean("brim_outside_only"));
break;
case EPlatformAdhesion::RAFT:
generateRaft(storage, getSettingInMicrons("raft_margin"));
Raft::generate(storage, train->getSettingInMicrons("raft_margin"));
break;
}
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
}
+65 -15
Ver Arquivo
@@ -6,10 +6,12 @@
#include "utils/polygonUtils.h"
#include "utils/NoCopy.h"
#include "utils/gettime.h"
#include "settings.h"
#include "settings/settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
#include "PrintFeature.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
namespace cura
{
@@ -44,7 +46,19 @@ public:
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* \brief Helper function to get the actual height of the draft shield.
*
* The draft shield is the height of the print if we've set the draft shield
* limitation to FULL. Otherwise the height is set to the height limit
* setting. If the draft shield is disabled, the height is always 0.
*
* \param total_layers The total number of layers in the print (the height
* of the draft shield if the limit is FULL.
* \return The actual height of the draft shield.
*/
unsigned int getDraftShieldLayerCount(unsigned int total_layers) const;
/*!
* Slice the \p object and store the outlines in the \p storage.
*
@@ -64,34 +78,67 @@ private:
*/
void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);
/*!
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, skin and infill
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_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);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_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);
/*!
* Process features which are derived from the basic walls, skin, and infill:
* fuzzy skin, infill combine
*
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers over all objects
*/
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers);
/*!
* Remove all bottom layers which are empty.
*
* \warning Changes \p total_layers
*
* \param storage Input and Ouput parameter: stores all layers
* \param layer_height The height of each layer
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
void removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers);
/*!
* Generate the inset polygons which form the walls.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
void processInsets(SliceMeshStorage& mesh, unsigned int layer_nr);
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers
*/
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
void processOozeShield(SliceDataStorage& storage);
/*!
* Generate the skin areas.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the skin areas.
* \param process_infill Generate infill areas
*/
void processSkinsAndInfill(SliceDataStorage& storage, unsigned int layer_nr);
void processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill);
/*!
* Generate the polygons where the draft screen should be.
@@ -100,22 +147,25 @@ private:
* \param total_layers The total number of layers
*/
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Generate the skirt/brim/raft areas/insets.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
*/
void processPlatformAdhesion(SliceDataStorage& storage);
/*!
* Make the outer wall 'fuzzy'
*
* Introduce new vertices and move existing vertices in or out by a random distance, based on the fuzzy skin settings.
*
* This only changes the outer wall.
*
* \param[in,out] mesh where the outer wall is retrieved and stored in.
*/
void processFuzzyWalls(SliceMeshStorage& mesh);
};
}//namespace cura
#endif // FFF_AREA_GENERATOR_H
+35 -31
Ver Arquivo
@@ -5,6 +5,18 @@ namespace cura
FffProcessor FffProcessor::instance; // definition must be in cpp
FffProcessor::FffProcessor()
: polygon_generator(this)
, gcode_writer(this)
, meshgroup_number(0)
{
}
int FffProcessor::getMeshgroupNr()
{
return meshgroup_number;
}
std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup)
{
@@ -27,50 +39,38 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup.meshes[mesh_idx];
sstream << " -e" << mesh.getSettingAsCount("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
sstream << " -e" << mesh.getSettingAsIndex("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
}
sstream << "\n";
return sstream.str();
}
bool FffProcessor::processFiles(const std::vector< std::string >& files)
{
time_keeper.restart();
MeshGroup* meshgroup = new MeshGroup(this);
for(std::string filename : files)
{
log("Loading %s from disk...\n", filename.c_str());
FMatrix3x3 matrix;
if (!loadMeshIntoMeshGroup(meshgroup, filename.c_str(), matrix))
{
logError("Failed to load model: %s\n", filename.c_str());
return false;
}
}
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", time_keeper.restart());
return processMeshGroup(meshgroup);
}
bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, first_meshgroup).c_str()); }
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, meshgroup_number == 0).c_str()); }
time_keeper.restart();
if (!meshgroup)
return false;
TimeKeeper time_keeper_total;
if (meshgroup->meshes.empty())
polygon_generator.setParent(meshgroup);
gcode_writer.setParent(meshgroup);
bool empty = true;
for (Mesh& mesh : meshgroup->meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
{
empty = false;
}
}
if (empty)
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
return true;
}
@@ -103,12 +103,16 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->sendLayerData();
CommandSocket::getInstance()->sendOptimizedLayerData();
}
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
first_meshgroup = false;
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
meshgroup_number++;
polygon_generator.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
gcode_writer.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
return true;
}
+102 -24
Ver Arquivo
@@ -1,13 +1,13 @@
#ifndef FFF_PROCESSOR_H
#define FFF_PROCESSOR_H
#include "settings.h"
#include "settings/settings.h"
#include "FffGcodeWriter.h"
#include "FffPolygonGenerator.h"
#include "commandSocket.h"
#include "Weaver.h"
#include "Wireframe2gcode.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "utils/gettime.h"
#include "utils/NoCopy.h"
@@ -19,67 +19,145 @@ namespace cura {
class FffProcessor : public SettingsBase , NoCopy
{
private:
/*!
* The FffProcessor used for the (current) slicing (The instance of this singleton)
*/
static FffProcessor instance;
FffProcessor()
: polygon_generator(this)
, gcode_writer(this)
, first_meshgroup(true)
{
}
FffProcessor();
public:
/*!
* Get the instance
* \return The instance
*/
static FffProcessor* getInstance()
{
return &instance;
}
/*!
* Get the index of the meshgroup currently being processed, starting at zero.
*/
int getMeshgroupNr();
private:
/*!
* The polygon generator, which slices the models and generates all polygons to be printed and areas to be filled.
*/
FffPolygonGenerator polygon_generator;
/*!
* The gcode writer, which generates paths in layer plans in a buffer, which converts these paths into gcode commands.
*/
FffGcodeWriter gcode_writer;
bool first_meshgroup;
/*!
* The index of the meshgroup currently being processed, starting at zero.
*/
int meshgroup_number;
/*!
* A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* Used in debugging.
*/
std::string profile_string = "";
/*!
* Get all settings for the current meshgroup in the format by which CuraEngine is called via the command line.
*
* Also includes all global settings if this is the first meshgroup.
*
* Used in debugging.
*
* \param meshgroup The meshgroup for which to stringify all settings
* \param first_meshgroup Whether this is the first meshgroup and all global settigns should be included as well
*/
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
public:
/*!
* Get a string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* \return A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*/
std::string getProfileString() { return profile_string; }
/*!
* The stop watch used to time how long the different stages take to compute.
*/
TimeKeeper time_keeper; // TODO: use singleton time keeper
void resetFileNumber()
/*!
* Reset the meshgroup number to the first meshgroup to start a new slicing.
*/
void resetMeshGroupNumber()
{
gcode_writer.resetFileNumber();
meshgroup_number = 0;
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
return gcode_writer.setTargetStream(stream);
}
double getTotalFilamentUsed(int e)
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode_writer.getTotalFilamentUsed(e);
return gcode_writer.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode_writer.getTotalPrintTime();
}
/*!
* Add the end gcode and set all temperatures to zero.
*/
void finalize()
{
gcode_writer.finalize();
}
bool processFiles(const std::vector<std::string> &files);
/*!
* Generate gcode for a given \p meshgroup
* The primary function of this class.
*
* \param meshgroup The meshgroup for which to generate gcode
* \return Whether this function succeeded
*/
bool processMeshGroup(MeshGroup* meshgroup);
};
+111
Ver Arquivo
@@ -0,0 +1,111 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "utils/intpoint.h" // INT2MM
#include "GCodePathConfig.h"
namespace cura
{
GCodePathConfig::BasicConfig::BasicConfig()
: speed(0)
, acceleration(0)
, jerk(0)
, line_width(0)
, flow(100)
{
}
GCodePathConfig::BasicConfig::BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow)
: speed(speed)
, acceleration(acceleration)
, jerk(jerk)
, line_width(line_width)
, flow(flow)
{
}
void GCodePathConfig::BasicConfig::set(double speed, double acceleration, double jerk, int line_width, double flow)
{
this->speed = speed;
this->acceleration = acceleration;
this->jerk = jerk;
this->line_width = line_width;
this->flow = flow;
}
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()
{
return extrusion_mm3_per_mm;
}
double GCodePathConfig::getSpeed()
{
return current_config.speed;
}
double GCodePathConfig::getAcceleration()
{
return current_config.acceleration;
}
double GCodePathConfig::getJerk()
{
return current_config.jerk;
}
int GCodePathConfig::getLineWidth()
{
return current_config.line_width;
}
bool GCodePathConfig::isTravelPath()
{
return current_config.line_width == 0;
}
double GCodePathConfig::getFlowPercentage()
{
return current_config.flow;
}
void GCodePathConfig::calculateExtrusion()
{
extrusion_mm3_per_mm = INT2MM(current_config.line_width) * INT2MM(layer_thickness) * double(current_config.flow) / 100.0;
}
}//namespace cura
+112
Ver Arquivo
@@ -0,0 +1,112 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef G_CODE_PATH_CONFIG_H
#define G_CODE_PATH_CONFIG_H
#include "RetractionConfig.h"
#include "PrintFeature.h"
namespace cura
{
/*!
* The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
*/
class GCodePathConfig
{
friend class GCodePlannerTest;
public:
/*!
* The path config settings which may change from layer to layer
*/
struct BasicConfig
{
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
};
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
/*!
* Basic 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);
/*!
* 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 !
*
* \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();
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM();
/*!
* Get the movement speed in mm/s
*/
double getSpeed();
/*!
* Get the current acceleration of this config
*/
double getAcceleration();
/*!
* Get the current jerk of this config
*/
double getJerk();
int getLineWidth();
bool isTravelPath();
double getFlowPercentage();
private:
void calculateExtrusion();
};
}//namespace cura
#endif // G_CODE_PATH_CONFIG_H
+126 -102
Ver Arquivo
@@ -3,6 +3,7 @@
#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "FffProcessor.h"
namespace cura {
@@ -16,9 +17,11 @@ void LayerPlanBuffer::flush()
}
while (!buffer.empty())
{
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
buffer.front().writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
}
@@ -27,51 +30,52 @@ void LayerPlanBuffer::flush()
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
{
double acc_time = 0.0;
for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
for (unsigned int path_idx = extruder_plan_before.paths.size() - 1; int(path_idx) != -1 ; path_idx--)
{
GCodePath& path = extruder_plan_before.paths[path_idx];
acc_time += path.estimates.getTotalTime();
const double time_this_path = path.estimates.getTotalTime();
acc_time += time_this_path;
if (acc_time > time_after_extruder_plan_start)
{
// logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
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);
return;
}
}
extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
// = special insert after all extruder plans
extruder_plan_before.insertCommand(0, extruder, temp, false); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
}
double LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
bool first_it = true;
double in_between_time = 0.0;
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
{
GCodePlanner& layer = *layers[layer_idx];
if (!first_it)
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)
{
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
Preheat::WarmUpResult warm_up = preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + extra_preheat_time);
return warm_up;
}
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
if (extruder_plan.extruder == extruder)
{
return preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
}
in_between_time += extruder_plan.estimates.getTotalTime();
}
first_it = false;
in_between_time += extruder_plan.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.
return preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(preheat_config.getStandbyTemp(extruder), extruder, required_temp, false);
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);
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.heating_time = warm_up.heating_time + extra_preheat_time;
return warm_up;
}
@@ -79,95 +83,81 @@ void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_ex
{
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
if (time_after_extruder_plan_start < 0)
{
time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
}
insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
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);
}
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
void LayerPlanBuffer::handleStandbyTemp(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx, double standby_temp)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 2; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
if (extruder_plans[extruder_plan_before_idx]->extruder == extruder)
{
extruder_plans[extruder_plan_before_idx + 1]->prev_extruder_standby_temp = standby_temp;
return;
}
}
logWarning("Warning: Couldn't find previous extruder plan so as to set the standby temperature. Inserting temp command in earliest available layer.\n");
ExtruderPlan& earliest_extruder_plan = *extruder_plans[0];
constexpr bool wait = false;
earliest_extruder_plan.insertCommand(0, extruder, standby_temp, wait);
}
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(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;
extruder_plan.insertCommand(0, extruder, required_temp, true); // just after the extruder switch, wait for the destination temperature to be reached
double time_before_extruder_plan_to_insert = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);
unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
bool first_it = true; // Whether it's the first iteration of the for loop below
for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
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))
{
GCodePlanner& layer = *layers[layer_idx];
if (!first_it)
handleStandbyTemp(extruder_plans, extruder_plan_idx, required_temp);
return; // don't insert preheat command and just stay on printing temperature
}
else
{
handleStandbyTemp(extruder_plans, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);
}
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)
{
extruder_plan_before_idx = layer.extruder_plans.size() - 1;
insertPreheatCommand(extruder_plan_before, time_before_extruder_plan_to_insert, extruder, required_temp);
return;
}
for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
assert (extruder_plan_before.extruder != extruder);
double time_here = extruder_plan_before.estimates.getTotalTime();
if (time_here > time_before_extruder_plan_to_insert)
{
insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
return;
}
time_before_extruder_plan_to_insert -= time_here;
}
first_it = false;
time_before_extruder_plan_to_insert -= time_here;
}
// time_before_extruder_plan_to_insert falls before all plans in the buffer
ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
extruder_plans[0]->insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
}
void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
void LayerPlanBuffer::insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[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 = nullptr;
if (extruder_plan_idx == 0)
{
if (layer_plan_idx == 0)
{ // the very first extruder plan
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
{ // set temperature of the first nozzle, turn other nozzles down
if (extruder_idx == extruder)
{
// extruder_plan.insertCommand(0, extruder, required_temp, true);
// the first used extruder should already be set to the required temp in the start gcode
}
else
{
extruder_plan.insertCommand(0, extruder_idx, preheat_config.getStandbyTemp(extruder_idx), false);
}
}
return;
}
prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
}
else
{
prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
}
assert(prev_extruder_plan != nullptr);
ExtruderPlan* prev_extruder_plan = extruder_plans[extruder_plan_idx - 1];
int prev_extruder = prev_extruder_plan->extruder;
if (prev_extruder != extruder)
{ // set previous extruder to standby temperature
prev_extruder_plan->insertCommand(prev_extruder_plan->paths.size(), prev_extruder, preheat_config.getStandbyTemp(prev_extruder), false);
extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(prev_extruder);
}
if (prev_extruder == extruder)
@@ -179,7 +169,7 @@ void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, u
}
else
{
insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
insertPreheatCommand_multiExtrusion(extruder_plans, extruder_plan_idx);
}
}
@@ -191,18 +181,20 @@ void LayerPlanBuffer::insertPreheatCommands()
buffer.pop_back();
return;
}
std::vector<GCodePlanner*> layers;
layers.reserve(buffer.size());
std::vector<ExtruderPlan*> extruder_plans;
extruder_plans.reserve(buffer.size() * 2);
for (GCodePlanner& layer_plan : buffer)
{
layers.push_back(&layer_plan);
for (ExtruderPlan& extr_plan : layer_plan.extruder_plans)
{
extruder_plans.push_back(&extr_plan);
}
}
unsigned int layer_idx = layers.size() - 1;
// insert commands for all extruder plans on this layer
GCodePlanner& layer_plan = *layers[layer_idx];
GCodePlanner& layer_plan = buffer.back();
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
{
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
@@ -213,10 +205,42 @@ void LayerPlanBuffer::insertPreheatCommands()
{
continue;
}
double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);
insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
if (buffer.size() == 1 && extruder_plan_idx == 0)
{ // the very first extruder plan of the current meshgroup
int extruder = extruder_plan.extruder;
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
{ // set temperature of the first nozzle, turn other nozzles down
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{
// override values from GCodeExport::setInitialTemps
// the first used extruder should be set to the required temp in the start gcode
// see FffGcodeWriter::processStartingCode
if (extruder_idx == extruder)
{
gcode.setInitialTemp(extruder_idx, extruder_plan.required_temp);
}
else
{
gcode.setInitialTemp(extruder_idx, preheat_config.getStandbyTemp(extruder_idx));
}
}
else
{
if (extruder_idx != extruder)
{ // TODO: do we need to do this?
extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(extruder_idx);
}
}
}
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);
}
}
+31 -18
Ver Arquivo
@@ -1,9 +1,10 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef LAYER_PLAN_BUFFER_H
#define LAYER_PLAN_BUFFER_H
#include <list>
#include "settings.h"
#include "settings/settings.h"
#include "commandSocket.h"
#include "gcodeExport.h"
@@ -23,7 +24,9 @@ class LayerPlanBuffer : SettingsMessenger
static constexpr unsigned int buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value
// this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted.
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.
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
@@ -51,9 +54,11 @@ public:
buffer.emplace_back(constructor_args...);
if (buffer.size() > buffer_size)
{
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
buffer.front().writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
}
return buffer.back();
@@ -68,22 +73,21 @@ public:
* Insert the preheat command for @p extruder into @p extruder_plan_before
*
* \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command
* \param time_after_extruder_plan_start The time after the start of the extruder plan, before which to insert the preheat command
* \param time_before_extruder_plan_end The time before the end of the extruder plan, before which to insert the preheat command
* \param extruder The extruder for which to set the temperature
* \param temp The temperature of the preheat command
*/
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp);
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_before_extruder_plan_end, int extruder, double temp);
/*!
* Compute the time needed to preheat, based either on the time the extruder has been on standby
* or based on the temp of the previous extruder plan which has the same extruder nr.
*
* \param layers The layers in the buffer, moved to a vector
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
* \return the time needed to preheat
* \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 find the preheat time needed
* \return the time needed to preheat and the temperature from which heating starts
*/
double timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
Preheat::WarmUpResult timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* For two consecutive extruder plans of the same extruder (so on different layers),
@@ -103,25 +107,34 @@ public:
* and compute at what time the preheat command needs to be inserted.
* Then insert the preheat command in the right extruder plan.
*
* \param layers The layers in the buffer, moved to a vector
* \param layer_plan_idx The index into @p layers in which to find the extruder plan
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to find the preheat time needed
* \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 find the preheat time needed
*/
void insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
void insertPreheatCommand_multiExtrusion(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the preheat command for the extruder plan corersponding to @p extruder_plan_idx of the layer corresponding to @p layer_plan_idx.
*
* \param layers The layers of the buffer, moved to a temporary vector (from lower to upper layers)
* \param layer_plan_idx The index of the layer plan for which to generate a preheat command
* \param extruder_plan_idx The index of the extruder plan in the layer corresponding to @p layer_plan_idx for which to generate the preheat command
* \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<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx);
void insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the preheat commands for the last added layer (unless that layer was empty)
*/
void insertPreheatCommands();
private:
/*!
* 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]
* Set the prev_extruder_standby_temp in the next extruder plan
*
* \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 before which to reconfigure the standby temperature
* \param standby_temp The temperature to which to cool down when the extruder is in standby mode.
*/
void handleStandbyTemp(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx, double standby_temp);
};
+12 -8
Ver Arquivo
@@ -11,14 +11,18 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
{
double old_line_width = INT2MM(last_path.config->getLineWidth());
double new_line_width_mm = INT2MM(new_line_width);
double speed_mod = old_line_width / new_line_width_mm;
double extrusion_mod = new_line_width_mm / old_line_width;
double new_speed = std::min(speed * speed_mod, 150.0); // TODO: hardcoded value: max extrusion speed is 150 mm/s = 9000 mm/min
sendPolygon(last_path.config->type, gcode.getPositionXY(), to, last_path.getLineWidth());
double new_speed = speed;
if (speed_equalize_flow_enabled)
{
double speed_mod = old_line_width / new_line_width_mm;
new_speed = std::min(speed * speed_mod, speed_equalize_flow_max);
}
sendLineTo(last_path.config->type, to, last_path.getLineWidth());
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
bool MergeInfillLines::mergeInfillLines(unsigned int& path_idx)
{ //Check for lots of small moves and combine them into one large line
Point prev_middle;
Point last_middle;
@@ -31,12 +35,12 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
GCodePath& move_path = paths[path_idx];
for(unsigned int point_idx = 0; point_idx < move_path.points.size() - 1; point_idx++)
{
gcode.writeMove(move_path.points[point_idx], speed, move_path.getExtrusionMM3perMM());
gcode.writeMove(move_path.points[point_idx], move_path.config->getSpeed() * extruder_plan.getTravelSpeedFactor(), move_path.getExtrusionMM3perMM());
}
gcode.writeMove(prev_middle, travelConfig.getSpeed(), 0);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, speed, last_path, line_width);
writeCompensatedMove(last_middle, last_path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(), last_path, line_width);
}
path_idx += 2;
@@ -45,7 +49,7 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, speed, last_path, line_width);
writeCompensatedMove(last_middle, last_path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(), last_path, line_width);
}
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
extruder_plan.handleInserts(path_idx, gcode);
@@ -227,4 +231,4 @@ void MergeInfillLines::merge(Point& from, Point& p0, Point& p1)
}//namespace cura
}//namespace cura
+10 -16
Ver Arquivo
@@ -4,6 +4,7 @@
#include "utils/intpoint.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "GCodePathConfig.h"
namespace cura
{
@@ -18,6 +19,8 @@ class MergeInfillLines
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
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
@@ -61,8 +64,8 @@ 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)
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size, 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.
@@ -72,28 +75,19 @@ public:
* \param paths The paths currently under consideration
* \param travelConfig The travel settings used to see whether a path is a travel path or an extrusion path
* \param nozzle_size The diameter of the hole in the nozzle
* \param speed A factor used to scale the movement speed
* \param path_idx Input/Output parameter: The current index in \p paths where to start combining and the current index after combining as output parameter.
* \return Whether lines have been merged and normal path-to-gcode generation can be skipped for the current resulting \p path_idx .
*/
bool mergeInfillLines(double speed, unsigned int& path_idx);
bool mergeInfillLines(unsigned int& path_idx);
/*!
* send a polygon through the command socket from the previous point to the given point
* send a line segment through the command socket from the previous point to the given point \p to
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width)
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
CommandSocket::sendLineTo(print_feature_type, to, line_width);
}
};
}//namespace cura
#endif // MERGE_INFILL_LINES_H
#endif // MERGE_INFILL_LINES_H
+163 -5
Ver Arquivo
@@ -4,9 +4,12 @@
#include <stdio.h>
#include "MeshGroup.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/string.h"
#include "settings/SettingRegistry.h" // loadExtruderJSONsettings
namespace cura
{
@@ -28,7 +31,159 @@ void* fgets_(char* ptr, size_t len, FILE* f)
return nullptr;
}
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
MeshGroup::MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
{}
MeshGroup::~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
int MeshGroup::getExtruderCount() const
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
int err = SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, extruders[extruder_nr]);
if (err)
{
logError("Couldn't load extruder.def.json for extruder %i\n", extruder_nr);
std::exit(1);
}
}
return extruders[extruder_nr];
}
ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr)
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
const ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr) const
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
Point3 MeshGroup::min() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 MeshGroup::max() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void MeshGroup::clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void MeshGroup::finalize()
{
extruder_count = getSettingAsCount("machine_extruder_count");
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
createExtruderTrain(extruder_nr); // create it if it didn't exist yet
if (getSettingAsIndex("adhesion_extruder_nr") == extruder_nr)
{
getExtruderTrain(extruder_nr)->setIsUsed(true);
continue;
}
for (const Mesh& mesh : meshes)
{
if (mesh.getSettingBoolean("support_enable")
&& (
getSettingAsIndex("support_infill_extruder_nr") == extruder_nr
|| getSettingAsIndex("support_extruder_nr_layer_0") == extruder_nr
|| (getSettingBoolean("support_interface_enable") && getSettingAsIndex("support_interface_extruder_nr") == extruder_nr)
)
)
{
getExtruderTrain(extruder_nr)->setIsUsed(true);
break;
}
}
}
for (const Mesh& mesh : meshes)
{
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.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rt");
char buffer[1024];
@@ -61,7 +216,7 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return true;
}
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
@@ -114,7 +269,7 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return true;
}
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
if (f == nullptr)
@@ -168,8 +323,10 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
return loadMeshSTL_binary(mesh, filename, matrix);
}
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
{
TimeKeeper load_timer;
const char* ext = strrchr(filename, '.');
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
{
@@ -177,10 +334,11 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
{
meshgroup->meshes.push_back(mesh);
log("loading '%s' took %.3f seconds\n",filename,load_timer.restart());
return true;
}
}
return false;
}
}//namespace cura
}//namespace cura
+15 -102
Ver Arquivo
@@ -18,118 +18,31 @@ namespace cura
class MeshGroup : public SettingsBase, NoCopy
{
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
int extruder_count;
mutable int extruder_count; //!< The number of extruders. (mutable because of lazy evaluation)
public:
int getExtruderCount()
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
int getExtruderCount() const;
MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
{}
MeshGroup(SettingsBaseVirtual* settings_base);
~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
~MeshGroup();
/*!
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
*/
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
}
return extruders[extruder_nr];
}
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr)
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr);
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr);
const ExtruderTrain* getExtruderTrain(unsigned int extruder_nr) const;
std::vector<Mesh> meshes;
Point3 min() //! minimal corner of bounding box
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 max() //! maximal corner of bounding box
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
Point3 min() const; //! minimal corner of bounding box
Point3 max() const; //! maximal corner of bounding box
void clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void clear();
void finalize()
{
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
void finalize();
};
/*!
@@ -141,7 +54,7 @@ public:
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
* \return whether the file could be loaded
*/
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
}//namespace cura
#endif//MESH_GROUP_H
+49 -8
Ver Arquivo
@@ -33,6 +33,8 @@ class Preheat
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)
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
@@ -42,6 +44,16 @@ class Preheat
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
public:
/*!
* The type of result when computing when to start heating up a nozzle before it's going to be used again.
*/
struct WarmUpResult
{
double total_time_window; //!< The total time in which cooling and heating takes place.
double heating_time; //!< The total time needed to heat to the required temperature.
double lowest_temperature; //!< The lower temperature from which heating starts.
};
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
@@ -51,7 +63,18 @@ public:
{
return config_per_extruder[extruder].standby_temp;
}
/*!
* 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
* \return the time it takes to heat up one degree celsius
*/
double getTimeToHeatup1Degree(int extruder)
{
return config_per_extruder[extruder].time_to_heatup_1_degree;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
*/
@@ -67,7 +90,9 @@ public:
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
config.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");
@@ -105,7 +130,17 @@ public:
{
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
/*!
* Return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
* \param extruder The extruder for which to get the minimal time window
* \return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
*/
double getMinimalTimeWindow(unsigned int extruder)
{
return config_per_extruder[extruder].min_time_window;
}
/*!
* Decide when to start warming up again after starting to cool down towards the standby temperature.
* Two cases are considered:
@@ -119,21 +154,27 @@ public:
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \return The time before the end of the @p time_window to insert the preheat command
* \return The time before the end of the @p time_window to insert the preheat command and the temperature from which the heating starts
*/
double timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
WarmUpResult timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
{
double time_ratio_cooldown_heatup = config_per_extruder[extruder].time_to_cooldown_1_degree / config_per_extruder[extruder].time_to_heatup_1_degree;
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)
{
return time_to_heat_from_standby_to_print_temp;
result.heating_time = time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = config.standby_temp;
}
else
{
return time_window * config_per_extruder[extruder].time_to_heatup_1_degree / (config_per_extruder[extruder].time_to_cooldown_1_degree + config_per_extruder[extruder].time_to_heatup_1_degree);
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;
}
/*!
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
+163 -165
Ver Arquivo
@@ -1,5 +1,7 @@
#include "PrimeTower.h"
#include <limits>
#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
@@ -9,34 +11,38 @@
namespace cura
{
PrimeTower::PrimeTower()
: current_pre_wipe_location_idx(0)
{
}
void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder)
{
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
for (int extr = 0; extr < extruder_count; extr++)
for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
{
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
last_prime_tower_poly_printed[extruder_nr] = -1;
}
}
void PrimeTower::setConfigs(MeshGroup* meshgroup, int layer_thickness)
void PrimeTower::initConfigs(const MeshGroup* meshgroup)
{
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
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];
@@ -49,7 +55,7 @@ void PrimeTower::setConfigs(MeshGroup* meshgroup, int layer_thickness)
void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{ // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the prime tower
extruder_count = storage.getSettingAsCount("machine_extruder_count");
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
@@ -61,13 +67,13 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
std::max( max_object_height_per_extruder[extr_nr]
, mesh.layer_nr_max_filled_layer );
}
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_extruder_nr] =
std::max( max_object_height_per_extruder[support_extruder_nr]
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_infill_extruder_nr] =
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
max_object_height_per_extruder[support_roof_extruder_nr] =
std::max( max_object_height_per_extruder[support_roof_extruder_nr]
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
@@ -92,19 +98,18 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{
storage.max_object_height_second_to_last_extruder = -1;
}
else
else
{
storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height];
}
}
}
void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
PolygonRef p = ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
@@ -112,68 +117,28 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
void PrimeTower::generatePaths(const SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
&& storage.getSettingInMicrons("prime_tower_size") > 0)
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
{
generatePaths3(storage);
generatePaths_denseInfill(storage);
generateWipeLocations(storage);
}
}
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0; //storage.getSettingInMicrons("prime_tower_distance");
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
}
void PrimeTower::generatePaths2(SliceDataStorage& storage) // half baked attempt at spiral shaped prime tower pattern
void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
{
// extruder_count = storage.getSettingAsCount("machine_extruder_count");
//
// int64_t line_dists[extruder_count + 1]; // distance between the lines of different extruders, and half the line dist for beginning and ending
// int64_t total_width = 0;
// {
// int64_t last_line_width = 0;
// for (int extr = 0; extr < extruder_count; extr++)
// {
// int64_t line_width = config_per_extruder[extr].getLineWidth();
// line_dists[extr] = (line_width + last_line_width) / 2;
// total_width += line_width;
// last_line_width = line_width;
// }
// line_dists[extruder_count] = last_line_width / 2;
// }
//
}
void PrimeTower::generatePaths3(SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
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");
@@ -182,25 +147,20 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int outline_offset = -line_width/2;
int outline_offset = -line_width;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
infill_comp.generate(result_polygons, result_lines, in_between);
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);
}
}
}
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, bool wipe)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
&& storage.getSettingInMicrons("prime_tower_size") > 0) )
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
{
return;
}
@@ -213,99 +173,137 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
{ // 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)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
if (prev_extruder == gcodeLayer.getExtruder())
{
wipe = false;
}
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
// pre-wipe:
if (wipe)
{
preWipe(storage, gcodeLayer, new_extruder);
}
addToGcode_denseInfill(storage, gcodeLayer, gcode, layer_nr, prev_extruder);
// post-wipe:
if (false && wipe) // TODO: make a separate setting for the 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));
}
}
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
Polygons& pattern = patterns_per_extruder[new_extruder][layer_nr % 2];
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
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;
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
}
if (wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
}
CommandSocket::sendPolygons(PrintFeatureType::Support, pattern, config.getLineWidth());
}
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
Point ret(0, 0);
int absolute_starting_points = 0;
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
int64_t offset = -config_per_extruder[new_extruder].getLineWidth();
if (layer_nr > 0)
offset *= 2;
//If we changed extruder, print the wipe/prime tower for this nozzle;
std::vector<Polygons> insets;
{ // generate polygons
if ((layer_nr % 2) == 1)
insets.push_back(storage.primeTower.ground_poly.offset(offset / 2));
else
insets.push_back(storage.primeTower.ground_poly);
while(true)
ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0);
if (train.getSettingBoolean("machine_extruder_start_pos_abs"))
{
Polygons new_inset = insets[insets.size() - 1].offset(offset);
if (new_inset.size() < 1)
break;
insets.push_back(new_inset);
ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y"));
absolute_starting_points++;
}
}
for(unsigned int n=0; n<insets.size(); n++)
if (absolute_starting_points > 0)
{ // take the average over all absolute starting positions
ret /= absolute_starting_points;
}
else
{ // use the middle of the bed
if (!storage.getSettingBoolean("machine_center_is_zero"))
{
ret = Point(storage.getSettingInMicrons("machine_width"), storage.getSettingInMicrons("machine_depth")) / 2;
}
// otherwise keep (0, 0)
}
return ret;
}
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
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)
{
GCodePathConfig& config = config_per_extruder[new_extruder];
gcodeLayer.addPolygonsByOptimizer(insets[(prime_tower_dir_outward)? insets.size() - 1 - n : n], &config);
segment_start = prev;
segment_end = closest_vert;
}
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
else
{
segment_start = closest_vert;
segment_end = next;
}
};
}//namespace cura
// TODO: come up with alternatives for better segments once the prime tower can be different shapes
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)
{
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 ;
const int start_dist = train.getSettingInMicrons("machine_nozzle_size") * 2;
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
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);
}
}//namespace cura
+118 -28
Ver Arquivo
@@ -1,9 +1,12 @@
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
#include "gcodeExport.h" // GCodePathConfig
#include <vector>
#include "GCodePathConfig.h"
#include "MeshGroup.h"
#include "utils/polygon.h" // Polygons
#include "utils/polygonUtils.h"
namespace cura
{
@@ -15,49 +18,136 @@ class GCodeExport;
typedef std::vector<IntPoint> PolyLine;
/*!
* Class for everything to do with the prime tower:
* - generating the areas
* - checking up till which height the prime tower has to be printed
* - generating the paths and adding them to the layer plan
*/
class PrimeTower
{
private:
int extruder_count;
std::vector<GCodePathConfig> config_per_extruder;
int extruder_count; //!< number of extruders
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
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
class WallInfill
{
};
public:
void initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder);
void setConfigs(MeshGroup* configs, int layer_thickness);
Polygons ground_poly;
std::vector<PolyLine> extruder_paths;
void generateGroundpoly(SliceDataStorage& storage);
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);
/*!
* 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
*/
void setConfigs(const MeshGroup* meshgroup, const int layer_thickness);
/*!
* Generate the prime tower area to be used on each layer
*
* \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
void generatePaths3(SliceDataStorage& storage);
void generatePaths2(SliceDataStorage& storage);
/*!
* Generate the area where the prime tower should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param storage where to get settings from
* \param total_layers The total number of layers
*/
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
void 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();
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed);
PrimeTower(); //!< basic constructor
/*!
* 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 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)
*/
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, bool wipe);
private:
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
/*!
* Find an approriate representation for the point representing the location before going to the prime tower
*
* \warning This is not the actual position each time before the wipe tower
*
* \param storage where to get settings from
* \return that location
*/
Point getLocationBeforePrimeTower(const SliceDataStorage& storage);
/*!
* \param storage where to get settings from
* Depends on ground_poly being generated
*/
void generateWipeLocations(const SliceDataStorage& storage);
/*!
* \see WipeTower::generatePaths
*
* 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_denseInfill(const SliceDataStorage& storage);
/*!
* \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
*/
void addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder);
/*!
* 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 extruder_nr The current extruder
*/
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr);
};
+6 -5
Ver Arquivo
@@ -4,18 +4,19 @@
namespace cura
{
enum class PrintFeatureType
enum class PrintFeatureType: unsigned char
{
NoneType, // unused, but libArcus depends on it
NoneType, // used to mark unspecified jumps in polygons. libArcus depends on it
OuterWall,
InnerWall,
Skin,
Support,
Skirt,
SkirtBrim,
Infill,
SupportInfill,
MoveCombing,
MoveRetraction
MoveRetraction,
SupportInterface
};
@@ -23,4 +24,4 @@ enum class PrintFeatureType
} // namespace cura
#endif // PRINT_FEATURE
#endif // PRINT_FEATURE
+28
Ver Arquivo
@@ -0,0 +1,28 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef RETRACTION_CONFIG_H
#define RETRACTION_CONFIG_H
namespace cura
{
/*!
* The retraction configuration used in the GCodePathConfig of each feature (and the travel config)
*/
class RetractionConfig
{
public:
double distance; //!< The distance retracted (in mm)
double speed; //!< The speed with which to retract (in mm/s)
double primeSpeed; //!< the speed with which to unretract (in mm/s)
double prime_volume; //!< the amount of material primed after unretracting (in mm^3)
int zHop; //!< the amount with which to lift the head during a retraction-travel
int retraction_min_travel_distance; //!< Minimal distance traversed to even consider retracting (in micron)
double retraction_extrusion_window; //!< Window of mm extruded filament in which to limit the amount of retractions
int retraction_count_max; //!< The maximum amount of retractions allowed to occur in the RetractionConfig::retraction_extrusion_window
};
}//namespace cura
#endif // RETRACTION_CONFIG_H
+187
Ver Arquivo
@@ -0,0 +1,187 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "SkirtBrim.h"
#include "support.h"
namespace cura
{
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline)
{
bool external_only = is_skirt; // whether to include holes or not
const int layer_nr = 0;
if (is_skirt)
{
const bool include_helper_parts = true;
first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
first_layer_outline = first_layer_outline.approxConvexHull();
}
else
{ // 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
Polygons first_layer_empty_holes;
if (outside_only)
{
first_layer_empty_holes = first_layer_outline.getEmptyHoles();
first_layer_outline = first_layer_outline.removeEmptyHoles();
}
if (storage.support.generated && primary_line_count > 0)
{ // remove model-brim from support
// avoid gap in the middle
// V
// +---+ +----+
// |+-+| |+--+|
// || || ||[]|| > 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
if (outside_only)
{ // don't remove support within empty holes where no brim is generated.
model_brim_covered_area.add(first_layer_empty_holes);
}
SupportLayer& support_layer = storage.support.supportLayers[0];
support_layer.supportAreas = support_layer.supportAreas.difference(model_brim_covered_area);
first_layer_outline.add(support_layer.supportAreas);
first_layer_outline.add(support_layer.skin);
}
}
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
}
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)
{
int offset_distance = start_distance - primary_extruder_skirt_brim_line_width / 2;
for (unsigned int skirt_brim_number = 0; skirt_brim_number < primary_line_count; skirt_brim_number++)
{
offset_distance += primary_extruder_skirt_brim_line_width;
Polygons outer_skirt_brim_line = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
//Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
{
double area = outer_skirt_brim_line[n].area();
if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
{
outer_skirt_brim_line.remove(n--);
}
}
skirt_brim_primary_extruder.add(outer_skirt_brim_line);
int length = skirt_brim_primary_extruder.polygonLength();
if (skirt_brim_number + 1 >= primary_line_count && length > 0 && length < primary_extruder_minimal_length) //Make brim or skirt have more lines when total length is too small.
{
primary_line_count++;
}
}
return offset_distance;
}
void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, bool outside_only)
{
const bool is_skirt = start_distance > 0;
const int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
const ExtruderTrain* adhesion_extruder = storage.meshgroup->getExtruderTrain(adhesion_extruder_nr);
const int primary_extruder_skirt_brim_line_width = adhesion_extruder->getSettingInMicrons("skirt_brim_line_width");
const int64_t primary_extruder_minimal_length = adhesion_extruder->getSettingInMicrons("skirt_brim_minimal_length");
Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];
Polygons first_layer_outline;
getFirstLayerOutline(storage, primary_line_count, primary_extruder_skirt_brim_line_width, is_skirt, outside_only, first_layer_outline);
const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
const bool has_draft_shield = storage.draft_protection_shield.size() > 0;
if (is_skirt && (has_ooze_shield || has_draft_shield))
{ // make sure we don't generate skirt through draft / ooze shield
first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
if (has_ooze_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
}
first_layer_outline = first_layer_outline.approxConvexHull();
start_distance = primary_extruder_skirt_brim_line_width / 2;
}
int offset_distance = generatePrimarySkirtBrimLines(storage, start_distance, primary_line_count, primary_extruder_skirt_brim_line_width, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);
// generate brim for ooze shield and draft shield
if (!is_skirt && (has_ooze_shield || has_draft_shield))
{
// generate areas where to make extra brim for the shields
// avoid gap in the middle
// V
// +---+ +----+
// |+-+| |+--+|
// || || ||[]|| > expand to fit an extra brim line
// |+-+| |+--+|
// +---+ +----+
const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides
Polygons shield_brim;
if (has_ooze_shield)
{
shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
}
if (has_draft_shield)
{
shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
}
const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));
// generate brim within shield_brim
skirt_brim_primary_extruder.add(shield_brim);
while (shield_brim.size() > 0)
{
shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
skirt_brim_primary_extruder.add(shield_brim);
}
// update parameters to generate secondary skirt around
first_layer_outline = outer_primary_brim;
if (has_draft_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
}
if (has_ooze_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
}
offset_distance = 0;
}
{ // process other extruders' brim/skirt (as one brim line around the old brim)
int last_width = primary_extruder_skirt_brim_line_width;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{
if (extruder == adhesion_extruder_nr || !storage.meshgroup->getExtruderTrain(extruder)->getIsUsed())
{
continue;
}
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
const int width = train->getSettingInMicrons("skirt_brim_line_width");
const int64_t minimal_length = train->getSettingInMicrons("skirt_brim_minimal_length");
offset_distance += last_width / 2 + width/2;
last_width = width;
while (storage.skirt_brim[extruder].polygonLength() < minimal_length)
{
storage.skirt_brim[extruder].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
offset_distance += width;
}
}
}
}
}//namespace cura
+59
Ver Arquivo
@@ -0,0 +1,59 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIRT_BRIM_H
#define SKIRT_BRIM_H
#include "sliceDataStorage.h"
namespace cura
{
class SkirtBrim
{
public:
/*!
* Generate skirt or brim (depending on parameters).
*
* When \p distance > 0 and \p count == 1 a skirt is generated, which has
* slightly different configuration. Otherwise, a brim is generated.
*
* \param storage Storage containing the parts at the first layer.
* \param distance The distance of the first outset from the parts at the first
* layer.
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param outside_only Whether to only generate a brim on the outside, rather than also in holes
*/
static void generate(SliceDataStorage& storage, int distance, unsigned int primary_line_count, bool outside_only);
private:
/*!
* Get the reference outline of the first layer around which to generate the first brim/skirt line.
*
* This function may change the support polygons in the first layer
* in order to meet criteria for putting brim around the model as well as around the support.
*
* \param storage Storage containing the parts at the first layer.
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param primary_extruder_skirt_brim_line_width Line widths of the initial skirt/brim lines
* \param is_skirt Whether a skirt is being generated vs a brim
* \param outside_only Whether to only generate a brim on the outside, rather than also in holes
* \param[out] first_layer_outline The resulting reference polygons
*/
static void getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline);
/*!
* Generate the skirt/brim lines around the model
*
* \param storage Storage containing the parts at the first layer.
* \param start_distance The distance of the first outset from the parts at the first
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param primary_extruder_skirt_brim_line_width Line widths of the initial skirt/brim lines
* \param primary_extruder_minimal_length The minimal total length of the skirt/brim lines of the primary extruder
* \param first_layer_outline The reference polygons from which to offset outward to generate skirt/brim lines
* \param[out] skirt_brim_primary_extruder Where to store the resulting brim/skirt lines in
* \return The offset of the last brim/skirt line from the reference polygon \p first_layer_outline
*/
static int 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);
};
}//namespace cura
#endif //SKIRT_BRIM_H
+80
Ver Arquivo
@@ -0,0 +1,80 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "WallsComputation.h"
#include "utils/polygonUtils.h"
namespace cura {
WallsComputation::WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall)
: wall_0_inset(wall_0_inset)
, line_width_0(line_width_0)
, line_width_x(line_width_x)
, insetCount(insetCount)
, recompute_outline_based_on_outer_wall(recompute_outline_based_on_outer_wall)
{
}
void WallsComputation::generateInsets(SliceLayerPart* part)
{
if (insetCount == 0)
{
part->insets.push_back(part->outline);
part->print_outline = part->outline;
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
part->insets[0] = part->outline.offset(-line_width_0 / 2 - wall_0_inset);
} else if (i == 1)
{
part->insets[1] = part->insets[0].offset(-line_width_0 / 2 + wall_0_inset - line_width_x / 2);
} else
{
part->insets[i] = part->insets[i-1].offset(-line_width_x);
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
part->insets[i].removeDegenerateVerts();
if (i == 0)
{
if (recompute_outline_based_on_outer_wall)
{
part->print_outline = part->insets[0].offset(line_width_0 / 2);
}
else
{
part->print_outline = part->outline;
}
}
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void WallsComputation::generateInsets(SliceLayer* layer)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr]);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
+69
Ver Arquivo
@@ -0,0 +1,69 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef WALLS_COMPUTATION_H
#define WALLS_COMPUTATION_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Function container for computing the outer walls / insets / perimeters polygons of a layer
*/
class WallsComputation
{
public:
/*!
* The offset applied to the outer wall
*/
int wall_0_inset;
/*!
* line width of the outer wall
*/
int line_width_0;
/*!
* line width of other walls
*/
int line_width_x;
/*!
* The number of insets to to generate
*/
int insetCount;
/*!
* Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
bool recompute_outline_based_on_outer_wall;
/*!
* Basic constructor initializing the parameters with which to perform the walls computation
*
* \param wall_0_inset The offset applied to the outer wall
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param recompute_outline_based_on_outer_wall Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by WallsComputation::line_width_0 instead of the first,
* which leads to better results for a smaller WallsComputation::line_width_0 than WallsComputation::line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
*/
void generateInsets(SliceLayer* layer);
private:
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
*/
void generateInsets(SliceLayerPart* part);
};
}//namespace cura
#endif//WALLS_COMPUTATION_H
+20 -34
Ver Arquivo
@@ -4,7 +4,7 @@
#include <fstream> // debug IO
#include <unistd.h>
#include "Progress.h"
#include "progress/Progress.h"
#include "weaveDataStorage.h"
#include "PrintFeature.h"
@@ -19,7 +19,7 @@ void Weaver::weave(MeshGroup* meshgroup)
int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;
DEBUG_SHOW(layer_count);
std::cerr << "Layer count: " << layer_count << "\n";
std::vector<cura::Slicer*> slicerList;
@@ -35,14 +35,14 @@ void Weaver::weave(MeshGroup* meshgroup)
{
Polygons parts;
for (cura::Slicer* slicer : slicerList)
parts.add(slicer->layers[starting_layer_idx].polygonList);
parts.add(slicer->layers[starting_layer_idx].polygons);
if (parts.size() > 0)
break;
}
if (starting_layer_idx > 0)
{
logError("First %i layers are empty!\n", starting_layer_idx);
logWarning("First %i layers are empty!\n", starting_layer_idx);
}
}
@@ -51,10 +51,9 @@ void Weaver::weave(MeshGroup* meshgroup)
{
int starting_z = -1;
for (cura::Slicer* slicer : slicerList)
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*0,*/ wireFrame.bottom_outline, 1);
if (slicerList.empty()) //Wait, there is nothing to slice.
{
@@ -71,23 +70,22 @@ void Weaver::weave(MeshGroup* meshgroup)
else
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
parts1.add(slicer->layers[layer_idx].polygonList);
parts1.add(slicer->layers[layer_idx].polygons);
Polygons chainified;
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*layer_idx - starting_layer_idx,*/ chainified, 1);
if (chainified.size() > 0)
{
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
@@ -108,10 +106,10 @@ void Weaver::weave(MeshGroup* meshgroup)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
@@ -326,7 +324,7 @@ void Weaver::connections2moves(WeaveRoofPart& inset)
WeaveConnectionSegment& segment = segments[idx];
assert(segment.segmentType == WeaveSegmentType::UP);
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
bool skipped = (segment.to - from).vSize2() < line_width * line_width;
if (skipped)
{
unsigned int begin = idx;
@@ -335,9 +333,11 @@ void Weaver::connections2moves(WeaveRoofPart& inset)
WeaveConnectionSegment& segment = segments[idx];
assert(segments[idx].segmentType == WeaveSegmentType::UP);
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
bool skipped = (segment.to - from).vSize2() < line_width * line_width;
if (!skipped)
{
break;
}
}
int end = idx - ((include_half_of_last_down)? 2 : 1);
if (idx >= segments.size())
@@ -387,8 +387,6 @@ void Weaver::connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveCo
void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last)
{
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
{
const PolygonRef upperPart = parts1[prt];
@@ -402,9 +400,9 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
bool found = true;
int idx = 0;
for (Point upper_point = upperPart[closestInPoly.pos]; found; upper_point = next_upper.location)
for (Point upper_point = upperPart[closestInPoly.point_idx]; found; upper_point = next_upper.location)
{
found = PolygonUtils::getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
found = PolygonUtils::getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.point_idx, next_upper);
if (!found)
@@ -429,7 +427,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
if (supporting.size() < 1)
{
DEBUG_PRINTLN("lower layer has zero parts!");
std::cerr << "lower layer has zero parts!\n";
return;
}
@@ -475,16 +473,4 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
}
}//namespace cura
+3 -5
Ver Arquivo
@@ -3,7 +3,7 @@
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings.h"
#include "settings/settings.h"
#include "MeshGroup.h"
#include "slicer.h"
@@ -12,8 +12,6 @@
#include "utils/polygon.h"
#include "utils/polygonUtils.h"
#include "debug.h"
namespace cura
{
@@ -30,7 +28,7 @@ private:
int initial_layer_thickness;
int connectionHeight;
int extrusionWidth;
int line_width;
int roof_inset;
@@ -47,7 +45,7 @@ public:
initial_layer_thickness = getSettingInMicrons("layer_height_0");
connectionHeight = getSettingInMicrons("wireframe_height");
extrusionWidth = getSettingInMicrons("wall_line_width_x");
line_width = getSettingInMicrons("wall_line_width_x");
roof_inset = getSettingInMicrons("wireframe_roof_inset");
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
+78 -59
Ver Arquivo
@@ -3,10 +3,12 @@
#include <cmath> // sqrt
#include <fstream> // debug IO
#include "utils/math.h"
#include "utils/logoutput.h"
#include "weaveDataStorage.h"
#include "Progress.h"
#include "progress/Progress.h"
#include "pathOrderOptimizer.h" // for skirt
#include "pathOrderOptimizer.h" //For skirt/brim.
namespace cura
{
@@ -17,6 +19,8 @@ void Wireframe2gcode::writeGCode()
gcode.preSetup(wireFrame.meshgroup);
gcode.setInitialTemps(*wireFrame.meshgroup);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
@@ -31,23 +35,22 @@ void Wireframe2gcode::writeGCode()
{
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment("SKIRT");
gcode.setZ(initial_layer_thickness);
processSkirt();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment(PrintFeatureType::SkirtBrim);
for (PolygonRef bottom_part : wireFrame.bottom_infill.roof_outlines)
{
if (bottom_part.size() == 0) continue;
writeMoveWithRetract(bottom_part[bottom_part.size()-1]);
for (Point& segment_to : bottom_part)
{
gcode.writeMove(segment_to, speedBottom, extrusion_per_mm_flat);
gcode.writeMove(segment_to, speedBottom, extrusion_mm3_per_mm_flat);
}
}
@@ -63,7 +66,7 @@ void Wireframe2gcode::writeGCode()
writeMoveWithRetract(segment.to);
} else
{
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_connection);
gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_connection);
}
}
,
@@ -73,7 +76,7 @@ void Wireframe2gcode::writeGCode()
else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
return; // do nothing
else
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
@@ -96,7 +99,7 @@ void Wireframe2gcode::writeGCode()
if (part.connection.segments.size() == 0) continue;
gcode.writeTypeComment("SUPPORT"); // connection
gcode.writeTypeComment(PrintFeatureType::Support); // connection
{
if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight)
{
@@ -112,7 +115,7 @@ void Wireframe2gcode::writeGCode()
gcode.writeTypeComment("WALL-OUTER"); // top
gcode.writeTypeComment(PrintFeatureType::OuterWall); // top
{
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
{
@@ -123,7 +126,7 @@ void Wireframe2gcode::writeGCode()
writeMoveWithRetract(segment.to);
} else
{
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat);
gcode.writeDelay(flat_delay);
}
}
@@ -145,7 +148,7 @@ void Wireframe2gcode::writeGCode()
// do nothing
} else
{
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat);
gcode.writeDelay(flat_delay);
}
});
@@ -177,7 +180,7 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
gcode.writeMove(from, speedDown, 0);
if (straight_first_when_going_down <= 0)
{
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection);
} else
{
Point3& to = segment.to;
@@ -189,14 +192,14 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
int64_t new_length = (up - from).vSize() + (to - up).vSize() + 5;
int64_t orr_length = vec.vSize();
double enlargement = new_length / orr_length;
gcode.writeMove(up, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
gcode.writeMove(to, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
gcode.writeMove(up, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement);
gcode.writeMove(to, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement);
}
gcode.writeDelay(bottom_delay);
if (up_dist_half_speed > 0)
{
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_per_mm_connection * 2);
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_mm3_per_mm_connection * 2);
}
}
@@ -205,7 +208,7 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
void Wireframe2gcode::strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
{
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
gcode.writeMove(segment.to, speedUp, extrusion_per_mm_connection);
gcode.writeMove(segment.to, speedUp, extrusion_mm3_per_mm_connection);
Point3 next_vector;
if (segment_idx + 1 < part.connection.segments.size())
{
@@ -239,7 +242,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
retraction_config.primeSpeed = 15; // 30;
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
double top_retract_pause = 2.0;
@@ -255,7 +258,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
Point3 vec = to - from;
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
Point3 lower = to - lowering;
gcode.writeMove(lower, speedUp, extrusion_per_mm_connection);
gcode.writeMove(lower, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeMove(to + lowering, speedUp, 0);
gcode.writeDelay(top_retract_pause);
@@ -264,7 +267,7 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
} else
{
gcode.writeMove(to, speedUp, extrusion_per_mm_connection);
gcode.writeMove(to, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
gcode.writeDelay(top_retract_pause);
@@ -302,7 +305,7 @@ void Wireframe2gcode::strategy_compensate(WeaveLayer& layer, WeaveConnectionPart
int64_t orrLength = (segment.to - from).vSize() + next_vector.vSize() + 1; // + 1 in order to avoid division by zero
int64_t newLength = (newTop - from).vSize() + (next_point - newTop).vSize() + 1; // + 1 in order to avoid division by zero
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_per_mm_connection * orrLength / newLength);
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_mm3_per_mm_connection * orrLength / newLength);
}
void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
{
@@ -317,7 +320,7 @@ void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& par
go_down(layer, part, segment_idx);
break;
case WeaveSegmentType::FLAT:
DEBUG_SHOW("flat piece in connection?!!?!");
logWarning("Warning: flat piece in wire print connection.\n");
break;
case WeaveSegmentType::UP:
if (strategy == STRATEGY_KNOT)
@@ -381,12 +384,12 @@ void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionP
detoured -= next_dir;
}
gcode.writeMove(detoured, speedUp, extrusion_per_mm_connection);
gcode.writeMove(detoured, speedUp, extrusion_mm3_per_mm_connection);
}
break;
case WeaveSegmentType::DOWN:
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection);
gcode.writeDelay(roof_outer_delay);
break;
case WeaveSegmentType::FLAT:
@@ -404,7 +407,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
{
// bottom:
gcode.writeTypeComment("FILL");
gcode.writeTypeComment(PrintFeatureType::Infill);
for (unsigned int inset_idx = 0; inset_idx < infill_insets.size(); inset_idx++)
{
WeaveRoofPart& inset = infill_insets[inset_idx];
@@ -415,7 +418,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
WeaveConnectionPart& inset_part = inset.connections[inset_part_nr];
std::vector<WeaveConnectionSegment>& segments = inset_part.connection.segments;
gcode.writeTypeComment("SUPPORT"); // connection
gcode.writeTypeComment(PrintFeatureType::Support); // connection
if (segments.size() == 0) continue;
Point3 first_extrusion_from = inset_part.connection.from;
unsigned int first_segment_idx;
@@ -431,7 +434,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
connectionHandler(*this, inset, inset_part, segment_idx);
}
gcode.writeTypeComment("WALL-INNER"); // top
gcode.writeTypeComment(PrintFeatureType::InnerWall); // top
for (unsigned int segment_idx = 0; segment_idx < segments.size(); segment_idx++)
{
WeaveConnectionSegment& segment = segments[segment_idx];
@@ -445,7 +448,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
}
gcode.writeTypeComment("WALL-OUTER"); // outer perimeter of the flat parts
gcode.writeTypeComment(PrintFeatureType::OuterWall); // outer perimeter of the flat parts
for (PolygonRef poly : roof_outlines)
{
writeMoveWithRetract(poly[poly.size() - 1]);
@@ -485,16 +488,15 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
roof_inset = getSettingInMicrons("wireframe_roof_inset");
filament_diameter = getSettingInMicrons("material_diameter");
extrusionWidth = getSettingInMicrons("wall_line_width_x");
line_width = getSettingInMicrons("wall_line_width_x");
flowConnection = getSettingInPercentage("wireframe_flow_connection");
flowFlat = getSettingInPercentage("wireframe_flow_flat");
double filament_area = /* M_PI * */ (INT2MM(filament_diameter) / 2.0) * (INT2MM(filament_diameter) / 2.0);
double lineArea = /* M_PI * */ (INT2MM(extrusionWidth) / 2.0) * (INT2MM(extrusionWidth) / 2.0);
extrusion_per_mm_connection = lineArea / filament_area * flowConnection / 100.0;
extrusion_per_mm_flat = lineArea / filament_area * flowFlat / 100.0;
const double line_area = M_PI * square(INT2MM(line_width) / 2.0);
extrusion_mm3_per_mm_connection = line_area * flowConnection / 100.0;
extrusion_mm3_per_mm_flat = line_area * flowFlat / 100.0;
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | .
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
@@ -534,26 +536,29 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
standard_retraction_config.distance = INT2MM(getSettingInMicrons("retraction_amount"));
standard_retraction_config.distance = getSettingInMillimeters("retraction_amount");
standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
standard_retraction_config.retraction_extrusion_window = INT2MM(getSettingInMicrons("retraction_extrusion_window"));
standard_retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode()
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
if (!CommandSocket::isInstantiated())
{
if (!CommandSocket::isInstantiated())
{
gcode.writeCode(gcode.getFileHeader().c_str());
}
std::string prefix = gcode.getFileHeader();
gcode.writeCode(prefix.c_str());
}
else
int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE && gcode.getFlavor() != EGCodeFlavor::GRIFFIN)
{
if (getSettingBoolean("material_bed_temp_prepend"))
{
@@ -562,23 +567,27 @@ void Wireframe2gcode::processStartingCode()
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
}
}
if (getSettingBoolean("material_print_temp_prepend"))
{
if (getSettingInDegreeCelsius("material_print_temperature") > 0)
for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++)
{
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
if (getSettingBoolean("machine_print_temp_wait"))
double print_temp = getSettingInDegreeCelsius("material_print_temperature");
gcode.writeTemperatureCommand(extruder_nr, print_temp);
}
if (getSettingBoolean("material_print_temp_wait"))
{
for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++)
{
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"), true);
double print_temp = getSettingInDegreeCelsius("material_print_temperature");
gcode.writeTemperatureCommand(extruder_nr, print_temp, true);
}
}
}
}
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
if (gcode.getFlavor() == EGCodeFlavor::BFB)
{
gcode.writeComment("enable auto-retraction");
@@ -586,6 +595,16 @@ void Wireframe2gcode::processStartingCode()
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000);
gcode.writeLine(tmp.str().c_str());
}
else if (gcode.getFlavor() == EGCodeFlavor::GRIFFIN)
{ // initialize extruder trains
gcode.writeCode("T0"); // Toolhead already assumed to be at T0, but writing it just to be safe...
CommandSocket::setSendCurrentPosition(gcode.getPositionXY());
gcode.startExtruder(start_extruder_nr);
constexpr bool wait = true;
gcode.writeTemperatureCommand(start_extruder_nr, getSettingInDegreeCelsius("material_print_temperature"), wait);
gcode.writePrimeTrain(getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(&standard_retraction_config);
}
}
@@ -599,7 +618,7 @@ void Wireframe2gcode::processSkirt()
PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN));
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
{
unsigned int poly_idx = order.polyOrder[poly_order_idx];
@@ -608,7 +627,7 @@ void Wireframe2gcode::processSkirt()
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_speed"), getSettingInMillimetersPerSecond("skirt_line_width"));
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_brim_speed"), getSettingInMillimeters("skirt_brim_line_width") * INT2MM(initial_layer_thickness));
}
}
}
@@ -616,7 +635,7 @@ void Wireframe2gcode::processSkirt()
void Wireframe2gcode::finalize()
{
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
gcode.finalize(getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
}
+4 -6
Ver Arquivo
@@ -8,7 +8,7 @@
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings.h"
#include "settings/settings.h"
#include "MeshGroup.h"
#include "slicer.h"
@@ -16,8 +16,6 @@
#include "utils/polygon.h"
#include "Weaver.h"
#include "debug.h"
namespace cura
{
@@ -33,11 +31,11 @@ private:
int initial_layer_thickness;
int filament_diameter;
int extrusionWidth;
int line_width;
double flowConnection;
double flowFlat;
double extrusion_per_mm_connection;
double extrusion_per_mm_flat;
double extrusion_mm3_per_mm_connection;
double extrusion_mm3_per_mm_flat;
int nozzle_outer_diameter;
int nozzle_head_distance;
double nozzle_expansion_angle;
-392
Ver Arquivo
@@ -1,392 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "comb.h"
#include <algorithm>
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "utils/SVG.h"
namespace cura {
// boundary_outside is only computed when it's needed!
Polygons* Comb::getBoundaryOutside()
{
if (!boundary_outside)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
}
return boundary_outside;
}
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, avoid_other_parts(travel_avoid_other_parts)
// , boundary_inside( boundary.offset(-offset_from_outlines) ) // TODO: make inside boundary configurable?
, boundary_inside( comb_boundary_inside )
, boundary_outside(nullptr)
, partsView_inside( boundary_inside.splitIntoPartsView() ) // !! changes the order of boundary_inside !!
{
}
Comb::~Comb()
{
if (boundary_outside)
delete boundary_outside;
}
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
{
return true;
}
//Move start and end point inside the comb boundary
unsigned int start_inside_poly = NO_INDEX;
if (startInside)
{
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
if (!boundary_inside.inside(start_inside_poly) || start_inside_poly == NO_INDEX)
{
if (start_inside_poly != NO_INDEX)
{ // if not yet inside because of overshoot, try again
start_inside_poly = PolygonUtils::moveInside(boundary_inside, startPoint, offset_extra_start_end, max_moveInside_distance2);
}
if (start_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
{
startInside = false;
}
}
}
unsigned int end_inside_poly = NO_INDEX;
if (endInside)
{
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
if (!boundary_inside.inside(endPoint) || end_inside_poly == NO_INDEX)
{
if (end_inside_poly != NO_INDEX)
{ // if not yet inside because of overshoot, try again
end_inside_poly = PolygonUtils::moveInside(boundary_inside, endPoint, offset_extra_start_end, max_moveInside_distance2);
}
if (end_inside_poly == NO_INDEX) //If we fail to move the point inside the comb boundary we need to retract.
{
endInside = false;
}
}
}
unsigned int start_part_boundary_poly_idx;
unsigned int end_part_boundary_poly_idx;
unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx);
unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx);
if (startInside && endInside && start_part_idx == end_part_idx)
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
return true;
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
Point middle_from;
Point middle_to;
Point inside_middle_from;
Point inside_middle_to;
if (startInside && endInside)
{
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(endPoint, boundary_inside[start_part_boundary_poly_idx]);
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
middle_from = middle_from_cp.location;
inside_middle_from = middle_from_cp.location;
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else if(!startInside && !endInside)
{
middle_from = startPoint;
inside_middle_from = startPoint;
middle_to = endPoint;
inside_middle_to = endPoint;
}
else if(!startInside && endInside)
{
middle_from = startPoint;
inside_middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else if(startInside && !endInside)
{
middle_to = endPoint;
inside_middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
inside_middle_from = middle_from_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
if (startInside)
{
// start to boundary
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_begin, startPoint, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
// throught air from boundary to boundary
if (avoid_other_parts)
{
Polygons& middle = *getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
Point from_outside = middle_from;
if (startInside || middle.inside(from_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, from_outside, -offset_extra_start_end, max_moveInside_distance2);
}
Point to_outside = middle_to;
if (endInside || middle.inside(to_outside, true))
{ // move outside
PolygonUtils::moveInside(middle, to_outside, -offset_extra_start_end, max_moveInside_distance2);
}
combPaths.emplace_back();
combPaths.back().throughAir = true;
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
{ // via outside is a detour
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
else
{
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
}
else
{ // directly through air (not avoiding other parts)
combPaths.emplace_back();
combPaths.back().throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
if (endInside)
{
// boundary to end
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_end, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
return true;
}
}
void LinePolygonsCrossings::calcScanlineCrossings()
{
min_crossing_idx = NO_INDEX;
max_crossing_idx = NO_INDEX;
for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
{
PolyCrossings minMax(poly_idx);
PolygonRef poly = boundary[poly_idx];
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = transformation_matrix.apply(poly[point_idx]);
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
{
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
{
p0 = p1;
continue;
}
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
{
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
{
minMax.min.x = x;
minMax.min.point_idx = point_idx;
}
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
{
minMax.max.x = x;
minMax.max.point_idx = point_idx;
}
}
}
p0 = p1;
}
if (minMax.min.point_idx != NO_INDEX)
{ // then also max.point_idx != -1
if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
crossings.push_back(minMax);
}
}
}
bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
{
Point diff = endPoint - startPoint;
transformation_matrix = PointMatrix(diff);
transformed_startPoint = transformation_matrix.apply(startPoint);
transformed_endPoint = transformation_matrix.apply(endPoint);
for(PolygonRef poly : boundary)
{
Point p0 = transformation_matrix.apply(poly.back());
for(Point p1_ : poly)
{
Point p1 = transformation_matrix.apply(p1_);
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x > transformed_startPoint.X && x < transformed_endPoint.X)
return true;
}
p0 = p1;
}
}
return false;
}
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
{
//We're not crossing any boundaries. So skip the comb generation.
combPath.push_back(startPoint);
combPath.push_back(endPoint);
return;
}
calcScanlineCrossings();
CombPath basicPath;
getBasicCombingPath(basicPath);
optimizePath(basicPath, combPath);
}
void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
{
for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
; crossing != nullptr
; crossing = getNextPolygonAlongScanline(crossing->max.x))
{
getBasicCombingPath(*crossing, combPath);
}
combPath.push_back(endPoint);
}
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
< poly.size() / 2 )
{ // follow the path in the same direction as the winding order of the boundary polygon
for(unsigned int point_idx = polyCrossings.min.point_idx
; point_idx != polyCrossings.max.point_idx
; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
else
{ // follow the path in the opposite direction of the winding order of the boundary polygon
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
}
LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
{
PolyCrossings* ret = nullptr;
for(PolyCrossings& crossing : crossings)
{
if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
{
ret = &crossing;
}
}
return ret;
}
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
optimized_comb_path.push_back(startPoint);
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
{
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
{
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
{
comb_path.cross_boundary = true;
}
optimized_comb_path.push_back(comb_path[point_idx - 1]);
}
else
{
// : dont add the newest point
// 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]))
{
break;
}
else
{
optimized_comb_path.pop_back();
}
}
}
}
optimized_comb_path.push_back(comb_path.back());
return true;
}
}//namespace cura
+504 -90
Ver Arquivo
@@ -1,14 +1,16 @@
#include "utils/logoutput.h"
#include "commandSocket.h"
#include "FffProcessor.h"
#include "Progress.h"
#include "progress/Progress.h"
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#include <Arcus/SocketListener.h>
#include <Arcus/Error.h>
#endif
#include <string> // stoi
@@ -17,6 +19,7 @@
#endif
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
// std::cerr << x;
namespace cura {
@@ -27,6 +30,7 @@ namespace cura {
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
#ifdef ARCUS
class Listener : public Arcus::SocketListener
{
public:
@@ -40,7 +44,7 @@ public:
void error(const Arcus::Error & error) override
{
if(error.getErrorCode() == Arcus::ErrorCode::Debug)
if (error.getErrorCode() == Arcus::ErrorCode::Debug)
{
log("%s\n", error.toString().c_str());
}
@@ -51,44 +55,197 @@ public:
}
};
/*!
* A template structure used to store data to be sent to the front end.
*/
template <typename T>
class SliceDataStruct
{
SliceDataStruct(const SliceDataStruct&) = delete;
SliceDataStruct& operator=(const SliceDataStruct&) = delete;
public:
SliceDataStruct()
: sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
//! The number of sliced objects for this sliced object list
int sliced_objects;
int current_layer_count;//!< Number of layers for which data has been buffered in slice_data so far.
int current_layer_offset;//!< Offset to add to layer number for the current slice object when slicing one at a time.
std::unordered_map<int, std::shared_ptr<T>> slice_data;
};
class CommandSocket::Private
{
public:
Private()
: socket(nullptr)
, object_count(0)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
std::shared_ptr<cura::proto::LayerOptimized> getOptimizedLayerById(int id);
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
// Number of sliced objects for this sliced object list
int sliced_objects;
// Number of layers sent to the front end so far
// Used for incrementing the current layer in one at a time mode
int current_layer_count;
int current_layer_offset;
std::string temp_gcode_file;
std::ostringstream gcode_output_stream;
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
std::unordered_map<int, std::shared_ptr<cura::proto::Layer>> sliced_layers;
SliceDataStruct<cura::proto::Layer> sliced_layers;
SliceDataStruct<cura::proto::LayerOptimized> optimized_layers;
};
CommandSocket::CommandSocket()
: private_data(new Private)
/*!
* PathCompiler buffers and prepares the sliced data to be sent to the front end and saves them in
* appropriate buffers
*/
class CommandSocket::PathCompiler
{
typedef cura::proto::PathSegment::PointType PointType;
static_assert(sizeof(PrintFeatureType) == 1, "To be compatible with the Cura frontend code PrintFeatureType needs to be of size 1");
//! Reference to the private data of the CommandSocket used to send the data to the front end.
CommandSocket::Private& _cs_private_data;
//! Keeps track of the current layer number being processed. If layer number is set to a different value, the current data is flushed to CommandSocket.
int _layer_nr;
int extruder;
PointType data_point_type;
std::vector<PrintFeatureType> line_types; //!< Line types for the line segments stored, the size of this vector is N.
std::vector<float> line_widths; //!< Line widths for the line segments stored, the size of this vector is N.
std::vector<float> points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is the dimensionality of the point.
Point last_point;
PathCompiler(const PathCompiler&) = delete;
PathCompiler& operator=(const PathCompiler&) = delete;
public:
PathCompiler(CommandSocket::Private& cs_private_data):
_cs_private_data(cs_private_data),
_layer_nr(0),
extruder(0),
data_point_type(cura::proto::PathSegment::Point2D),
line_types(),
line_widths(),
points(),
last_point{0,0}
{}
~PathCompiler()
{
if (line_types.size())
{
flushPathSegments();
}
}
/*!
* Used to select which layer the following layer data is intended for.
*/
void setLayer(int new_layer_nr)
{
if (_layer_nr != new_layer_nr)
{
flushPathSegments();
_layer_nr = new_layer_nr;
}
}
/*!
* Returns the current layer which data is written to.
*/
int getLayer() const
{
return _layer_nr;
}
/*!
* Used to set which extruder will be used for printing the following layer data is intended for.
*/
void setExtruder(int new_extruder)
{
if (extruder != new_extruder)
{
flushPathSegments();
extruder = new_extruder;
}
}
/*!
* Special handling of the first point in an added line sequence.
* If the new sequence of lines does not start at the current end point
* of the path this jump is marked as PrintFeatureType::NoneType
*/
void handleInitialPoint(Point from)
{
if (points.size() == 0)
{
addPoint2D(from);
}
else if (from != last_point)
{
addLineSegment(PrintFeatureType::NoneType, from, 1.0);
}
}
/*!
* Transfers the currently buffered line segments to the
* CommandSocket layer message storage.
*/
void flushPathSegments();
/*!
* Move the current point of this path to \position.
*/
void setCurrentPosition(Point position)
{
handleInitialPoint(position);
}
/*!
* Adds a single line segment to the current path. The line segment added is from the current last point to point \p to
*/
void sendLineTo(PrintFeatureType print_feature_type, Point to, int width);
/*!
* Adds closed polygon to the current path
*/
void sendPolygon(PrintFeatureType print_feature_type, Polygon 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.
*/
void addPoint2D(Point point)
{
points.push_back(INT2MM(point.X));
points.push_back(INT2MM(point.Y));
last_point = point;
}
/*!
* Implements the functionality of adding a single 2D line segment to the path data. All member functions adding a 2D line segment should use this functions.
*/
void addLineSegment(PrintFeatureType print_feature_type, Point point, int line_width)
{
addPoint2D(point);
line_types.push_back(print_feature_type);
line_widths.push_back(INT2MM(line_width));
}
};
#endif
CommandSocket::CommandSocket()
#ifdef ARCUS
: private_data(new Private)
, path_comp(new PathCompiler(*private_data))
#endif
{
#ifdef ARCUS
#endif
}
CommandSocket* CommandSocket::getInstance()
@@ -109,18 +266,21 @@ bool CommandSocket::isInstantiated()
void CommandSocket::connect(const std::string& ip, int port)
{
#ifdef ARCUS
private_data->socket = new Arcus::Socket();
private_data->socket->addListener(new Listener());
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::LayerOptimized::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
private_data->socket->registerMessageType(&cura::proto::PrintTimeMaterialEstimates::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingExtruder::default_instance());
private_data->socket->connect(ip, port);
@@ -140,44 +300,83 @@ void CommandSocket::connect(const std::string& ip, int port)
{
// Actually start handling messages.
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
/*
* handle a message which consists purely of a SettingList
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if(setting_list)
if (setting_list)
{
handleSettingList(setting_list);
}
*/
/*cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
if(object_list)
/*
* handle a message which consists purely of an ObjectList
cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
if (object_list)
{
handleObjectList(object_list);
}*/
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get());
if(slice)
}
*/
// Handle the main Slice message
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get()); // See if the message is of the message type Slice; returns nullptr otherwise
if (slice)
{
logDebug("Received a Slice message\n");
const cura::proto::SettingList& global_settings = slice->global_settings();
for (auto setting : global_settings.settings())
{
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
// Reset object counts
private_data->object_count = 0;
for(auto object : slice->object_lists())
for (auto object : slice->object_lists())
{
handleObjectList(&object);
handleObjectList(&object, slice->extruders());
}
//For every object, set the extruder fallbacks from the limit_to_extruder.
for (const cura::proto::SettingExtruder setting_extruder : slice->limit_to_extruder())
{
const int32_t extruder_nr = setting_extruder.extruder(); //Implicit cast from Protobuf's int32 to normal int32.
for (std::shared_ptr<MeshGroup> meshgroup : private_data->objects_to_slice)
{
if (extruder_nr < 0 || extruder_nr >= meshgroup->getExtruderCount()) //We obtained an invalid value from the front-end. Ignore.
{
continue;
}
const ExtruderTrain* settings_base = meshgroup->getExtruderTrain(extruder_nr); //The extruder train that the setting should fall back to.
for (Mesh& mesh : meshgroup->meshes)
{
mesh.setSettingInheritBase(setting_extruder.name(), *settings_base);
}
}
}
logDebug("Done reading Slice message\n");
}
//If there is an object to slice, do so.
if(private_data->objects_to_slice.size())
if (private_data->objects_to_slice.size())
{
FffProcessor::getInstance()->resetFileNumber();
for(auto object : private_data->objects_to_slice)
int object_count = private_data->objects_to_slice.size();
logDebug("Slicing %i objects\n", object_count);
FffProcessor::getInstance()->resetMeshGroupNumber();
int i = 1;
for (auto object : private_data->objects_to_slice)
{
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
logDebug("Slicing object %i of %i\n", i, object_count);
if (!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
i++;
}
logDebug("Done slicing objects\n");
private_data->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
flushGcode();
sendPrintTime();
sendPrintTimeMaterialEstimates();
sendFinishedSlicing();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
@@ -185,19 +384,20 @@ void CommandSocket::connect(const std::string& ip, int port)
//private_data->object_to_slice.reset();
//private_data->processor->resetFileNumber();
//sendPrintTime();
//sendPrintTimeMaterialEstimates();
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
log("Closing connection\n");
private_data->socket->close();
#endif
}
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list, const google::protobuf::RepeatedPtrField<cura::proto::Extruder> settings_per_extruder_train)
{
if(list->objects_size() <= 0)
if (list->objects_size() <= 0)
{
return;
}
@@ -207,30 +407,45 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
//private_data->object_ids.clear();
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
for(auto setting : list->settings())
// load meshgroup settings
for (auto setting : list->settings())
{
meshgroup->setSetting(setting.name(), setting.value());
}
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
{ // load extruder settings
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
}
for (auto extruder : settings_per_extruder_train)
{
int extruder_nr = extruder.id();
ExtruderTrain* train = meshgroup->getExtruderTrain(extruder_nr);
for (auto setting : extruder.settings().settings())
{
train->setSetting(setting.name(), setting.value());
}
}
}
for(auto object : list->objects())
for (auto object : list->objects())
{
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
if(face_count <= 0)
if (face_count <= 0)
{
logWarning("Got an empty mesh, ignoring it!");
continue;
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
for(auto setting : object.settings())
// Check to which extruder train this object belongs
int extruder_train_nr = 0; // assume extruder 0 if setting wasn't supplied
for (auto setting : object.settings())
{
if (setting.name() == "extruder_nr")
{
@@ -243,7 +458,7 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = meshgroup->meshes.back();
for(int i = 0; i < face_count; ++i)
for (int i = 0; i < face_count; ++i)
{
//TODO: Apply matrix
std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face);
@@ -264,7 +479,8 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n");
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n");
for(auto setting : object.settings())
for (auto setting : object.settings())
{
mesh.setSetting(setting.name(), setting.value());
}
@@ -275,47 +491,104 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
private_data->object_count++;
meshgroup->finalize();
}
#endif
void CommandSocket::handleSettingList(cura::proto::SettingList* list)
void CommandSocket::sendOptimizedLayerInfo(int layer_nr, int32_t z, int32_t height)
{
for(auto setting : list->settings())
{
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
#ifdef ARCUS
std::shared_ptr<cura::proto::LayerOptimized> layer = private_data->getOptimizedLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
#endif
}
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
void CommandSocket::sendPolygons(PrintFeatureType type, const Polygons& polygons, int line_width)
{
#ifdef ARCUS
if (polygons.size() == 0)
return;
std::shared_ptr<cura::proto::Layer> proto_layer = private_data->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
cura::proto::Polygon* p = proto_layer->add_polygons();
p->set_type(static_cast<cura::proto::Polygon_Type>(type));
std::string polydata;
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
p->set_points(polydata);
p->set_line_width(line_width);
return;
}
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
for (unsigned int i = 0; i < polygons.size(); ++i)
{
path_comp->sendPolygon(type, polygons[i], line_width);
}
}
#endif
}
void CommandSocket::sendPolygon(PrintFeatureType type, Polygon& polygon, int line_width)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->sendPolygon(type, polygon, line_width);
}
#endif
}
void CommandSocket::sendLineTo(cura::PrintFeatureType type, Point to, int line_width)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->sendLineTo(type, to, line_width);
}
#endif
}
void CommandSocket::setSendCurrentPosition(Point position)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setCurrentPosition(position);
}
#endif
}
void CommandSocket::setLayerForSend(int layer_nr)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setLayer(layer_nr);
}
#endif
}
void CommandSocket::setExtruderForSend(int extruder)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setExtruder(extruder);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::Progress>();
amount /= private_data->object_count;
amount += private_data->sliced_objects * (1. / private_data->object_count);
amount += private_data->optimized_layers.sliced_objects * (1. / private_data->object_count);
message->set_amount(amount);
private_data->socket->sendMessage(message);
#endif
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
@@ -323,12 +596,25 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
// TODO
}
void CommandSocket::sendPrintTime()
void CommandSocket::sendPrintTimeMaterialEstimates()
{
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
#ifdef ARCUS
logDebug("Sending print time and material estimates.\n");
auto message = std::make_shared<cura::proto::PrintTimeMaterialEstimates>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
int num_extruders = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
for (int extruder_nr (0); extruder_nr < num_extruders; ++extruder_nr)
{
cura::proto::MaterialEstimates* material_message = message->add_materialestimates();
material_message->set_id(extruder_nr);
material_message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(extruder_nr));
}
private_data->socket->sendMessage(message);
logDebug("Done sending print time and material estimates.\n");
#endif
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -342,60 +628,101 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
void CommandSocket::sendLayerData()
{
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
log("End sliced object called. Sending ", private_data->current_layer_count, " layers.");
#ifdef ARCUS
#endif
#ifdef ARCUS
auto& data = private_data->sliced_layers;
if(private_data->sliced_objects >= private_data->object_count)
data.sliced_objects++;
data.current_layer_offset = data.current_layer_count;
// log("End sliced object called. Sending %d layers.", data.current_layer_count);
// Only send the data to the front end when all mesh groups have been processed.
if (data.sliced_objects >= private_data->object_count)
{
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : private_data->sliced_layers) //Note: This is in no particular order!
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : data.slice_data) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
private_data->sliced_objects = 0;
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_layers.clear();
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
data.sliced_objects = 0;
data.current_layer_count = 0;
data.current_layer_offset = 0;
data.slice_data.clear();
}
#endif
}
void CommandSocket::sendOptimizedLayerData()
{
#ifdef ARCUS
path_comp->flushPathSegments(); // make sure the last path segment has been flushed from the compiler
auto& data = private_data->optimized_layers;
data.sliced_objects++;
data.current_layer_offset = data.current_layer_count;
log("End sliced object called. Sending %d layers.", data.current_layer_count);
if (data.sliced_objects >= private_data->object_count)
{
for (std::pair<const int, std::shared_ptr<cura::proto::LayerOptimized>> entry : data.slice_data) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
data.sliced_objects = 0;
data.current_layer_count = 0;
data.current_layer_offset = 0;
data.slice_data.clear();
}
#endif
}
void CommandSocket::sendFinishedSlicing()
{
#ifdef ARCUS
logDebug("Sending Slicing Finished message.\n");
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
logDebug("Done sending Slicing Finished message.\n");
#endif
}
void CommandSocket::beginGCode()
{
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
#endif
}
void CommandSocket::flushGcode()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodeLayer>();
message->set_data(private_data->gcode_output_stream.str());
private_data->socket->sendMessage(message);
private_data->gcode_output_stream.str("");
#endif
}
void CommandSocket::sendGCodePrefix(std::string prefix)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
message->set_data(prefix);
private_data->socket->sendMessage(message);
#endif
}
#ifdef ARCUS
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
id += sliced_layers.current_layer_offset;
auto itr = sliced_layers.find(id);
auto itr = sliced_layers.slice_data.find(id);
std::shared_ptr<cura::proto::Layer> layer;
if(itr != sliced_layers.end())
if (itr != sliced_layers.slice_data.end())
{
layer = itr->second;
}
@@ -403,11 +730,98 @@ std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
{
layer = std::make_shared<cura::proto::Layer>();
layer->set_id(id);
current_layer_count++;
sliced_layers[id] = layer;
sliced_layers.current_layer_count++;
sliced_layers.slice_data[id] = layer;
}
return layer;
}
#endif
#ifdef ARCUS
std::shared_ptr<cura::proto::LayerOptimized> CommandSocket::Private::getOptimizedLayerById(int id)
{
id += optimized_layers.current_layer_offset;
auto itr = optimized_layers.slice_data.find(id);
std::shared_ptr<cura::proto::LayerOptimized> layer;
if (itr != optimized_layers.slice_data.end())
{
layer = itr->second;
}
else
{
layer = std::make_shared<cura::proto::LayerOptimized>();
layer->set_id(id);
optimized_layers.current_layer_count++;
optimized_layers.slice_data[id] = layer;
}
return layer;
}
#endif
#ifdef ARCUS
void CommandSocket::PathCompiler::flushPathSegments()
{
if (line_types.size() > 0 && CommandSocket::isInstantiated())
{
std::shared_ptr<cura::proto::LayerOptimized> proto_layer = _cs_private_data.getOptimizedLayerById(_layer_nr);
cura::proto::PathSegment* p = proto_layer->add_path_segment();
p->set_extruder(extruder);
p->set_point_type(data_point_type);
std::string line_type_data;
line_type_data.append(reinterpret_cast<const char*>(line_types.data()), line_types.size()*sizeof(PrintFeatureType));
p->set_line_type(line_type_data);
std::string polydata;
polydata.append(reinterpret_cast<const char*>(points.data()), points.size() * sizeof(float));
p->set_points(polydata);
std::string line_width_data;
line_width_data.append(reinterpret_cast<const char*>(line_widths.data()), line_widths.size()*sizeof(float));
p->set_line_width(line_width_data);
}
points.clear();
line_widths.clear();
line_types.clear();
}
void CommandSocket::PathCompiler::sendLineTo(PrintFeatureType print_feature_type, Point to, int width)
{
assert(points.size() > 0 && "A point must already be in the buffer for sendLineTo(.) to function properly");
if (to != last_point)
{
addLineSegment(print_feature_type, to, width);
}
}
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, Polygon polygon, int width)
{
if (polygon.size() < 2)
{
return;
}
auto it = polygon.begin();
handleInitialPoint(*it);
const auto it_end = polygon.end();
while (++it != it_end)
{
// Ignore zero-length segments.
if (*it != last_point)
{
addLineSegment(print_feature_type, *it, width);
}
}
// Make sure the polygon is closed
if (*polygon.begin() != polygon.back())
{
addLineSegment(print_feature_type, *polygon.begin(), width);
}
}
#endif
}//namespace cura
+67 -26
Ver Arquivo
@@ -3,16 +3,18 @@
#include "utils/socket.h"
#include "utils/polygon.h"
#include "settings.h"
#include "Progress.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura {
namespace cura
{
class CommandSocket
{
@@ -35,33 +37,59 @@ public:
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
*
* Also handles meshgroup settings and extruder settings.
*
* \param[in] list The list of objects to slice
* \param[in] settings_per_extruder_train The extruder train settings to load into the meshgroup
*/
void handleObjectList(cura::proto::ObjectList* list);
/*!
* Handler for SettingList message.
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
void handleObjectList(cura::proto::ObjectList* list, const google::protobuf::RepeatedPtrField<cura::proto::Extruder> settings_per_extruder_train);
#endif
/*!
* Send info on a layer to be displayed by the forntend: set the z and the thickness of the layer.
* Send info on an optimized layer to be displayed by the forntend: set the z and the thickness of the layer.
*/
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the engine. This is used for the layerview in the GUI
*/
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
void sendOptimizedLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
* Send a polygon to the front-end. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
static void sendPolygons(cura::PrintFeatureType type, const cura::Polygons& polygons, int line_width);
/*!
* 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);
/*!
* Send a line to the front-end. This is used for the layerview in the GUI
*/
static void sendLineTo(cura::PrintFeatureType type, Point to, int line_width);
/*!
* Set the current position of the path compiler to \p position. This is used for the layerview in the GUI
*/
static void setSendCurrentPosition(Point position);
/*!
* Set which layer is being used for the following calls to SendPolygons, SendPolygon and SendLineTo.
*/
static void setLayerForSend(int layer_nr);
/*!
* Set which extruder is being used for the following calls to SendPolygons, SendPolygon and SendLineTo.
*/
static void setExtruderForSend(int extruder);
/*!
* Send a polygon to the front-end if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, const cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
@@ -76,7 +104,7 @@ public:
/*!
* Send time estimate of how long print would take.
*/
void sendPrintTime();
void sendPrintTimeMaterialEstimates();
/*!
* Does nothing at the moment
@@ -84,13 +112,22 @@ public:
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
/*!
* Send the sliced layer data to the GUI.
* Send the slices of the model as polygons to the GUI.
*
* The GUI may use this to visualise the g-code, so that the user can
* inspect the result of slicing.
* The GUI may use this to visualize the early result of the slicing
* process.
*/
void sendLayerData();
/*!
* Send the sliced layer data to the GUI after the optimization is done and
* the actual order in which to print has been set.
*
* The GUI may use this to visualize the g-code, so that the user can
* inspect the result of slicing.
*/
void sendOptimizedLayerData();
/*!
* \brief Sends a message to indicate that all the slicing is done.
*
@@ -107,9 +144,13 @@ public:
void flushGcode();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> private_data;
class PathCompiler;
const std::unique_ptr<PathCompiler> path_comp;
#endif
};
}//namespace cura
-61
Ver Arquivo
@@ -1,61 +0,0 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <string.h>
#define __FILE_NAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define DEBUG_HERE std::cerr << __FILE_NAME__ << " : " << __LINE__ << std::endl
#define DEBUG 1
#define DEBUG_SHOW_LINE 1
#if DEBUG_SHOW_LINE == 1
#define DEBUG_FILE_LINE __FILE_NAME__ << "." << __LINE__ << ": "
#else
#define DEBUG_FILE_LINE ""
#endif
#if DEBUG == 1
# define DEBUG_DO(x) do { x } while (0)
# define DEBUG_SHOW(x) do { std::cerr << DEBUG_FILE_LINE << #x << " = " << x << std::endl; } while (0)
# define DEBUG_PRINTLN(x) do { std::cerr << DEBUG_FILE_LINE << x << std::endl; } while (0)
#else
# define DEBUG_DO(x)
# define DEBUG_SHOW(x)
# define DEBUG_PRINTLN(x)
#endif
#include <sstream>
#if 0==1
#define ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT};
#endif
#define ENUM(name, ...) enum class name { __VA_ARGS__}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
else if(str[i] == ',') { \
strings.push_back(temp.str()); \
temp.str(std::string());\
} \
else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;}
#endif // DEBUG_H
+354 -142
Ver Arquivo
@@ -6,6 +6,8 @@
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "PrintFeature.h"
#include "utils/Date.h"
#include "utils/string.h" // MMtoStream, PrecisionedDouble
namespace cura {
@@ -14,35 +16,174 @@ GCodeExport::GCodeExport()
, currentPosition(0,0,MM2INT(20))
, layer_nr(0)
{
*output_stream << std::fixed;
current_e_value = 0;
current_extruder = 0;
currentFanSpeed = -1;
totalPrintTime = 0.0;
currentSpeed = 1;
current_acceleration = -1;
current_jerk = -1;
current_max_z_feedrate = -1;
isZHopped = 0;
setFlavor(EGCodeFlavor::REPRAP);
initial_bed_temp = 0;
extruder_count = 0;
}
GCodeExport::~GCodeExport()
{
}
std::string GCodeExport::getFileHeader(double print_time, int filament_used_0, int filament_used_1)
void GCodeExport::preSetup(const MeshGroup* meshgroup)
{
setFlavor(meshgroup->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = meshgroup->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
for (const Mesh& mesh : meshgroup->meshes)
{
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))
{
extruder_attr[extruder_nr].is_used = true;
}
for (const Mesh& mesh : meshgroup->meshes)
{
if ((mesh.getSettingBoolean("support_enable") && mesh.getSettingBoolean("support_interface_enable") && meshgroup->getSettingAsIndex("support_interface_extruder_nr") == int(extruder_nr))
|| (mesh.getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_infill_extruder_nr") == int(extruder_nr))
|| (mesh.getSettingBoolean("support_enable") && meshgroup->getSettingAsIndex("support_extruder_nr_layer_0") == int(extruder_nr))
)
{
extruder_attr[extruder_nr].is_used = true;
}
}
setFilamentDiameter(extruder_nr, train->getSettingInMicrons("material_diameter"));
extruder_attr[extruder_nr].prime_pos = Point3(train->getSettingInMicrons("extruder_prime_pos_x"), train->getSettingInMicrons("extruder_prime_pos_y"), train->getSettingInMicrons("extruder_prime_pos_z"));
extruder_attr[extruder_nr].prime_pos_is_abs = train->getSettingBoolean("extruder_prime_pos_abs");
extruder_attr[extruder_nr].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[extruder_nr].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[extruder_nr].material_guid = train->getSettingString("material_guid");
extruder_attr[extruder_nr].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[extruder_nr].end_code = train->getSettingString("machine_extruder_end_code");
extruder_attr[extruder_nr].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
}
machine_dimensions.x = meshgroup->getSettingInMicrons("machine_width");
machine_dimensions.y = meshgroup->getSettingInMicrons("machine_depth");
machine_dimensions.z = meshgroup->getSettingInMicrons("machine_height");
machine_name = meshgroup->getSettingString("machine_name");
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
}
else
{
new_line = "\n";
}
estimateCalculator.setFirmwareDefaults(meshgroup);
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
{
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
const ExtruderTrain* extr_train = settings.getExtruderTrain(extr_nr);
assert(extr_train);
double temp = extr_train->getSettingInDegreeCelsius((extr_nr == 0)? "material_print_temperature" : "material_standby_temperature");
setInitialTemp(extr_nr, temp);
}
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature");
}
void GCodeExport::setInitialTemp(int extruder_nr, double temp)
{
extruder_attr[extruder_nr].initial_temp = temp;
if (flavor == EGCodeFlavor::GRIFFIN || flavor == EGCodeFlavor::ULTIGCODE)
{
extruder_attr[extruder_nr].currentTemperature = temp;
}
}
std::string GCodeExport::getFileHeader(const double* print_time, const std::vector<double>& filament_used, const std::vector<std::string>& mat_ids)
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";TIME:" << int(print_time) << new_line;
if (flavor == EGCodeFlavor::ULTIGCODE)
switch (flavor)
{
prefix << ";MATERIAL:" << int(filament_used_0) << new_line;
prefix << ";MATERIAL2:" << int(filament_used_1) << new_line;
case EGCodeFlavor::GRIFFIN:
prefix << ";START_OF_HEADER" << new_line;
prefix << ";HEADER_VERSION:0.1" << new_line;
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";GENERATOR.NAME:Cura_SteamEngine" << new_line;
prefix << ";GENERATOR.VERSION:" << VERSION << new_line;
prefix << ";GENERATOR.BUILD_DATE:" << Date::getDate().toStringDashed() << new_line;
prefix << ";TARGET_MACHINE.NAME:" << machine_name << new_line;
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(1))) << new_line; // TODO: the second nozzle size isn't always initiated!
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
if (!extruder_attr[extr_nr].is_used)
{
continue;
}
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".INITIAL_TEMPERATURE:" << extruder_attr[extr_nr].initial_temp << new_line;
if (filament_used.size() == extruder_count)
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.VOLUME_USED:" << static_cast<int>(filament_used[extr_nr]) << new_line;
}
if (mat_ids.size() == extruder_count && mat_ids[extr_nr] != "")
{
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".MATERIAL.GUID:" << mat_ids[extr_nr] << new_line;
}
prefix << ";EXTRUDER_TRAIN." << extr_nr << ".NOZZLE.DIAMETER:" << float(INT2MM(getNozzleSize(extr_nr))) << new_line;
}
prefix << ";BUILD_PLATE.INITIAL_TEMPERATURE:" << initial_bed_temp << new_line;
if (print_time)
{
prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line;
}
prefix << ";PRINT.SIZE.MIN.X:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Y:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Z:0" << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(machine_dimensions.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(machine_dimensions.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(machine_dimensions.z) << new_line;
prefix << ";END_OF_HEADER" << new_line;
return prefix.str();
default:
prefix << ";FLAVOR:" << toString(flavor) << new_line;
prefix << ";TIME:" << ((print_time)? static_cast<int>(*print_time) : 6666) << new_line;
if (flavor == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << ((filament_used.size() >= 1)? static_cast<int>(filament_used[0]) : 6666) << new_line;
prefix << ";MATERIAL2:" << ((filament_used.size() >= 2)? static_cast<int>(filament_used[1]) : 0) << new_line;
prefix << ";NOZZLE_DIAMETER:" << float(INT2MM(getNozzleSize(0))) << new_line;
// TODO: the second nozzle size isn't always initiated! ";NOZZLE_DIAMETER2:"
}
return prefix.str();
}
return prefix.str();
}
@@ -56,17 +197,27 @@ void GCodeExport::setOutputStream(std::ostream* stream)
*output_stream << std::fixed;
}
int GCodeExport::getNozzleSize(int extruder_idx)
bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const
{
return extruder_attr[extruder_idx].nozzle_size;
return extruder_attr[extruder_nr].is_used;
}
Point GCodeExport::getExtruderOffset(int id)
int GCodeExport::getNozzleSize(const int extruder_nr) const
{
return extruder_attr[extruder_nr].nozzle_size;
}
Point GCodeExport::getExtruderOffset(const int id) const
{
return extruder_attr[id].nozzle_offset;
}
Point GCodeExport::getGcodePos(int64_t x, int64_t y, int extruder_train)
std::string GCodeExport::getMaterialGUID(const int extruder_nr) const
{
return extruder_attr[extruder_nr].material_guid;
}
Point GCodeExport::getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const
{
if (use_extruder_offset_to_offset_coords) { return Point(x,y) - getExtruderOffset(extruder_train); }
else { return Point(x,y); }
@@ -108,7 +259,7 @@ EGCodeFlavor GCodeExport::getFlavor()
void GCodeExport::setZ(int z)
{
this->zPos = z;
this->current_layer_z = z;
}
Point3 GCodeExport::getPosition()
@@ -192,11 +343,11 @@ double GCodeExport::mmToE(double mm)
}
double GCodeExport::getTotalFilamentUsed(int e)
double GCodeExport::getTotalFilamentUsed(int extruder_nr)
{
if (e == current_extruder)
return extruder_attr[e].totalFilament + getCurrentExtrudedVolume();
return extruder_attr[e].totalFilament;
if (extruder_nr == current_extruder)
return extruder_attr[extruder_nr].totalFilament + getCurrentExtrudedVolume();
return extruder_attr[extruder_nr].totalFilament;
}
double GCodeExport::getTotalPrintTime()
@@ -220,6 +371,7 @@ void GCodeExport::updateTotalPrintTime()
{
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
writeTimeComment(totalPrintTime);
}
void GCodeExport::writeComment(std::string comment)
@@ -237,9 +389,9 @@ void GCodeExport::writeComment(std::string comment)
*output_stream << new_line;
}
void GCodeExport::writeTypeComment(const char* type)
void GCodeExport::writeTimeComment(const double time)
{
*output_stream << ";TYPE:" << type << new_line;
*output_stream << ";TIME_ELAPSED:" << time << new_line;
}
void GCodeExport::writeTypeComment(PrintFeatureType type)
@@ -258,7 +410,7 @@ void GCodeExport::writeTypeComment(PrintFeatureType type)
case PrintFeatureType::Support:
*output_stream << ";TYPE:SUPPORT" << new_line;
break;
case PrintFeatureType::Skirt:
case PrintFeatureType::SkirtBrim:
*output_stream << ";TYPE:SKIRT" << new_line;
break;
case PrintFeatureType::Infill:
@@ -293,7 +445,7 @@ void GCodeExport::writeLine(const char* line)
void GCodeExport::resetExtrusionValue()
{
if (current_e_value != 0.0 && flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
if (flavor != EGCodeFlavor::MAKERBOT && flavor != EGCodeFlavor::BFB)
{
*output_stream << "G92 " << extruder_attr[current_extruder].extruderCharacter << "0" << new_line;
double current_extruded_volume = getCurrentExtrudedVolume();
@@ -315,7 +467,7 @@ void GCodeExport::writeDelay(double timeAmount)
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
{
writeMove(p.X, p.Y, zPos, speed, extrusion_mm3_per_mm);
writeMove(p.X, p.Y, current_layer_z, speed, extrusion_mm3_per_mm);
}
void GCodeExport::writeMove(Point3 p, double speed, double extrusion_mm3_per_mm)
@@ -342,7 +494,7 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
{
//fprintf(f, "; %f e-per-mm %d mm-width %d mm/s\n", extrusion_per_mm, lineWidth, speed);
//fprintf(f, "M108 S%0.1f\r\n", rpm);
*output_stream << "M108 S" << std::setprecision(1) << rpm << new_line;
*output_stream << "M108 S" << PrecisionedDouble{1, rpm} << new_line;
currentSpeed = double(rpm);
}
//Add M101 or M201 to enable the proper extruder.
@@ -367,10 +519,8 @@ void GCodeExport::writeMoveBFB(int x, int y, int z, double speed, double extrusi
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 used as stub; BFB doesn't use the actual retraction amount; it performs retraction on the firmware automatically
}
}
*output_stream << std::setprecision(3) <<
"G1 X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y) <<
" Z" << INT2MM(z) << std::setprecision(1) << " F" << fspeed << new_line;
*output_stream << "G1 X" << MMtoStream{gcode_pos.X} << " Y" << MMtoStream{gcode_pos.Y} << " Z" << MMtoStream{z};
*output_stream << " F" << PrecisionedDouble{1, fspeed} << new_line;
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
@@ -382,8 +532,9 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
return;
#ifdef ASSERT_INSANE_OUTPUT
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
assert(speed < 400 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
assert(currentPosition != no_point3);
assert(Point3(x, y, z) != no_point3);
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
#endif //ASSERT_INSANE_OUTPUT
@@ -405,7 +556,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
Point3 diff = Point3(x,y,z) - getPosition();
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << new_line;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z} << new_line;
isZHopped = 0;
}
double prime_volume = extruder_attr[current_extruder].prime_volume;
@@ -418,7 +569,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
//Assume default UM2 retraction settings.
if (prime_volume > 0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, current_e_value} << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), 25.0);
@@ -426,7 +577,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
else
{
current_e_value += extruder_attr[current_extruder].retraction_e_amount_current;
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, current_e_value} << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
@@ -438,7 +589,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
}
else if (prime_volume > 0.0)
{
*output_stream << "G1 F" << (extruder_attr[current_extruder].last_retraction_prime_speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*output_stream << "G1 F" << PrecisionedDouble{1, extruder_attr[current_extruder].last_retraction_prime_speed * 60} << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, current_e_value} << new_line;
currentSpeed = extruder_attr[current_extruder].last_retraction_prime_speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
@@ -450,54 +601,55 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
{
*output_stream << "G0";
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons travelPoly;
PolygonRef travel = travelPoly.newPoly();
travel.add(Point(currentPosition.x, currentPosition.y));
travel.add(Point(x, y));
CommandSocket::getInstance()->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
CommandSocket::sendLineTo(extruder_attr[current_extruder].retraction_e_amount_current ? PrintFeatureType::MoveRetraction : PrintFeatureType::MoveCombing, Point(x, y), extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
if (currentSpeed != speed)
{
*output_stream << " F" << (speed * 60);
*output_stream << " F" << PrecisionedDouble{1, speed * 60};
currentSpeed = speed;
}
*output_stream << std::setprecision(3) <<
" X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y);
*output_stream << " X" << MMtoStream{gcode_pos.X} << " Y" << MMtoStream{gcode_pos.Y};
if (z != currentPosition.z + isZHopped)
*output_stream << " Z" << INT2MM(z + isZHopped);
{
*output_stream << " Z" << MMtoStream{z + isZHopped};
}
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value;
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << PrecisionedDouble{5, current_e_value};
*output_stream << new_line;
currentPosition = Point3(x, y, z);
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), speed);
}
void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool extruder_switch)
{
ExtruderTrainAttributes& extr_attr = extruder_attr[current_extruder];
if (flavor == EGCodeFlavor::BFB)//BitsFromBytes does automatic retraction.
{
if (extruder_switch)
{
if (!extr_attr.retraction_e_amount_current)
*output_stream << "M103" << new_line;
extr_attr.retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
}
return;
}
if (extruder_attr[current_extruder].retraction_e_amount_current == mmToE(config->distance))
{
return;
}
if (config->distance <= 0)
double old_retraction_e_amount = extr_attr.retraction_e_amount_current;
double new_retraction_e_amount = mmToE(config->distance);
double retraction_diff_e_amount = old_retraction_e_amount - new_retraction_e_amount;
if (std::abs(retraction_diff_e_amount) < 0.000001)
{
return;
}
{ // handle retraction limitation
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
std::deque<double>& extruded_volume_at_previous_n_retractions = extr_attr.extruded_volume_at_previous_n_retractions;
while (int(extruded_volume_at_previous_n_retractions.size()) > config->retraction_count_max && !extruded_volume_at_previous_n_retractions.empty())
{
// extruder switch could have introduced data which falls outside the retraction window
@@ -509,7 +661,7 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
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 * extruder_attr[current_extruder].filament_area)
&& current_extruded_volume < extruded_volume_at_previous_n_retractions.back() + config->retraction_extrusion_window * extr_attr.filament_area)
{
return;
}
@@ -519,105 +671,90 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
extruded_volume_at_previous_n_retractions.pop_back();
}
}
extruder_attr[current_extruder].last_retraction_prime_speed = config->primeSpeed;
double retraction_e_amount = mmToE(config->distance);
if (firmware_retract)
{
*output_stream << "G10" << new_line;
//Assume default UM2 retraction settings.
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value - retraction_e_amount)), 25); // TODO: hardcoded values!
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (config->speed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
currentSpeed = config->speed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value)), currentSpeed);
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount ;
extruder_attr[current_extruder].prime_volume += config->prime_volume;
if (config->zHop > 0)
{
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
}
}
void GCodeExport::writeRetraction_extruderSwitch()
{
if (flavor == EGCodeFlavor::BFB)
{
if (!extruder_attr[current_extruder].retraction_e_amount_current)
*output_stream << "M103" << new_line;
extruder_attr[current_extruder].retraction_e_amount_current = 1.0; // 1.0 is a stub; BFB doesn't use the actual retracted amount; retraction is performed by firmware
return;
}
double retraction_e_amount = mmToE(extruder_attr[current_extruder].extruder_switch_retraction_distance);
if (extruder_attr[current_extruder].retraction_e_amount_current == retraction_e_amount)
{
return;
}
double current_extruded_volume = getCurrentExtrudedVolume();
std::deque<double>& extruded_volume_at_previous_n_retractions = extruder_attr[current_extruder].extruded_volume_at_previous_n_retractions;
extruded_volume_at_previous_n_retractions.push_front(current_extruded_volume);
if (firmware_retract)
{
if (extruder_attr[current_extruder].retraction_e_amount_current)
if (extruder_switch && extr_attr.retraction_e_amount_current)
{
return;
}
*output_stream << "G10 S1" << new_line;
*output_stream << "G10";
if (extruder_switch)
{
*output_stream << " S1";
}
*output_stream << new_line;
//Assume default UM2 retraction settings.
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), eToMm(current_e_value + retraction_diff_e_amount)), 25); // TODO: hardcoded values!
}
else
{
current_e_value -= retraction_e_amount;
*output_stream << "G1 F" << (extruder_attr[current_extruder].extruderSwitchRetractionSpeed * 60) << " "
<< extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << current_e_value << new_line;
// the E value of the extruder switch retraction 'overwrites' the E value of the normal retraction
currentSpeed = extruder_attr[current_extruder].extruderSwitchRetractionSpeed;
extruder_attr[current_extruder].last_retraction_prime_speed = extruder_attr[current_extruder].extruderSwitchPrimeSpeed;
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;
}
extruder_attr[current_extruder].retraction_e_amount_current = retraction_e_amount; // suppose that for UM2 the retraction amount in the firmware is equal to the provided amount
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;
}
void GCodeExport::switchExtruder(int new_extruder)
void GCodeExport::writeZhopStart(int hop_height)
{
if (hop_height > 0)
{
isZHopped = hop_height;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z + isZHopped} << new_line;
}
}
void GCodeExport::startExtruder(int new_extruder)
{
if (new_extruder != current_extruder) // wouldn't be the case on the very first extruder start if it's extruder 0
{
if (flavor == EGCodeFlavor::MAKERBOT)
{
*output_stream << "M135 T" << new_extruder << new_line;
}
else
{
*output_stream << "T" << new_extruder << new_line;
}
}
current_extruder = new_extruder;
assert(getCurrentExtrudedVolume() == 0.0 && "Just after an extruder switch we haven't extruded anything yet!");
resetExtrusionValue(); // zero the E value on the new extruder, just to be sure
writeCode(extruder_attr[new_extruder].start_code.c_str());
CommandSocket::setExtruderForSend(new_extruder);
CommandSocket::setSendCurrentPosition( getPositionXY() );
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
currentPosition.z += 1;
}
void GCodeExport::switchExtruder(int new_extruder, const RetractionConfig& retraction_config_old_extruder)
{
if (current_extruder == new_extruder)
return;
writeRetraction_extruderSwitch();
bool force = true;
bool extruder_switch = true;
writeRetraction(&const_cast<RetractionConfig&>(retraction_config_old_extruder), force, extruder_switch);
resetExtrusionValue(); // should be called on the old extruder
resetExtrusionValue(); // zero the E value on the old extruder, so that the current_e_value is registered on the old extruder
int old_extruder = current_extruder;
current_extruder = new_extruder;
if (flavor == EGCodeFlavor::MACH3)
{
resetExtrusionValue(); // also zero the E value on the new extruder
}
writeCode(extruder_attr[old_extruder].end_code.c_str());
if (flavor == EGCodeFlavor::MAKERBOT)
{
*output_stream << "M135 T" << current_extruder << new_line;
}
else
{
*output_stream << "T" << current_extruder << new_line;
}
writeCode(extruder_attr[new_extruder].start_code.c_str());
//Change the Z position so it gets re-writting again. We do not know if the switch code modified the Z position.
currentPosition.z += 1;
startExtruder(new_extruder);
}
void GCodeExport::writeCode(const char* str)
@@ -625,6 +762,32 @@ void GCodeExport::writeCode(const char* str)
*output_stream << str << new_line;
}
void GCodeExport::writePrimeTrain(double travel_speed)
{
if (extruder_attr[current_extruder].is_primed)
{ // extruder is already primed once!
return;
}
Point3 prime_pos = extruder_attr[current_extruder].prime_pos;
if (!extruder_attr[current_extruder].prime_pos_is_abs)
{
prime_pos += currentPosition;
}
writeMove(prime_pos, travel_speed, 0.0);
if (flavor == EGCodeFlavor::GRIFFIN)
{
*output_stream << "G280" << new_line;
}
else
{
// there is no prime gcode for other firmware versions...
}
extruder_attr[current_extruder].is_primed = true;
}
void GCodeExport::writeFanCommand(double speed)
{
if (currentFanSpeed == speed)
@@ -634,7 +797,7 @@ void GCodeExport::writeFanCommand(double speed)
if (flavor == EGCodeFlavor::MAKERBOT)
*output_stream << "M126 T0" << new_line; //value = speed * 255 / 100 // Makerbot cannot set fan speed...;
else
*output_stream << "M106 S" << (speed * 255 / 100) << new_line;
*output_stream << "M106 S" << PrecisionedDouble{1, speed * 255 / 100} << new_line;
}
else
{
@@ -657,7 +820,10 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
*output_stream << "M104";
if (extruder != current_extruder)
*output_stream << " T" << extruder;
*output_stream << " S" << temperature << new_line;
#ifdef ASSERT_INSANE_OUTPUT
assert(temperature >= 0);
#endif // ASSERT_INSANE_OUTPUT
*output_stream << " S" << PrecisionedDouble{1, temperature} << new_line;
extruder_attr[extruder].currentTemperature = temperature;
}
@@ -667,15 +833,61 @@ void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << new_line;
*output_stream << PrecisionedDouble{1, temperature} << new_line;
}
void GCodeExport::finalize(double moveSpeed, const char* endCode)
void GCodeExport::writeAcceleration(double acceleration)
{
if (current_acceleration != acceleration)
{
*output_stream << "M204 S" << PrecisionedDouble{0, acceleration} << new_line; // Print and Travel acceleration
current_acceleration = acceleration;
estimateCalculator.setAcceleration(acceleration);
}
}
void GCodeExport::writeJerk(double jerk)
{
if (current_jerk != jerk)
{
if (getFlavor() == EGCodeFlavor::REPETIER)
{
*output_stream << "M207 X";
}
else
{
*output_stream << "M205 X";
}
*output_stream << PrecisionedDouble{2, jerk} << new_line;
current_jerk = jerk;
estimateCalculator.setMaxXyJerk(jerk);
}
}
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;
current_max_z_feedrate = max_z_feedrate;
estimateCalculator.setMaxZFeedrate(max_z_feedrate);
}
}
double GCodeExport::getCurrentMaxZFeedrate()
{
return current_max_z_feedrate;
}
void GCodeExport::finalize(const char* endCode)
{
writeFanCommand(0);
writeCode(endCode);
log("Print time: %d\n", int(getTotalPrintTime()));
log("Filament: %d\n", int(getTotalFilamentUsed(0)));
long print_time = getTotalPrintTime();
int mat_0 = getTotalFilamentUsed(0);
log("Print time: %d\n", print_time);
log("Print time (readable): %dh %dm %ds\n", print_time / 60 / 60, (print_time / 60) % 60, print_time % 60);
log("Filament: %d\n", mat_0);
for(int n=1; n<MAX_EXTRUDERS; n++)
if (getTotalFilamentUsed(n) > 0)
log("Filament%d: %d\n", n + 1, int(getTotalFilamentUsed(n)));
+174 -186
Ver Arquivo
@@ -6,141 +6,30 @@
#include <deque> // for extrusionAmountAtPreviousRetractions
#include <sstream> // for stream.str()
#include "settings.h"
#include "settings/settings.h"
#include "utils/intpoint.h"
#include "utils/NoCopy.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "commandSocket.h"
#include "RetractionConfig.h"
namespace cura {
/*!
* Coasting configuration used during printing.
* Can differ per extruder.
*
* Might be used in the future to have different coasting per feature, e.g. outer wall only.
*/
struct CoastingConfig
{
bool coasting_enable;
double coasting_volume;
double coasting_speed;
double coasting_min_volume;
};
class RetractionConfig
{
public:
double distance; //!< The distance retracted (in mm)
double speed; //!< The speed with which to retract (in mm/s)
double primeSpeed; //!< the speed with which to unretract (in mm/s)
double prime_volume; //!< the amount of material primed after unretracting (in mm^3)
int zHop; //!< the amount with which to lift the head during a retraction-travel
int retraction_min_travel_distance; //!<
double retraction_extrusion_window; //!< in mm
int retraction_count_max;
bool coasting_enable; //!< Whether coasting is enabled on the extruder to which this config is attached
double coasting_volume; //!< The volume leeked when printing without feeding
double coasting_speed; //!< A modifier (0-1) on the last used travel speed to move slower during coasting
double coasting_min_volume; //!< The minimal volume printed to build up enough pressure to leek the coasting_volume
};
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
class GCodePathConfig
{
private:
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
double speed; //!< current movement speed (mm/s) (modified by layer_nr etc.)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow in %
int layer_thickness; //!< layer height
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
public:
PrintFeatureType type; //!< name of the feature type
RetractionConfig *const retraction_config;
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type)
: speed_iconic(0)
, speed(0)
, line_width(0)
, extrusion_mm3_per_mm(0.0)
, type(type)
, retraction_config(retraction_config)
{
}
/*!
* Initialize some of the member variables.
*
* Warning! setLayerHeight still has to be called before this object can be used.
*/
void init(double speed, int line_width, double flow)
{
speed_iconic = speed;
this->speed = speed;
this->line_width = line_width;
this->flow = flow;
}
/*!
* Set the layer height and (re)compute the extrusion_per_mm
*/
void setLayerHeight(int layer_height)
{
this->layer_thickness = layer_height;
calculateExtrusion();
}
/*!
* Set the speed to somewhere between the @p min_speed and the speed_iconic.
*
* This functions should not be called with @p layer_nr > @p max_speed_layer !
*
* \param min_speed The speed at layer zero
* \param layer_nr The layer number
* \param max_speed_layer The layer number for which the speed_iconic should be used.
*/
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
{
speed = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
* Set the speed to the iconic speed, i.e. the normal speed of the feature type for which this is a config.
*/
void setSpeedIconic()
{
speed = speed_iconic;
}
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM()
{
return extrusion_mm3_per_mm;
}
/*!
* Get the movement speed in mm/s
*/
double getSpeed()
{
return speed;
}
int getLineWidth()
{
return line_width;
}
bool isTravelPath()
{
return line_width == 0;
}
double getFlowPercentage()
{
return flow;
}
private:
void calculateExtrusion()
{
extrusion_mm3_per_mm = INT2MM(line_width) * INT2MM(layer_thickness) * double(flow) / 100.0;
}
};
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
// Any customizations on GCodes flavors are done in this class.
@@ -149,19 +38,23 @@ class GCodeExport : public NoCopy
private:
struct ExtruderTrainAttributes
{
Point3 prime_pos; //!< The location this nozzle is primed before printing
bool prime_pos_is_abs; //!< Whether the prime position is absolute, rather than relative to the last given position
bool is_primed; //!< Whether this extruder has currently already been primed in this print
bool is_used; //!< Whether this extruder train is actually used during the printing of all meshgroups
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string material_guid; //!< The GUID for the material used by this extruder
std::string start_code;
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
double extruder_switch_retraction_distance; //<! extruder switch retraction distance in mm
int extruderSwitchRetractionSpeed; //!< extruder switch retraction speed in mm/s
int extruderSwitchPrimeSpeed; //!< prime speed of extruder switch in mm/s
double totalFilament; //!< total filament used per extruder in mm^3
int currentTemperature;
int initial_temp; //!< Temperature this nozzle needs to be at the start of the print.
double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values)
double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle.
@@ -172,16 +65,18 @@ private:
std::deque<double> extruded_volume_at_previous_n_retractions; // in mm^3
ExtruderTrainAttributes()
: nozzle_offset(0,0)
: prime_pos(0, 0, 0)
, prime_pos_is_abs(false)
, is_primed(false)
, is_used(false)
, nozzle_offset(0,0)
, extruderCharacter(0)
, start_code("")
, end_code("")
, filament_area(0)
, extruder_switch_retraction_distance(0.0)
, extruderSwitchRetractionSpeed(0)
, extruderSwitchPrimeSpeed(0)
, totalFilament(0)
, currentTemperature(0)
, initial_temp(0)
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
@@ -189,7 +84,10 @@ private:
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
unsigned int extruder_count;
bool use_extruder_offset_to_offset_coords;
Point3 machine_dimensions;
std::string machine_name;
std::ostream* output_stream;
std::string new_line;
@@ -197,21 +95,33 @@ private:
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
Point3 currentPosition;
double currentSpeed; //!< The current speed (F values / 60) in mm/s
int zPos; // TODO: why is this different from currentPosition.z ? zPos is set every layer, while currentPosition.z is set every move. However, the z position is generally not changed within a layer!
double current_acceleration; //!< The current acceleration in the XY direction (in mm/s^2)
double current_jerk; //!< The current jerk in the XY direction (in mm/s^3)
double current_max_z_feedrate; //!< The current max z speed
/*!
* The z position to be used on the next xy move, if the head wasn't in the correct z position yet.
*
* \see GCodeExport::writeMove(Point, double, double)
*
* \note After GCodeExport::writeMove(Point, double, double) has been called currentPosition.z coincides with this value
*/
int current_layer_z;
int isZHopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts)
int current_extruder;
int currentFanSpeed;
EGCodeFlavor flavor;
double totalPrintTime;
double totalPrintTime; //!< The total estimated print time in seconds
TimeEstimateCalculator estimateCalculator;
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
unsigned int layer_nr; //!< for sending travel data
int initial_bed_temp; //!< bed temperature at the beginning of the print.
protected:
/*!
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
@@ -254,22 +164,26 @@ public:
/*!
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
*
* \param print_time The total print time of the whole file (if known)
* \param filament_used_0 The total mm^3 filament used for the primary extruder (if known)
* \param filament_used_1 The total mm^3 filament used for the secondary extruder (if used and if known)
* \param print_time The total print time in seconds of the whole gcode (if known)
* \param filament_used The total mm^3 filament used for each extruder or a vector of the wrong size of unknown
* \param mat_ids The material GUIDs for each material.
* \return The string representing the file header
*/
std::string getFileHeader(double print_time = 666, int filament_used_0 = 666, int filament_used_1 = 0);
std::string getFileHeader(const double* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<std::string>& mat_ids = std::vector<std::string>());
void setLayerNr(unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
int getNozzleSize(int extruder_idx);
bool getExtruderIsUsed(const int extruder_nr) const; //!< Returns whether the extruder with the given index is used up until the current meshgroup
Point getExtruderOffset(int id);
Point getGcodePos(int64_t x, int64_t y, int extruder_train);
int getNozzleSize(const int extruder_nr) const;
Point getExtruderOffset(const int id) const;
std::string getMaterialGUID(const int extruder_nr) const; //!< returns the GUID of the material used for the nozzle with id \p extruder_nr
Point getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const;
void setFlavor(EGCodeFlavor flavor);
EGCodeFlavor getFlavor();
@@ -293,15 +207,34 @@ public:
double getCurrentExtrudedVolume();
double getTotalFilamentUsed(int e);
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr);
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime();
void updateTotalPrintTime();
void resetTotalPrintTimeAndFilament();
void writeComment(std::string comment);
void writeTypeComment(const char* type);
void writeTypeComment(PrintFeatureType type);
/*!
* Write a comment saying what (estimated) time has passed up to this point
*
* \param time The time passed up till this point
*/
void writeTimeComment(const double time);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
@@ -316,65 +249,120 @@ public:
void writeDelay(double timeAmount);
void writeMove(Point p, double speed, double extrusion_per_mm);
void writeMove(Point p, double speed, double extrusion_mm3_per_mm);
void writeMove(Point3 p, double speed, double extrusion_per_mm);
void writeMove(Point3 p, double speed, double extrusion_mm3_per_mm);
private:
void writeMove(int x, int y, int z, double speed, double extrusion_per_mm);
void writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
/*!
* The writeMove when flavor == BFB
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_per_mm);
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
public:
void writeRetraction(RetractionConfig* config, bool force=false);
void writeRetraction_extruderSwitch();
void switchExtruder(int newExtruder);
void writeRetraction(RetractionConfig* config, bool force = false, bool extruder_switch = false);
/*!
* Start a z hop with the given \p hop_height
*
* \param hop_height The height to move above the current layer
*/
void writeZhopStart(int hop_height);
/*!
* Start the new_extruder:
* - set new extruder
* - zero E value
* - write extruder start gcode
*
* \param new_extruder The extruder to start with
*/
void startExtruder(int new_extruder);
/*!
* Switch to the new_extruder:
* - perform neccesary retractions
* - fiddle with E-values
* - write extruder end gcode
* - set new extruder
* - write extruder start gcode
*
* \param new_extruder The extruder to switch to
* \param retraction_config_old_extruder The extruder switch retraction config of the old extruder, to perform the extruder switch retraction with.
*/
void switchExtruder(int new_extruder, const RetractionConfig& retraction_config_old_extruder);
void writeCode(const char* str);
/*!
* Write the gcode for priming the current extruder train so that it can be used.
*
* \param travel_speed The travel speed when priming involves a movement
*/
void writePrimeTrain(double travel_speed);
void writeFanCommand(double speed);
void writeTemperatureCommand(int extruder, double temperature, bool wait = false);
void writeBedTemperatureCommand(double temperature, bool wait = false);
void preSetup(MeshGroup* settings)
{
for(int n=0; n<settings->getSettingAsCount("machine_extruder_count"); n++)
{
ExtruderTrain* train = settings->getExtruderTrain(n);
setFilamentDiameter(n, train->getSettingInMicrons("material_diameter"));
extruder_attr[n].nozzle_size = train->getSettingInMicrons("machine_nozzle_size");
extruder_attr[n].nozzle_offset = Point(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
extruder_attr[n].start_code = train->getSettingString("machine_extruder_start_code");
extruder_attr[n].end_code = train->getSettingString("machine_extruder_end_code");
extruder_attr[n].extruder_switch_retraction_distance = INT2MM(train->getSettingInMicrons("switch_extruder_retraction_amount"));
extruder_attr[n].extruderSwitchRetractionSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_retraction_speed");
extruder_attr[n].extruderSwitchPrimeSpeed = train->getSettingInMillimetersPerSecond("switch_extruder_prime_speed");
extruder_attr[n].last_retraction_prime_speed = train->getSettingInMillimetersPerSecond("retraction_prime_speed"); // the alternative would be switch_extruder_prime_speed, but dual extrusion might not even be configured...
}
/*!
* Write the command for setting the acceleration to a specific value
*/
void writeAcceleration(double acceleration);
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
/*!
* Write the command for setting the jerk to a specific value
*/
void writeJerk(double jerk);
/*!
* Write the command for setting the maximum z feedrate to a specific value
*/
void writeMaxZFeedrate(double max_z_feedrate);
/*!
* Get the last set max z feedrate value sent in the gcode.
*
* Returns a value <= 0 when no value is set.
*/
double getCurrentMaxZFeedrate();
/*!
* Set member variables using the settings in \p settings
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
*/
void preSetup(const MeshGroup* settings);
/*!
* Handle the initial (bed/nozzle) temperatures before any gcode is processed.
* These temperatures are set in the pre-print setup in the firmware.
*
* See FffGcodeWriter::processStartingCode
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
*/
void setInitialTemps(const MeshGroup& settings);
/*!
* Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
* This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
*
* \param extruder_nr The extruder number for which to better specify the temp
* \param temp The temp at which the nozzle should be at startup
*/
void setInitialTemp(int extruder_nr, double temp);
/*!
* Finish the gcode: turn fans off, write end gcode and flush all gcode left in the buffer.
*
* \param endCode The end gcode to be appended at the very end.
*/
void finalize(const char* endCode);
if (flavor == EGCodeFlavor::BFB)
{
new_line = "\r\n";
}
else
{
new_line = "\n";
}
}
void finalize(double moveSpeed, const char* endCode);
};
}
#endif//GCODEEXPORT_H
+388 -225
Ver Arquivo
@@ -1,8 +1,8 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include <cstring>
#include "gcodePlanner.h"
#include "pathOrderOptimizer.h"
#include "sliceDataStorage.h"
#include "debug.h" // debugging
#include "utils/polygonUtils.h"
#include "MergeInfillLines.h"
@@ -22,6 +22,49 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
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)
: extruder(extruder)
, required_temp(-1)
, start_position(start_position)
, layer_nr(layer_nr)
, layer_thickness(layer_thickness)
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
, retraction_config(retraction_config)
, extrudeSpeedFactor(1.0)
, travelSpeedFactor(1.0)
, extraTime(0.0)
, totalPrintTime(0)
{
}
void ExtruderPlan::setExtrudeSpeedFactor(double speedFactor)
{
this->extrudeSpeedFactor = speedFactor;
}
double ExtruderPlan::getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void ExtruderPlan::setTravelSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1.0;
this->travelSpeedFactor = speedFactor;
}
double ExtruderPlan::getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void ExtruderPlan::setFanSpeed(double _fan_speed)
{
fan_speed = _fan_speed;
}
double ExtruderPlan::getFanSpeed()
{
return fan_speed;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow, bool spiralize)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
@@ -30,15 +73,12 @@ GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceF
paths.emplace_back();
GCodePath* ret = &paths.back();
ret->retract = false;
ret->perform_z_hop = false;
ret->config = config;
ret->done = false;
ret->flow = flow;
ret->spiralize = spiralize;
ret->space_fill_type = space_fill_type;
if (config != &storage.travel_config)
{
last_retraction_config = config->retraction_config;
}
return ret;
}
@@ -49,27 +89,24 @@ 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, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::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)
: storage(storage)
, layer_nr(layer_nr)
, z(z)
, layer_thickness(layer_thickness)
, start_position(last_position)
, lastPosition(last_position)
, comb_boundary_inside(computeCombBoundaryInside())
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
, last_extruder_previous_layer(current_extruder)
, last_planned_extruder_setting_base(storage.meshgroup->getExtruderTrain(current_extruder))
, comb_boundary_inside(computeCombBoundaryInside(combing_mode))
, fan_speed_layer_time_settings_per_extruder(fan_speed_layer_time_settings_per_extruder)
{
extruder_plans.reserve(storage.meshgroup->getExtruderCount());
extruder_plans.emplace_back(current_extruder);
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]);
comb = nullptr;
was_inside = true; // means it will try to get inside the comb boundary first
is_inside = true; // means it will try to get inside the comb boundary
last_retraction_config = &storage.retraction_config; // start with general config
setExtrudeSpeedFactor(1.0);
setTravelSpeedFactor(1.0);
extraTime = 0.0;
totalPrintTime = 0.0;
if (retraction_combing)
was_inside = is_inside_mesh;
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)
{
comb = new Comb(storage, layer_nr, comb_boundary_inside, comb_boundary_offset, travel_avoid_other_parts, travel_avoid_distance);
}
@@ -83,21 +120,52 @@ GCodePlanner::~GCodePlanner()
delete comb;
}
Polygons GCodePlanner::computeCombBoundaryInside()
SettingsBaseVirtual* GCodePlanner::getLastPlannedExtruderTrainSettings()
{
return last_planned_extruder_setting_base;
}
Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
{
if (combing_mode == CombingMode::OFF)
{
return Polygons();
}
if (layer_nr < 0)
{ // when a raft is present
return storage.raftOutline.offset(MM2INT(0.1));
if (combing_mode == CombingMode::NO_SKIN)
{
return Polygons();
}
else
{
return storage.raftOutline.offset(MM2INT(0.1));
}
}
else
{
Polygons layer_walls;
Polygons comb_boundary;
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(layer_walls);
if (mesh.getSettingAsCombingMode("retraction_combing") == CombingMode::NO_SKIN)
{
for (SliceLayerPart& part : layer.parts)
{
comb_boundary.add(part.infill_area);
}
}
else
{
if (mesh.getSettingBoolean("infill_mesh"))
{
continue;
}
layer.getSecondOrInnermostWalls(comb_boundary);
}
}
return layer_walls;
return comb_boundary;
}
}
@@ -108,14 +176,14 @@ void GCodePlanner::setIsInside(bool _is_inside)
bool GCodePlanner::setExtruder(int extruder)
{
if (extruder == extruder_plans.back().extruder)
if (extruder == getExtruder())
{
return false;
}
setIsInside(false);
{ // handle end position of the prev extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
SettingsBaseVirtual* train = getLastPlannedExtruderTrainSettings();
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
if (!end_pos_absolute)
{
@@ -123,18 +191,26 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
end_pos += extruder_offset; // absolute end pos is given as a head position
}
addTravel(end_pos); // + extruder_offset cause it
}
extruder_plans.emplace_back(extruder);
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
extruder_plans.back().extruder = extruder;
}
else
{
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
}
last_planned_extruder_setting_base = storage.meshgroup->getExtruderTrain(extruder);
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
{ // handle starting pos of the new extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
SettingsBaseVirtual* train = getLastPlannedExtruderTrainSettings();
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
if (!start_pos_absolute)
{
@@ -142,6 +218,7 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
start_pos += extruder_offset; // absolute start pos is given as a head position
}
lastPosition = start_pos;
@@ -170,76 +247,86 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
void 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()];
bool combed = false;
if (comb != nullptr && lastPosition != no_point)
SettingsBaseVirtual* extr = getLastPlannedExtruderTrainSettings();
const bool perform_z_hops = extr->getSettingBoolean("retraction_hop_enabled");
const bool is_first_travel_of_extruder_after_switch = extruder_plans.back().paths.size() == 0 && (extruder_plans.size() > 1 || last_extruder_previous_layer != getExtruder());
const bool bypass_combing = is_first_travel_of_extruder_after_switch && extr->getSettingBoolean("retraction_hop_after_extruder_switch");
if (comb != nullptr && !bypass_combing && lastPosition != no_point)
{
const bool perform_z_hops_only_when_collides = extr->getSettingBoolean("retraction_hop_only_when_collides");
CombPaths combPaths;
combed = comb->calc(lastPosition, p, combPaths, was_inside, is_inside, last_retraction_config->retraction_min_travel_distance);
bool via_outside_makes_combing_fail = perform_z_hops && !perform_z_hops_only_when_collides;
bool fail_on_unavoidable_obstacles = perform_z_hops && perform_z_hops_only_when_collides;
combed = comb->calc(lastPosition, p, combPaths, was_inside, is_inside, retraction_config.retraction_min_travel_distance, via_outside_makes_combing_fail, fail_on_unavoidable_obstacles);
if (combed)
{
bool retract = combPaths.size() > 1;
if (!retract)
{ // check whether we want to retract
for (CombPath& combPath : combPaths)
{ // retract when path moves through a boundary
if (combPath.cross_boundary || combPath.throughAir)
{
retract = true;
break;
if (combPaths.throughAir)
{
retract = true;
}
else
{
for (CombPath& combPath : combPaths)
{ // retract when path moves through a boundary
if (combPath.cross_boundary)
{
retract = true;
break;
}
}
}
if (combPaths.size() == 1)
{
CombPath path = combPaths[0];
if (path.throughAir && !path.cross_boundary && path.size() == 2 && path[0] == lastPosition && path[1] == p)
if (combPaths.throughAir && !path.cross_boundary && path.size() == 2 && path[0] == lastPosition && path[1] == p)
{ // limit the retractions from support to support, which didn't cross anything
retract = false;
}
}
}
if (retract && last_retraction_config->zHop > 0)
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
for (CombPath& combPath : combPaths)
{ // add all comb paths (don't do anything special for paths which are moving through air)
if (combPath.size() == 0)
{
path->retract = true;
continue;
}
}
else
{
for (CombPath& combPath : combPaths)
{ // add all comb paths (don't do anything special for paths which are moving through air)
if (combPath.size() == 0)
{
continue;
}
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path->retract = retract;
for (Point& combPoint : combPath)
{
path->points.push_back(combPoint);
}
lastPosition = combPath.back();
path = getLatestPathWithConfig(&travel_config, SpaceFillType::None);
path->retract = retract;
// don't perform a z-hop
for (Point& combPoint : combPath)
{
path->points.push_back(combPoint);
}
lastPosition = combPath.back();
}
}
}
if (!combed) {
// no combing? always retract!
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
if (!shorterThen(lastPosition - p, retraction_config.retraction_min_travel_distance))
{
if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc)
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
}
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&travel_config, SpaceFillType::None);
path->retract = true;
path->perform_z_hop = perform_z_hops;
}
}
@@ -251,7 +338,7 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path = getLatestPathWithConfig(&storage.travel_config_per_extruder[getExtruder()], SpaceFillType::None);
}
path->points.push_back(p);
lastPosition = p;
@@ -271,13 +358,15 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
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];
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0, spiralize);
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
}
else
{
@@ -312,7 +401,6 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
orderOptimizer.optimize();
for (int poly_idx : orderOptimizer.polyOrder)
{
// addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config); // adds line as polygon; old code
PolygonRef polygon = polygons[poly_idx];
int start = orderOptimizer.polyStart[poly_idx];
int end = 1 - start;
@@ -331,7 +419,8 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
}
}
void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrudeTime)
void ExtruderPlan::forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrudeTime)
{
double totalTime = travelTime + extrudeTime;
if (totalTime < minTime && extrudeTime > 0.0)
@@ -340,16 +429,13 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, do
if (minExtrudeTime < 1)
minExtrudeTime = 1;
double factor = extrudeTime / minExtrudeTime;
for(ExtruderPlan& extr_plan : extruder_plans)
for (GCodePath& path : paths)
{
for (GCodePath& path : extr_plan.paths)
{
if (path.isTravelPath())
continue;
double speed = path.config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path.config->getSpeed();
}
if (path.isTravelPath())
continue;
double speed = path.config->getSpeed() * factor;
if (speed < minimalSpeed)
factor = minimalSpeed / path.config->getSpeed();
}
//Only slow down for the minimal time if that will be slower.
@@ -366,13 +452,10 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, do
double inv_factor = 1.0 / factor; // cause multiplication is faster than division
// Adjust stored naive time estimates
for(ExtruderPlan& extr_plan : extruder_plans)
estimates.extrude_time *= inv_factor;
for (GCodePath& path : paths)
{
extr_plan.estimates.extrude_time *= inv_factor;
for (GCodePath& path : extr_plan.paths)
{
path.estimates.extrude_time *= inv_factor;
}
path.estimates.extrude_time *= inv_factor;
}
if (minTime - (extrudeTime * inv_factor) - travelTime > 0.1)
@@ -380,79 +463,75 @@ void GCodePlanner::forceMinimalLayerTime(double minTime, double minimalSpeed, do
this->extraTime = minTime - (extrudeTime * inv_factor) - travelTime;
}
this->totalPrintTime = (extrudeTime * inv_factor) + travelTime;
}else{
this->totalPrintTime = totalTime;
}
}
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
TimeMaterialEstimates ExtruderPlan::computeNaiveTimeEstimates()
{
TimeMaterialEstimates ret;
Point p0 = start_position;
bool was_retracted = false; // wrong assumption; won't matter that much. (TODO)
for(ExtruderPlan& extr_plan : extruder_plans)
for (GCodePath& path : paths)
{
for (GCodePath& path : extr_plan.paths)
bool is_extrusion_path = false;
double* path_time_estimate;
double& material_estimate = path.estimates.material;
if (!path.isTravelPath())
{
bool is_extrusion_path = false;
double* path_time_estimate;
double& material_estimate = path.estimates.material;
if (!path.isTravelPath())
is_extrusion_path = true;
path_time_estimate = &path.estimates.extrude_time;
}
else
{
if (path.retract)
{
is_extrusion_path = true;
path_time_estimate = &path.estimates.extrude_time;
path_time_estimate = &path.estimates.retracted_travel_time;
}
else
{
path_time_estimate = &path.estimates.unretracted_travel_time;
}
if (path.retract != was_retracted)
{ // handle retraction times
double retract_unretract_time;
if (path.retract)
{
path_time_estimate = &path.estimates.retracted_travel_time;
retract_unretract_time = retraction_config.distance / retraction_config.speed;
}
else
{
path_time_estimate = &path.estimates.unretracted_travel_time;
}
if (path.retract != was_retracted)
{ // handle retraction times
double retract_unretract_time;
RetractionConfig& retraction_config = *path.config->retraction_config;
if (path.retract)
{
retract_unretract_time = retraction_config.distance / retraction_config.speed;
}
else
{
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
}
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
retract_unretract_time = retraction_config.distance / retraction_config.primeSpeed;
}
path.estimates.retracted_travel_time += 0.5 * retract_unretract_time;
path.estimates.unretracted_travel_time += 0.5 * retract_unretract_time;
}
for(Point& p1 : path.points)
{
double length = vSizeMM(p0 - p1);
if (is_extrusion_path)
{
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
}
double thisTime = length / path.config->getSpeed();
*path_time_estimate += thisTime;
p0 = p1;
}
extr_plan.estimates += path.estimates;
}
ret += extr_plan.estimates;
for(Point& p1 : path.points)
{
double length = vSizeMM(p0 - p1);
if (is_extrusion_path)
{
material_estimate += length * INT2MM(layer_thickness) * INT2MM(path.config->getLineWidth());
}
double thisTime = length / path.config->getSpeed();
*path_time_estimate += thisTime;
p0 = p1;
}
estimates += path.estimates;
}
return ret;
return estimates;
}
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time)
{
FanSpeedLayerTimeSettings& fsml = fan_speed_layer_time_settings;
TimeMaterialEstimates estimates = computeNaiveTimeEstimates();
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
totalPrintTime = estimates.getTotalTime();
if (force_minimal_layer_time)
{
forceMinimalLayerTime(fsml.cool_min_layer_time, fsml.cool_min_speed, estimates.getTravelTime(), estimates.getExtrudeTime());
}
/*
min layer time
:
@@ -470,11 +549,11 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
// interpolate fan speed (for cool_fan_full_layer and for cool_min_layer_time_fan_speed_max)
fan_speed = fsml.cool_fan_speed_min;
double totalLayerTime = estimates.unretracted_travel_time + estimates.extrude_time;
if (totalLayerTime < fsml.cool_min_layer_time)
if (force_minimal_layer_time && totalLayerTime < fsml.cool_min_layer_time)
{
fan_speed = fsml.cool_fan_speed_max;
}
else if (totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
else if (force_minimal_layer_time && totalLayerTime < fsml.cool_min_layer_time_fan_speed_max)
{
// when forceMinimalLayerTime didn't change the extrusionSpeedFactor, we adjust the fan speed
double fan_speed_diff = fsml.cool_fan_speed_max - fsml.cool_fan_speed_min;
@@ -499,70 +578,143 @@ void GCodePlanner::processFanSpeedAndMinimalLayerTime()
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
fan_speed = fan_speed * layer_nr / fsml.cool_fan_full_layer;
fan_speed = fan_speed * std::max(0, layer_nr) / fsml.cool_fan_full_layer;
}
}
TimeMaterialEstimates GCodePlanner::computeNaiveTimeEstimates()
{
TimeMaterialEstimates ret;
for (ExtruderPlan& extruder_plan : extruder_plans)
{
ret += extruder_plan.computeNaiveTimeEstimates();
}
return ret;
}
void GCodePlanner::processFanSpeedAndMinimalLayerTime()
{
for (unsigned int extr_plan_idx = 0; extr_plan_idx < extruder_plans.size(); extr_plan_idx++)
{
ExtruderPlan& extruder_plan = extruder_plans[extr_plan_idx];
bool force_minimal_layer_time = extr_plan_idx == extruder_plans.size() - 1;
extruder_plan.processFanSpeedAndMinimalLayerTime(force_minimal_layer_time);
}
}
void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness)
void GCodePlanner::writeGCode(GCodeExport& gcode)
{
completeConfigs();
CommandSocket::setLayerForSend(layer_nr);
CommandSocket::setSendCurrentPosition( gcode.getPositionXY() );
gcode.setLayerNr(layer_nr);
gcode.writeLayerComment(layer_nr);
gcode.setZ(z);
gcode.writeFanCommand(fan_speed);
GCodePathConfig* last_extrusion_config = nullptr;
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");
bool jerk_enabled = storage.getSettingBoolean("jerk_enabled");
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];
if (extruder != extruder_plan.extruder)
{
int prev_extruder = extruder;
extruder = extruder_plan.extruder;
gcode.switchExtruder(extruder);
gcode.switchExtruder(extruder, storage.extruder_switch_retraction_config_per_extruder[prev_extruder]);
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
if (train->getSettingInMillimetersPerSecond("max_feedrate_z_override") > 0)
{
gcode.writeMaxZFeedrate(train->getSettingInMillimetersPerSecond("max_feedrate_z_override"));
}
{ // require printing temperature to be met
constexpr bool wait = true;
gcode.writeTemperatureCommand(extruder, extruder_plan.required_temp, wait);
}
// prime extruder if it hadn't been used yet
gcode.writePrimeTrain(storage.meshgroup->getExtruderTrain(extruder)->getSettingInMillimetersPerSecond("speed_travel"));
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);
}
}
gcode.writeFanCommand(extruder_plan.getFanSpeed());
std::vector<GCodePath>& paths = extruder_plan.paths;
extruder_plan.inserts.sort([](const NozzleTempInsert& a, const NozzleTempInsert& b) -> bool {
return a.path_idx < b.path_idx;
} );
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
if (train->getSettingInMillimetersPerSecond("max_feedrate_z_override") > 0)
{
gcode.writeMaxZFeedrate(train->getSettingInMillimetersPerSecond("max_feedrate_z_override"));
}
bool speed_equalize_flow_enabled = train->getSettingBoolean("speed_equalize_flow_enabled");
double speed_equalize_flow_max = train->getSettingInMillimetersPerSecond("speed_equalize_flow_max");
int64_t nozzle_size = gcode.getNozzleSize(extruder);
for(unsigned int path_idx = 0; path_idx < paths.size(); path_idx++)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& path = paths[path_idx];
if (acceleration_enabled)
{
gcode.writeAcceleration(path.config->getAcceleration());
}
if (jerk_enabled)
{
gcode.writeJerk(path.config->getJerk());
}
if (path.retract)
{
writeRetraction(gcode, extruder_plan_idx, path_idx);
gcode.writeRetraction(&retraction_config);
if (path.perform_z_hop)
{
gcode.writeZhopStart(retraction_config.zHop);
}
}
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
if (!path.config->isTravelPath() && last_extrusion_config != path.config)
{
gcode.writeTypeComment(path.config->type);
last_extrusion_config = path.config;
}
double speed = path.config->getSpeed();
if (path.isTravelPath())// Only apply the extrudeSpeed to extrusion moves
speed *= getTravelSpeedFactor();
// Apply the relevant factor
if (path.config->isTravelPath())
speed *= extruder_plan.getTravelSpeedFactor();
else
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
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 !!
{ // !! has effect on path_idx !!
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
continue;
}
if (path.config == &storage.travel_config)
if (path.config->isTravelPath())
{ // early comp for travel paths, which are handled more simply
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
@@ -583,7 +735,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layer_thickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
}
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
{ // normal path to gcode algorithm
@@ -591,14 +743,14 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
false &&
path_idx + 2 < paths.size() // has a next move
&& paths[path_idx+1].points.size() == 1 // is single extruded line
&& paths[path_idx+1].config != &storage.travel_config // next move is extrusion
&& paths[path_idx+2].config == &storage.travel_config // next next move is travel
&& !paths[path_idx+1].config->isTravelPath() // next move is extrusion
&& paths[path_idx+2].config->isTravelPath() // next next move is travel
&& shorterThen(path.points.back() - gcode.getPositionXY(), 2 * nozzle_size) // preceding extrusion is close by
&& shorterThen(paths[path_idx+1].points.back() - path.points.back(), 2 * nozzle_size) // extrusion move is small
&& shorterThen(paths[path_idx+2].points.back() - paths[path_idx+1].points.back(), 2 * nozzle_size) // consecutive extrusion is close by
)
{
sendPolygon(paths[path_idx+2].config->type, gcode.getPositionXY(), paths[path_idx+2].points.back(), paths[path_idx+2].getLineWidth());
sendLineTo(paths[path_idx+2].config->type, paths[path_idx+2].points.back(), paths[path_idx+2].getLineWidth());
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
path_idx += 2;
}
@@ -606,7 +758,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
sendLineTo(path.config->type, path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
@@ -638,38 +790,50 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
Point p1 = path.points[point_idx];
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.setZ(z + layer_thickness * length / totalLength);
sendLineTo(path.config->type, path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
path_idx--; // the last path_idx didnt spiralize, so it's not part of the current spiralize path
}
} // paths for this extruder /\ .
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);
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);
// 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.writeDelay(extruder_plan.extraTime);
}
extruder_plan.handleAllRemainingInserts(gcode);
}
} // extruder plans /\ .
gcode.updateTotalPrintTime();
if (liftHeadIfNeeded && extraTime > 0.0)
}
void GCodePlanner::overrideFanSpeeds(double speed)
{
for (ExtruderPlan& extruder_plan : extruder_plans)
{
gcode.writeComment("Small layer, adding delay");
if (last_extrusion_config)
{
bool extruder_switch_retract = false;// TODO: check whether we should do a retractoin_extruderSwitch; is the next path with a different extruder?
writeRetraction(gcode, extruder_switch_retract, last_extrusion_config->retraction_config);
}
gcode.setZ(gcode.getPositionZ() + MM2INT(3.0));
gcode.writeMove(gcode.getPositionXY(), storage.travel_config.getSpeed(), 0);
gcode.writeMove(gcode.getPositionXY() - Point(-MM2INT(20.0), 0), storage.travel_config.getSpeed(), 0); // TODO: is this safe?! wouldn't the head move into the sides then?!
gcode.writeDelay(extraTime);
extruder_plan.setFanSpeed(speed);
}
}
void GCodePlanner::completeConfigs()
{
storage.support_config.setLayerHeight(layer_thickness);
storage.support_roof_config.setLayerHeight(layer_thickness);
storage.support_skin_config.setLayerHeight(layer_thickness);
for (SliceMeshStorage& mesh : storage.meshes)
{
@@ -692,27 +856,65 @@ void GCodePlanner::completeConfigs()
void GCodePlanner::processInitialLayersSpeedup()
{
int initial_speedup_layers = storage.getSettingAsCount("speed_slowdown_layers");
if (static_cast<int>(layer_nr) < initial_speedup_layers)
if (layer_nr >= 0 && layer_nr < initial_speedup_layers)
{
double initial_layer_speed = storage.getSettingInMillimetersPerSecond("speed_layer_0");
storage.support_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
storage.support_roof_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
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 = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
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++)
{
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
//Infill speed (per combine part per mesh).
mesh.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
}
}
}
else if (static_cast<int>(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_roof_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();
@@ -726,46 +928,6 @@ void GCodePlanner::processInitialLayersSpeedup()
}
}
void GCodePlanner::writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after)
{
if (makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx_travel_after))
{
gcode.writeRetraction_extruderSwitch();
}
else
{
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
RetractionConfig* extrusion_retraction_config = nullptr;
for(int extrusion_path_idx = int(path_idx_travel_after) - 1; extrusion_path_idx >= 0; extrusion_path_idx--)
{ // backtrack to find the last extrusion path
if (paths[extrusion_path_idx].config != &storage.travel_config)
{
extrusion_retraction_config = paths[extrusion_path_idx].config->retraction_config;
break;
}
}
writeRetraction(gcode, false, extrusion_retraction_config);
}
}
void GCodePlanner::writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config)
{
if (extruder_switch_retract)
{
gcode.writeRetraction_extruderSwitch();
}
else
{
if (retraction_config)
{
gcode.writeRetraction(retraction_config);
}
else
{
gcode.writeRetraction(storage.travel_config.retraction_config);
}
}
}
bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx)
@@ -800,7 +962,8 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
{
return false;
}
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
ExtruderPlan& extruder_plan = extruder_plans[extruder_plan_idx];
std::vector<GCodePath>& paths = extruder_plan.paths;
GCodePath& path = paths[path_idx];
if (path_idx + 1 >= paths.size()
||
@@ -815,7 +978,7 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed
double extrude_speed = path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(); // travel speed
int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
@@ -888,10 +1051,10 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
{ // write normal extrude path:
for(unsigned int point_idx = 0; point_idx <= point_idx_before_start; point_idx++)
{
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
sendLineTo(path.config->type, path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM());
}
sendPolygon(path.config->type, gcode.getPositionXY(), start, path.getLineWidth());
sendLineTo(path.config->type, start, path.getLineWidth());
gcode.writeMove(start, extrude_speed, path.getExtrusionMM3perMM());
}
+274 -105
Ver Arquivo
@@ -1,17 +1,20 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef GCODE_PLANNER_H
#define GCODE_PLANNER_H
#include <vector>
#include "gcodeExport.h"
#include "comb.h"
#include "pathPlanning/Comb.h"
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
#include "GCodePathConfig.h"
#include "utils/optional.h"
namespace cura
{
@@ -48,21 +51,29 @@ struct NozzleTempInsert
}
};
class GCodePlanner; // forward declaration so that TimeMaterialEstimates can be a friend
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 GCodePlanner;
friend class ExtruderPlan; // cause there the naive estimates are calculated
private:
double extrude_time; //!< in seconds
double unretracted_travel_time; //!< in seconds
double retracted_travel_time; //!< in seconds
double material; //!< in mm^3
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)
@@ -70,6 +81,10 @@ public:
, material(material)
{
}
/*!
* Basic constructor initializing all estimates to zero.
*/
TimeMaterialEstimates()
: extrude_time(0.0)
, unretracted_travel_time(0.0)
@@ -77,7 +92,7 @@ public:
, material(0.0)
{
}
/*!
* Set all estimates to zero.
*/
@@ -88,12 +103,24 @@ public:
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;
@@ -102,7 +129,7 @@ public:
material += other.material;
return *this;
}
/*!
* \brief Subtracts the specified estimates from these estimates and returns
* the result.
@@ -113,7 +140,7 @@ public:
* \return These estimates with the specified estimates subtracted.
*/
TimeMaterialEstimates operator-(const TimeMaterialEstimates& other);
/*!
* \brief Subtracts the specified elements from these estimates.
*
@@ -124,29 +151,72 @@ public:
* \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:
@@ -154,20 +224,30 @@ public:
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();
}
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
* 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()
{
@@ -184,46 +264,74 @@ public:
}
};
class GCodePlanner; // forward declaration so that ExtruderPlan can be a friend
class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend
/*!
* An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train.
*
* It allows for temperature command inserts which can be inserted in between paths.
*/
class ExtruderPlan
{
public:
std::vector<GCodePath> paths;
std::list<NozzleTempInsert> inserts;
friend class GCodePlanner; // TODO: GCodePlanner still does a lot which should actually be handled in this class.
friend class LayerPlanBuffer; // TODO: LayerPlanBuffer handles paths directly
protected:
std::vector<GCodePath> paths; //!< The paths planned for this extruder
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
int extruder; //!< The extruder used for this paths in the current plan.
double required_temp;
TimeMaterialEstimates estimates;
ExtruderPlan(int extruder)
: extruder(extruder)
, required_temp(-1)
{
}
double required_temp; //!< The required temperature at the start of this extruder plan.
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.
public:
/*!
* Simple contructor.
*
* \warning Doesn't set the required temperature yet.
*
* \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);
/*!
* Add a new Insert, constructed with the given arguments
*
* \see NozzleTempInsert
*
* \param contructor_args The arguments for the constructor of an insert
*/
template<typename... Args>
void insertCommand(Args&&... contructor_args)
{
inserts.emplace_back(contructor_args...);
}
/*!
* Insert the inserts into gcode which should be inserted before @p path_idx
* Insert the inserts into gcode which should be inserted before \p path_idx
*
* \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleInserts(unsigned int& path_idx, GCodeExport& gcode)
{
{
while ( ! inserts.empty() && path_idx >= inserts.front().path_idx)
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
inserts.front().write(gcode);
inserts.pop_front();
}
}
/*!
* Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan
*
* Inserts temperature commands which should be inserted _after_ the last path.
* Also inserts all temperatures which should have been inserted earlier,
* but for which ExtruderPlan::handleInserts hasn't been called correctly.
*
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleAllRemainingInserts(GCodeExport& gcode)
{
@@ -235,21 +343,117 @@ public:
inserts.pop_front();
}
}
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*
* \param force_minimal_layer_time Whether we should apply speed changes and perhaps a head lift in order to meet the minimal layer time
*/
void processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time);
/*!
* Set the extrude speed factor. This is used for printing slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the extrusion move speed
*/
void setExtrudeSpeedFactor(double speedFactor);
/*!
* Get the extrude speed factor. This is used for printing slower than normal.
*
* \return The factor by which to alter the extrusion move speed
*/
double getExtrudeSpeedFactor();
/*!
* Set the travel speed factor. This is used for performing non-extrusion travel moves slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the non-extrusion move speed
*/
void setTravelSpeedFactor(double speedFactor);
/*!
* Get the travel speed factor. This is used for travelling slower than normal.
*
* Limited to at most 1.0
*
* \return The factor by which to alter the non-extrusion move speed
*/
double getTravelSpeedFactor();
/*!
* Get the fan speed computed for this extruder plan
*
* \warning assumes ExtruderPlan::processFanSpeedAndMinimalLayerTime has already been called
*
* \return The fan speed computed in processFanSpeedAndMinimalLayerTime
*/
double getFanSpeed();
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.
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 RetractionConfig& retraction_config; //!< The retraction settings for the extruder of this plan
double extrudeSpeedFactor; //!< The factor by which to alter the extrusion move speed
double travelSpeedFactor; //!< The factor by which to alter the non-extrusion move speed
double extraTime; //!< Extra waiting time at the and of this extruder plan, so that the filament can cool
double totalPrintTime; //!< The total naive time estimate for this extruder plan
double fan_speed; //!< The fan speed to be used during this extruder plan
/*!
* Set the fan speed to be used while printing this extruder plan
*
* \param fan_speed The speed for the fan
*/
void setFanSpeed(double fan_speed);
/*!
* Force the minimal layer time to hold by slowing down and lifting the head if required.
*
*/
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
};
class LayerPlanBuffer; // forward declaration to prevent circular dependency
/*!
* The GCodePlanner class stores multiple moves that are planned.
*
*
* It facilitates the combing to keep the head inside the print.
* It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
*
* A GCodePlanner is also knows as a 'layer plan'.
*
*/
class GCodePlanner : public NoCopy
{
friend class LayerPlanBuffer;
friend class GCodePlannerTest;
private:
SliceDataStorage& storage;
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
int layer_nr;
int layer_nr; //!< The layer number of this layer plan
int z;
@@ -259,23 +463,16 @@ private:
Point lastPosition;
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
SettingsBaseVirtual* last_planned_extruder_setting_base; //!< The setting base of the last planned extruder.
bool was_inside; //!< Whether the last planned (extrusion) move was inside a layer part
bool is_inside; //!< Whether the destination of the next planned travel move is inside a layer part
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
Comb* comb;
RetractionConfig* last_retraction_config;
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings;
double extrudeSpeedFactor;
double travelSpeedFactor;
double fan_speed;
double extraTime;
double totalPrintTime;
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
private:
/*!
@@ -304,19 +501,28 @@ private:
public:
/*!
*
* \param fan_speed_layer_time_settings_per_extruder The fan speed and layer time settings for each extruder.
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(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();
void overrideFanSpeeds(double speed);
/*!
* Get the settings base of the last extruder planned.
* \return the settings base of the last extruder planned.
*/
SettingsBaseVirtual* getLastPlannedExtruderTrainSettings();
private:
/*!
* Compute the boundary within which to comb, or to move into when performing a retraction.
* \param combing_mode Whether combing is enabled and full or within infill only.
* \return the comb_boundary_inside
*/
Polygons computeCombBoundaryInside();
Polygons computeCombBoundaryInside(CombingMode combing_mode);
public:
int getLayerNr()
@@ -330,19 +536,18 @@ public:
}
/*!
* send a polygon through the command socket from the previous point to the given point
* return whether the last position planned was inside the mesh (used in combing)
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, Point to, int line_width)
bool getIsInsideMesh()
{
if (CommandSocket::isInstantiated())
{
// we should send this travel as a non-retraction move
cura::Polygons pathPoly;
PolygonRef path = pathPoly.newPoly();
path.add(from);
path.add(to);
CommandSocket::getInstance()->sendPolygons(print_feature_type, layer_nr, pathPoly, line_width);
}
return was_inside;
}
/*!
* 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)
{
CommandSocket::sendLineTo(print_feature_type, to, line_width);
}
/*!
@@ -363,28 +568,7 @@ public:
return extruder_plans.back().extruder;
}
void setExtrudeSpeedFactor(double speedFactor)
{
this->extrudeSpeedFactor = speedFactor;
}
double getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void setTravelSpeedFactor(double speedFactor)
{
if (speedFactor < 1) speedFactor = 1.0;
this->travelSpeedFactor = speedFactor;
}
double getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void setFanSpeed(double _fan_speed)
{
fan_speed = _fan_speed;
}
/*!
* Add a travel path to a certain point, retract if needed and when avoiding boundary crossings:
@@ -452,25 +636,26 @@ public:
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \warning This function recomputes values which are already computed if you've called processFanSpeedAndMinimalLayerTime
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
/*!
* Write the planned paths to gcode
*
* \param gcode The gcode to write the planned paths to
*/
void writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int layerThickness);
void writeGCode(GCodeExport& gcode);
/*!
* Complete all GcodePathConfig s by
* - altering speed to conform to speed_layer_0
* 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();
@@ -507,22 +692,6 @@ public:
*/
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx_travel_after Index in GCodePlanner::paths to the travel move before which to do the retraction
*/
void writeRetraction(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx_travel_after);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the given retraction config.
* \param gcode The gcode to write the planned paths to
* \param extruder_switch_retract Whether to write an extruder switch retract
* \param retraction_config The config used.
*/
void writeRetraction(GCodeExport& gcode, bool extruder_switch_retract, RetractionConfig* retraction_config);
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*/
+82 -62
Ver Arquivo
@@ -6,7 +6,18 @@
namespace cura {
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between)
int Infill::computeScanSegmentIdx(int x, int line_width)
{
if (x < 0)
{
return (x + 1) / line_width - 1;
// - 1 because -1 belongs to scansegment -1
// + 1 because -line_width belongs to scansegment -1
}
return x / line_width;
}
void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
@@ -18,22 +29,21 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
generateGridInfill(result_lines);
break;
case EFillMethod::LINES:
generateLineInfill(result_lines, line_distance, fill_angle);
generateLineInfill(result_lines, line_distance, fill_angle, 0);
break;
case EFillMethod::CUBIC:
generateCubicInfill(result_lines);
break;
case EFillMethod::TETRAHEDRAL:
generateTetrahedralInfill(result_lines);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
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;
if (abs(infill_line_width - line_distance) < 10)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
}
else
{
generateConcentricInfill(*outline, result_polygons, line_distance);
}
generateConcentricInfill(*outline, result_polygons, line_distance);
break;
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
@@ -44,33 +54,11 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
}
}
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
{
while(outline.size() > 0)
{
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
PolygonRef r = outline[polyNr];
result.add(r);
}
Polygons next_outline;
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
}
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
PolygonRef r = outline[polyNr];
result.add(r);
}
result.add(outline);
outline = outline.offset(-inset_value);
}
}
@@ -78,18 +66,38 @@ void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int in
void Infill::generateGridInfill(Polygons& result)
{
generateLineInfill(result, line_distance * 2, fill_angle);
generateLineInfill(result, line_distance * 2, fill_angle + 90);
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 90, 0);
}
void Infill::generateCubicInfill(Polygons& result)
{
int64_t shift = one_over_sqrt_2 * z;
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle + 120, shift);
generateLineInfill(result, line_distance, fill_angle + 240, shift);
}
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
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);
}
void Infill::generateTriangleInfill(Polygons& result)
{
generateLineInfill(result, line_distance * 3, fill_angle);
generateLineInfill(result, line_distance * 3, fill_angle + 60);
generateLineInfill(result, line_distance * 3, fill_angle + 120);
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 60, 0);
generateLineInfill(result, line_distance, fill_angle + 120, 0);
}
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)
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)
{
@@ -113,7 +121,7 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
for(int64_t x = scanline_min_idx * line_distance + shift; x < boundary.max.X; x += line_distance)
{
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
@@ -129,19 +137,17 @@ void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix,
}
}
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t shift)
{
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
bool safe_outline_offset = false;
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
generateLinearBasedInfill(outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags, shift);
}
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
bool safe_outline_offset = true;
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
@@ -149,18 +155,18 @@ void Infill::generateZigZagInfill(Polygons& result, const int line_distance, con
if (connected_zigzags)
{
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
}
else
{
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
}
@@ -187,7 +193,7 @@ void Infill::generateZigZagInfill(Polygons& result, const int line_distance, con
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
*/
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift)
{
if (line_distance == 0)
{
@@ -197,18 +203,20 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
{
return;
}
int shift = extra_shift + this->shift;
Polygons outline;
if (outline_offset != 0)
{
PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
outline = in_outline.offset(outline_offset);
}
else
{
outline = in_outline;
}
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
outline = outline.offset(infill_overlap);
if (outline.size() == 0)
{
@@ -217,10 +225,19 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
outline.applyMatrix(rotation_matrix);
if (shift < 0)
{
shift = line_distance - (-shift) % line_distance;
}
else
{
shift = shift % line_distance;
}
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / line_distance;
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance);
int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
@@ -246,26 +263,29 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
continue;
}
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int scanline_idx0;
int scanline_idx1;
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
int direction = 1;
if (p0.X > p1.X)
if (p0.X < p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >)
}
else
{
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
direction = -1;
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >)
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
{
int x = scanline_idx * line_distance;
int x = scanline_idx * line_distance + shift;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!");
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
@@ -285,7 +305,7 @@ void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outli
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift);
}
}//namespace cura
+38 -22
Ver Arquivo
@@ -3,7 +3,7 @@
#define INFILL_H
#include "utils/polygon.h"
#include "settings.h"
#include "settings/settings.h"
// #include "ZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessor.h"
#include "infill/NoZigZagConnectorProcessor.h"
@@ -23,24 +23,27 @@ class Infill
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
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
int infill_line_width; //!< The line width of the infill lines to generate
int line_distance; //!< The distance between two infill lines / polygons
double infill_overlap; //!< the percentage (of infill_line_width) to overlap with the actual area within which to generate infill
int infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
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
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, bool remove_overlapping_perimeters, int infill_line_width, int line_distance, double infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
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)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
, remove_overlapping_perimeters(remove_overlapping_perimeters)
, infill_line_width(infill_line_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, z(z)
, shift(shift)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
@@ -50,12 +53,21 @@ public:
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \param result_lines (output) The resulting line segments (from linear infill types)
* \param in_between (optional output) The areas in between two concecutive concentric infill polygons
*/
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
void generate(Polygons& result_polygons, Polygons& result_lines);
private:
/*!
* Function which returns the scanline_idx for a given x coordinate
*
* For negative \p x this is different from simple division.
*
* \warning \p line_distance is assumed to be positive
*
* \param x the point to get the scansegment index for
* \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
@@ -64,22 +76,24 @@ private:
*/
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
/*!
* Generate dense concentric infill (100%)
*
* \param outline The actual outline of the area within which to generate infill
* \param result (output) The resulting polygons
* \param in_between (output) The areas in between each two consecutive polygons
* \param remove_overlapping_perimeters Whether to remove overlapping perimeter parts
*/
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool remove_overlapping_perimeters);
/*!
* Generate a rectangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a shifting triangular grid of infill lines, which combine with consecutive layers into a cubic pattern
* \param result (output) The resulting lines
*/
void generateCubicInfill(Polygons& result);
/*!
* Generate a double shifting square grid of infill lines, which combine with consecutive layers into a tetrahedral pattern
* \param result (output) The resulting lines
*/
void generateTetrahedralInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
@@ -94,8 +108,9 @@ private:
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
* \param total_shift total shift of the scanlines in the direction perpendicular to the fill_angle.
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t total_shift);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
@@ -106,8 +121,9 @@ private:
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t extra_shift);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
@@ -118,14 +134,14 @@ private:
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
void generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift);
/*!
*
-74
Ver Arquivo
@@ -1,74 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "utils/polygonUtils.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
{
PolygonUtils::offsetSafe(part->outline, - nozzle_width/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
else
{
PolygonUtils::offsetSafe(part->outline, - line_width_0/2, line_width_0, part->insets[0], avoidOverlappingPerimeters_0);
}
} else if (i == 1)
{
if (false) // line_width_0 < nozzle_width) // TODO: this is a quick fix for version 2.1 only; this line should not be in master
{
int offset_from_first_boundary_for_edge_of_outer_wall = -nozzle_width/2;
// ideally this /\ should be: nozzle_width/2 - line_width_0; however, factually, the nozzle will fill up part of the perimeter gaps
PolygonUtils::offsetSafe(part->insets[0], nozzle_width/2 - line_width_0 - line_width_x/2, offset_from_first_boundary_for_edge_of_outer_wall, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
else
{
PolygonUtils::offsetSafe(part->insets[0], -line_width_0/2 - line_width_x/2, -line_width_0/2, line_width_x, part->insets[1], &part->perimeterGaps, avoidOverlappingPerimeters);
}
} else
{
PolygonUtils::offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], nozzle_width, line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
-41
Ver Arquivo
@@ -1,41 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by \p line_width_0 instead of the first,
* which leads to better results for a smaller \p line_width_0 than \p line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
* \param nozzle_width The diameter of the hole in the nozzle
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+11 -11
Ver Arquivo
@@ -1,8 +1,8 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "layerPart.h"
#include "settings.h"
#include "Progress.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "utils/SVG.h" // debug output
@@ -26,15 +26,15 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
if (union_all_remove_holes)
{
for(unsigned int i=0; i<layer->polygonList.size(); i++)
for(unsigned int i=0; i<layer->polygons.size(); i++)
{
if (layer->polygonList[i].orientation())
layer->polygonList[i].reverse();
if (layer->polygons[i].orientation())
layer->polygons[i].reverse();
}
}
std::vector<PolygonsPart> result;
result = layer->polygonList.splitIntoParts(union_layers || union_all_remove_holes);
result = layer->polygons.splitIntoParts(union_layers || union_all_remove_holes);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.emplace_back();
@@ -42,14 +42,14 @@ void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool uni
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
storage.layers.push_back(SliceLayer());
storage.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
storage.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(storage.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
mesh.layers.push_back(SliceLayer());
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
}
}
+2 -2
Ver Arquivo
@@ -22,9 +22,9 @@ namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers = true, int layer_nr = -1);
void layerparts2HTML(SliceDataStorage& mesh, const char* filename, bool all_layers = true, int layer_nr = -1);
}//namespace cura
+145 -56
Ver Arquivo
@@ -16,36 +16,41 @@
#include "utils/string.h"
#include "FffProcessor.h"
#include "settingRegistry.h"
#include "settings/SettingRegistry.h"
#include "settings/SettingsToGV.h"
namespace cura
{
void print_usage()
{
cura::logError("\n");
cura::logError("usage:\n");
cura::logError("CuraEngine help\n");
cura::logError("\tShow this help message\n");
cura::logError("\n");
cura::logError("CuraEngine connect <host>[:<port>] [-j <settings.json>]\n");
cura::logError(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults\n");
cura::logError("\n");
cura::logError("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
cura::logError(" -v\n\tIncrease the verbose level (show log messages).\n");
cura::logError(" -p\n\tLog progress information.\n");
cura::logError(" -j\n\tLoad settings.json file to register all settings and their defaults.\n");
cura::logError(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
cura::logError(" -l <model_file>\n\tLoad an STL model. \n");
cura::logError(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
cura::logError(" -e\n\tAdd a new extruder train.\n");
cura::logError(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
cura::logError(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
cura::logError("\n");
cura::logError("The settings are appended to the last supplied object:\n");
cura::logError("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e [extruder train settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
cura::logError("\n");
logAlways("\n");
logAlways("usage:\n");
logAlways("CuraEngine help\n");
logAlways("\tShow this help message\n");
logAlways("\n");
logAlways("CuraEngine connect <host>[:<port>] [-j <settings.def.json>]\n");
logAlways(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
logAlways(" -j<settings.def.json>\n\tLoad settings.json file to register all settings and their defaults\n");
logAlways(" -v\n\tIncrease the verbose level (show log messages).\n");
logAlways("\n");
logAlways("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e<extruder_nr>] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
logAlways(" -v\n\tIncrease the verbose level (show log messages).\n");
logAlways(" -p\n\tLog progress information.\n");
logAlways(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n");
logAlways(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
logAlways(" -l <model_file>\n\tLoad an STL model. \n");
logAlways(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
logAlways(" -e<extruder_nr>\n\tSwitch setting focus to the extruder train with the given number.\n");
logAlways(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
logAlways(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
logAlways("\n");
logAlways("The settings are appended to the last supplied object:\n");
logAlways("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
logAlways("\n");
logAlways("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search paths delimited by a (semi-)colon.\n");
logAlways("\n");
}
//Signal handler for a "floating point exception", which can also be integer division by zero errors.
@@ -66,10 +71,10 @@ void print_call(int argc, char **argv)
void connect(int argc, char **argv)
{
CommandSocket::instantiate();
std::string ip;
int port = 49674;
// parse ip port
std::string ip_port(argv[2]);
if (ip_port.find(':') != std::string::npos)
{
@@ -77,7 +82,6 @@ void connect(int argc, char **argv)
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
for(int argn = 3; argn < argc; argn++)
{
char* str = argv[argn];
@@ -92,9 +96,10 @@ void connect(int argc, char **argv)
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], FffProcessor::getInstance()))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
cura::logError("Failed to load json file: %s\n", argv[argn]);
std::exit(1);
}
break;
default:
@@ -106,7 +111,8 @@ void connect(int argc, char **argv)
}
}
}
CommandSocket::instantiate();
CommandSocket::getInstance()->connect(ip, port);
}
@@ -120,7 +126,8 @@ void slice(int argc, char **argv)
int extruder_train_nr = 0;
SettingsBase* last_extruder_train = meshgroup->createExtruderTrain(0);
SettingsBase* last_extruder_train = nullptr;
// extruder defaults cannot be loaded yet cause no json has been parsed
SettingsBase* last_settings_object = FffProcessor::getInstance();
for(int argn = 2; argn < argc; argn++)
{
@@ -134,13 +141,15 @@ void slice(int argc, char **argv)
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->getExtruderTrain(extruder_nr)->setExtruderTrainDefaults(extruder_nr); // create new extruder train objects or use already existing ones
meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
}
meshgroup->finalize();
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
@@ -171,9 +180,10 @@ void slice(int argc, char **argv)
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn]))
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], last_settings_object))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
cura::logError("Failed to load json file: %s\n", argv[argn]);
std::exit(1);
}
break;
case 'e':
@@ -186,11 +196,17 @@ void slice(int argc, char **argv)
argn++;
log("Loading %s from disk...\n", argv[argn]);
// transformation = // TODO: get a transformation from somewhere
transformation = last_settings_object->getSettingAsPointMatrix("mesh_rotation_matrix"); // the transformation applied to a model when loaded
if (!last_extruder_train)
{
last_extruder_train = meshgroup->createExtruderTrain(0); // assume a json has already been provided on the command line
}
if (!loadMeshIntoMeshGroup(meshgroup, argv[argn], transformation, last_extruder_train))
{
logError("Failed to load model: %s\n", argv[argn]);
std::exit(1);
}
else
{
@@ -240,9 +256,10 @@ void slice(int argc, char **argv)
}
}
for (extruder_train_nr = 0; extruder_train_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_train_nr++)
int extruder_count = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
for (extruder_train_nr = 0; extruder_train_nr < extruder_count; extruder_train_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_train_nr)->setExtruderTrainDefaults(extruder_train_nr); // create new extruder train objects or use already existing ones
meshgroup->createExtruderTrain(extruder_train_nr); // create new extruder train objects or use already existing ones
}
@@ -287,23 +304,23 @@ int main(int argc, char **argv)
Progress::init();
logCopyright("\n");
logCopyright("Cura_SteamEngine version %s\n", VERSION);
logCopyright("Copyright (C) 2014 David Braam\n");
logCopyright("\n");
logCopyright("This program is free software: you can redistribute it and/or modify\n");
logCopyright("it under the terms of the GNU Affero General Public License as published by\n");
logCopyright("the Free Software Foundation, either version 3 of the License, or\n");
logCopyright("(at your option) any later version.\n");
logCopyright("\n");
logCopyright("This program is distributed in the hope that it will be useful,\n");
logCopyright("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
logCopyright("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
logCopyright("GNU Affero General Public License for more details.\n");
logCopyright("\n");
logCopyright("You should have received a copy of the GNU Affero General Public License\n");
logCopyright("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
std::cerr << std::boolalpha;
logAlways("\n");
logAlways("Cura_SteamEngine version %s\n", VERSION);
logAlways("Copyright (C) 2014 David Braam\n");
logAlways("\n");
logAlways("This program is free software: you can redistribute it and/or modify\n");
logAlways("it under the terms of the GNU Affero General Public License as published by\n");
logAlways("the Free Software Foundation, either version 3 of the License, or\n");
logAlways("(at your option) any later version.\n");
logAlways("\n");
logAlways("This program is distributed in the hope that it will be useful,\n");
logAlways("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
logAlways("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
logAlways("GNU Affero General Public License for more details.\n");
logAlways("\n");
logAlways("You should have received a copy of the GNU Affero General Public License\n");
logAlways("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
if (argc < 2)
@@ -325,6 +342,78 @@ int main(int argc, char **argv)
print_usage();
exit(0);
}
else if (stringcasecompare(argv[1], "analyse") == 0)
{ // CuraEngine analyse [json] [output.gv] [engine_settings] -[p|i|e|w]
// p = show parent-child relations
// i = show inheritance function
// e = show error functions
// w = show warning functions
// dot refl_ff.gv -Tpng > rafl_ff_dotted.png
// see meta/HOWTO.txt
bool parent_child_viz = false;
bool inherit_viz = false;
bool warning_viz = false;
bool error_viz = false;
bool global_only_viz = false;
if (argc >= 6)
{
char* str = argv[5];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'p':
parent_child_viz = true;
break;
case 'i':
inherit_viz = true;
break;
case 'e':
error_viz = true;
break;
case 'w':
warning_viz = true;
break;
case 'g':
global_only_viz = true;
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
break;
}
}
}
}
else
{
cura::log("\n");
cura::log("usage:\n");
cura::log("CuraEngine analyse <fdmPrinter.def.json> <output.gv> <engine_settings_list> -[p|i|e|w]\n");
cura::log("\tGenerate a grpah to visualize the setting inheritance structure.\n");
cura::log("\t<fdmPrinter.def.json>\n\tThe base seting definitions file.\n");
cura::log("\t<output.gv>\n\tThe output file.\n");
cura::log("\t<engine_settings_list>\n\tA text file with all setting keys used in the engine, separated by newlines.\n");
cura::log("\t-[p|i|e|w]\n\tOptions for what to include in the visualization\n");
cura::log("\t\tp\tVisualize the parent-child relationship.\n");
cura::log("\t\ti\tVisualize inheritance function relationships.\n");
cura::log("\t\te\tVisualize (max/min) error function relationships.\n");
cura::log("\t\tw\tVisualize (max/min) warning function relationships.\n");
cura::log("\n");
}
SettingsToGv gv_out(argv[3], argv[4], parent_child_viz, inherit_viz, error_viz, warning_viz, global_only_viz);
if (gv_out.generate(std::string(argv[2])))
{
cura::logError("Failed to analyse json file: %s\n", argv[2]);
}
exit(0);
}
else
{
cura::logError("Unknown command: %s\n", argv[1]);
+25 -18
Ver Arquivo
@@ -5,7 +5,11 @@ namespace cura
{
const int vertex_meld_distance = MM2INT(0.03);
static inline uint32_t pointHash(Point3& p)
/*!
* returns a hash for the location, but first divides by the vertex_meld_distance,
* so that any point within a box of vertex_meld_distance by vertex_meld_distance would get mapped to the same hash.
*/
static inline uint32_t pointHash(const Point3& p)
{
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
}
@@ -49,22 +53,32 @@ void Mesh::finish()
for(unsigned int i=0; i<faces.size(); i++)
{
MeshFace& face = faces[i];
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i); // faces are connected via the outside
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i);
// faces are connected via the outside
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i, face.vertex_index[2]);
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i, face.vertex_index[0]);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i, face.vertex_index[1]);
}
}
Point3 Mesh::min()
Point3 Mesh::min() const
{
return aabb.min;
}
Point3 Mesh::max()
Point3 Mesh::max() const
{
return aabb.max;
}
AABB3D Mesh::getAABB() const
{
return aabb;
}
void Mesh::expandXY(int64_t offset)
{
aabb.expandXY(offset);
}
int Mesh::findIndexOfVertex(Point3& v)
int Mesh::findIndexOfVertex(const Point3& v)
{
uint32_t hash = pointHash(v);
@@ -107,17 +121,13 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
*/
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const
{
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
for(int f : vertices[idx0].connected_faces) // search through all faces connected to the first vertex and find those that are also connected to the second
{
if (f == notFaceIdx)
{
for (int i = 0; i<3; i++) // find the vertex which is not idx0 or idx1
if (faces[f].vertex_index[i] != idx0 && faces[f].vertex_index[i] != idx1)
notFaceVertexIdx = faces[f].vertex_index[i];
continue;
}
if ( faces[f].vertex_index[0] == idx1 // && faces[f].vertex_index[1] == idx0 // next face should have the right direction!
@@ -127,12 +137,10 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
}
if (candidateFaces.size() == 0) { cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() == 0) { cura::logWarning("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() == 1) { return candidateFaces[0]; }
if (notFaceVertexIdx < 0) { cura::logError("Couldn't find third point on face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() % 2 == 0) cura::log("Warning! Edge with uneven number of faces connecting it!(%i)\n", candidateFaces.size()+1);
FPoint3 vn = vertices[idx1].p - vertices[idx0].p;
@@ -167,7 +175,6 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
if (angle == 0)
{
cura::log("Warning! Overlapping faces: face %i and face %i.\n", notFaceIdx, candidateFace);
std::cerr<< n.vSize() <<"; "<<n1.vSize()<<";"<<n0.vSize() <<std::endl;
}
if (angle < smallestAngle)
{
@@ -175,8 +182,8 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
bestIdx = candidateFace;
}
}
if (bestIdx < 0) cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
if (bestIdx < 0) cura::logWarning("Couldn't find face connected to face %i.\n", notFaceIdx);
return bestIdx;
}
}//namespace cura
}//namespace cura
+18 -8
Ver Arquivo
@@ -1,8 +1,8 @@
#ifndef MESH_H
#define MESH_H
#include "settings.h"
#include "utils/AABB.h"
#include "settings/settings.h"
#include "utils/AABB3D.h"
namespace cura
{
@@ -69,8 +69,10 @@ public:
void clear(); //!< clears all data
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
Point3 min() const; //!< min (in x,y and z) vertex of the bounding box
Point3 max() const; //!< max (in x,y and z) vertex of the bounding box
AABB3D getAABB() const; //!< Get the axis aligned bounding box
void expandXY(int64_t offset); //!< Register applied horizontal expansion in the AABB
/*!
* Offset the whole mesh (all vertices and the bounding box).
@@ -85,12 +87,20 @@ public:
}
private:
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
int findIndexOfVertex(const Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
/*!
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
* Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
*
* In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*
* \param idx0 the first vertex index
* \param idx1 the second vertex index
* \param notFaceIdx the index of a face which shouldn't be returned
* \param notFaceVertexIdx should be the third vertex of face \p notFaceIdx.
* \return the face index of a face sharing the edge from \p idx0 to \p idx1
*/
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const;
};
}//namespace cura
+51 -19
Ver Arquivo
@@ -6,15 +6,29 @@ namespace cura
void carveMultipleVolumes(std::vector<Slicer*> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int idx=0; idx < volumes.size(); idx++)
for (unsigned int volume_1_idx = 0; volume_1_idx < volumes.size(); volume_1_idx++)
{
for(unsigned int idx2=0; idx2<idx; idx2++)
Slicer& volume_1 = *volumes[volume_1_idx];
if (volume_1.mesh->getSettingBoolean("infill_mesh"))
{
for(unsigned int layerNr=0; layerNr < volumes[idx]->layers.size(); layerNr++)
continue;
}
for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++)
{
Slicer& volume_2 = *volumes[volume_2_idx];
if (volume_2.mesh->getSettingBoolean("infill_mesh"))
{
SlicerLayer& layer1 = volumes[idx]->layers[layerNr];
SlicerLayer& layer2 = volumes[idx2]->layers[layerNr];
layer1.polygonList = layer1.polygonList.difference(layer2.polygonList);
continue;
}
if (!volume_1.mesh->getAABB().hit(volume_2.mesh->getAABB()))
{
continue;
}
for (unsigned int layerNr = 0; layerNr < volume_1.layers.size(); layerNr++)
{
SlicerLayer& layer1 = volume_1.layers[layerNr];
SlicerLayer& layer2 = volume_2.layers[layerNr];
layer1.polygons = layer1.polygons.difference(layer2.polygons);
}
}
}
@@ -22,24 +36,42 @@ void carveMultipleVolumes(std::vector<Slicer*> &volumes)
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes, int overlap)
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
{
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0]->layers.size(); layerNr++)
if (volumes.size() < 2)
{
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
return;
}
int offset_to_merge_other_merged_volumes = 20;
for (Slicer* volume : volumes)
{
int overlap = volume->mesh->getSettingInMicrons("multiple_mesh_overlap");
if (volume->mesh->getSettingBoolean("infill_mesh")
|| overlap == 0)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
fullLayer = fullLayer.unionPolygons(layer1.polygonList.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
continue;
}
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
AABB3D aabb(volume->mesh->getAABB());
aabb.expandXY(overlap); // expand to account for the case where two models and their bounding boxes are adjacent along the X or Y-direction
for (unsigned int layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++)
{
SlicerLayer& layer1 = volumes[volIdx]->layers[layerNr];
layer1.polygonList = fullLayer.intersection(layer1.polygonList.offset(overlap / 2));
Polygons all_other_volumes;
for (Slicer* other_volume : volumes)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| !other_volume->mesh->getAABB().hit(aabb)
|| other_volume == volume
)
{
continue;
}
SlicerLayer& other_volume_layer = other_volume->layers[layer_nr];
all_other_volumes = all_other_volumes.unionPolygons(other_volume_layer.polygons.offset(offset_to_merge_other_merged_volumes));
}
SlicerLayer& volume_layer = volume->layers[layer_nr];
volume_layer.polygons = volume_layer.polygons.unionPolygons(all_other_volumes.intersection(volume_layer.polygons.offset(overlap / 2)));
}
}
}
+1 -1
Ver Arquivo
@@ -13,7 +13,7 @@ void carveMultipleVolumes(std::vector<Slicer*> &meshes);
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
* This generates some overlap in dual extrusion, for better bonding in touching parts.
*/
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes, int overlap);
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes);
}//namespace cura
+8 -6
Ver Arquivo
@@ -1,7 +1,7 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/SparsePointGridInclusive.h"
#include "utils/linearAlg2D.h"
#define INLINE static inline
@@ -152,8 +152,8 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
*/
void LineOrderOptimizer::optimize()
{
int gridSize = 5000; // the size of the cells in the hash grid.
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
int gridSize = 5000; // the size of the cells in the hash grid. TODO
SparsePointGridInclusive<unsigned int> line_bucket_grid(gridSize);
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
@@ -188,14 +188,16 @@ void LineOrderOptimizer::optimize()
int best_line_idx = -1;
float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line
for(unsigned int close_line_poly_idx : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
/// check if single-line-polygon is close to last point
for(unsigned int close_line_idx :
line_bucket_grid.getNearbyVals(prev_point, gridSize))
{
if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
if (picked[close_line_idx] || polygons[close_line_idx].size() < 1)
{
continue;
}
updateBestLine(close_line_poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
updateBestLine(close_line_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
}
if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
+1 -1
Ver Arquivo
@@ -4,7 +4,7 @@
#include <stdint.h>
#include "utils/polygon.h"
#include "settings.h"
#include "settings/settings.h"
namespace cura {
+336
Ver Arquivo
@@ -0,0 +1,336 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "Comb.h"
#include <algorithm>
#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"
namespace cura {
// boundary_outside is only computed when it's needed!
Polygons& Comb::getBoundaryOutside()
{
if (!boundary_outside)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
}
return *boundary_outside;
}
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;
}
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, 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 !!
{
}
Comb::~Comb()
{
if (boundary_outside)
{
delete boundary_outside;
}
if (outside_loc_to_line)
{
delete outside_loc_to_line;
}
}
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _startInside, bool _endInside, int64_t max_comb_distance_ignored, bool via_outside_makes_combing_fail, bool fail_on_unavoidable_obstacles)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
{
return true;
}
//Move start and end point inside the comb boundary
unsigned int start_inside_poly = NO_INDEX;
const bool startInside = moveInside(_startInside, startPoint, start_inside_poly);
unsigned int end_inside_poly = NO_INDEX;
const bool endInside = moveInside(_endInside, endPoint, end_inside_poly);
unsigned int start_part_boundary_poly_idx;
unsigned int end_part_boundary_poly_idx;
unsigned int start_part_idx = (start_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(start_inside_poly, &start_part_boundary_poly_idx);
unsigned int end_part_idx = (end_inside_poly == NO_INDEX)? NO_INDEX : partsView_inside.getPartContaining(end_inside_poly, &end_part_boundary_poly_idx);
if (startInside && endInside && start_part_idx == end_part_idx)
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
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);
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
// INSIDE | in_between | OUTSIDE | in_between | INSIDE
// ^crossing_1_in ^crossing_1_mid ^crossing_1_out ^crossing_2_out ^crossing_2_mid ^crossing_2_in
//
// when startPoint is inside crossing_1_in is of interest
// when it is in between inside and outside it is equal to crossing_1_mid
if (via_outside_makes_combing_fail)
{
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);
{ // 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)
{ // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside
avoid_other_parts_now = false;
}
if (avoid_other_parts_now)
{ // compute the crossing points when moving through air
Polygons& outside = getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
bool success = start_crossing.findOutside(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);
if (!success)
{
return false;
}
}
// generate the actual comb paths
if (startInside)
{
// 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);
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;
}
}
// throught air from boundary to boundary
if (avoid_other_parts_now)
{
combPaths.emplace_back();
combPaths.throughAir = true;
if ( vSize(start_crossing.in_or_mid - end_crossing.in_or_mid) < vSize(start_crossing.in_or_mid - start_crossing.out) + vSize(end_crossing.in_or_mid - end_crossing.out) )
{ // via outside is moving more over the in-between zone
combPaths.back().push_back(start_crossing.in_or_mid);
combPaths.back().push_back(end_crossing.in_or_mid);
}
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);
if (!combing_succeeded)
{
return false;
}
}
}
else
{ // 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().push_back(start_crossing.in_or_mid);
combPaths.back().push_back(end_crossing.in_or_mid);
}
if (endInside)
{
// boundary to end
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);
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;
}
}
return true;
}
}
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)
: 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
, dest_point(dest_point)
, dest_part_idx(dest_part_idx)
{
}
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)
{
return false;
}
else
{
inside_poly = cpp.poly_idx;
return true;
}
}
return false;
}
void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, const Point close_to)
{
if (dest_is_inside)
{ // in-case
// find the point on the start inside-polygon closest to the endpoint, but also kind of close to the start point
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);
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)
{
dest_crossing_poly = crossing_1_in_cp.poly;
in_or_mid = result;
}
else
{ // part is too small to be ensuring a point inside with the given distance
in_or_mid = dest_point; // just use the startPoint or endPoint itself
}
}
else
{
in_or_mid = dest_point; // mid-case
}
};
bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to, const bool fail_on_unavoidable_obstacles, Comb& comber)
{
out = in_or_mid;
if (dest_is_inside || outside.inside(in_or_mid, true)) // start in_between
{ // move outside
Point preferred_crossing_1_out = in_or_mid + normal(close_to - in_or_mid, comber.offset_from_inside_to_outside);
std::function<int(Point)> close_to_penalty_function([preferred_crossing_1_out](Point candidate){ return vSize2((candidate - preferred_crossing_1_out) / 2); });
std::optional<ClosestPolygonPoint> crossing_1_out_cpp = PolygonUtils::findClose(in_or_mid, outside, comber.getOutsideLocToLine(), close_to_penalty_function);
if (crossing_1_out_cpp)
{
out = PolygonUtils::moveOutside(*crossing_1_out_cpp, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
}
else
{
PolygonUtils::moveOutside(outside, out, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
}
}
int64_t in_out_dist2_1 = vSize2(out - in_or_mid);
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);
if (best)
{
in_or_mid = PolygonUtils::moveInside(best->first, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
out = PolygonUtils::moveOutside(best->second, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
}
if (fail_on_unavoidable_obstacles && vSize2(out - in_or_mid) > comber.max_crossing_dist2) // moveInside moved still too far
{
return false;
}
}
return true;
}
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)
{
ClosestPolygonPoint* best_in = nullptr;
ClosestPolygonPoint* best_out = nullptr;
int64_t best_detour_score = std::numeric_limits<int64_t>::max();
int64_t best_crossing_dist2;
std::vector<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> crossing_out_candidates = PolygonUtils::findClose(from, outside, comber.getOutsideLocToLine());
bool seen_close_enough_connection = false;
for (std::pair<ClosestPolygonPoint, ClosestPolygonPoint>& crossing_candidate : crossing_out_candidates)
{
int64_t crossing_dist2 = vSize2(crossing_candidate.first.location - crossing_candidate.second.location);
if (crossing_dist2 > comber.max_crossing_dist2 * 2)
{ // preliminary filtering
continue;
}
int64_t dist_to_start = vSize(crossing_candidate.second.location - estimated_start); // use outside location, so that the crossing direction is taken into account
int64_t dist_to_end = vSize(crossing_candidate.second.location - estimated_end);
int64_t detour_dist = dist_to_start + dist_to_end;
int64_t detour_score = crossing_dist2 + detour_dist * detour_dist / 1000; // prefer a closest connection over a detour
// The detour distance is generally large compared to the crossing distance.
// While the crossing is generally about 1mm across,
// the distance between an arbitrary point and the boundary may well be a couple of centimetres.
// So the crossing_dist2 is about 1.000.000 while the detour_dist_2 is in the order of 400.000.000
// In the end we just want to choose between two points which have the _same_ crossing distance, modulo rounding error.
if ((!seen_close_enough_connection && detour_score < best_detour_score) // keep the best as long as we havent seen one close enough (so that we may walk along the polygon to find a closer connection from it in the code below)
|| (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2) // make the one which is close enough the best as soon as we see one close enough
|| (seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2 && detour_score < best_detour_score)) // update to keep the best crossing which is close enough already
{
if (!seen_close_enough_connection && crossing_dist2 <= comber.max_crossing_dist2)
{
seen_close_enough_connection = true;
}
best_in = &crossing_candidate.first;
best_out = &crossing_candidate.second;
best_detour_score = detour_score;
best_crossing_dist2 = crossing_dist2;
}
}
if (best_detour_score == std::numeric_limits<int64_t>::max())
{ // i.e. if best_in == nullptr or if best_out == nullptr
return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>();
}
if (best_crossing_dist2 > comber.max_crossing_dist2)
{ // find closer point on line segments, rather than moving between vertices of the polygons only
PolygonUtils::walkToNearestSmallestConnection(*best_in, *best_out);
best_crossing_dist2 = vSize2(best_in->location - best_out->location);
if (best_crossing_dist2 > comber.max_crossing_dist2)
{
return std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>();
}
}
return std::make_shared<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>>(*best_in, *best_out);
}
}//namespace cura
+169
Ver Arquivo
@@ -0,0 +1,169 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_COMB_H
#define PATH_PLANNING_COMB_H
#include <memory> // shared_ptr
#include "../utils/polygon.h"
#include "../utils/SparsePointGridInclusive.h"
#include "../utils/polygonUtils.h"
#include "LinePolygonsCrossings.h"
#include "CombPath.h"
#include "CombPaths.h"
namespace cura
{
class SliceDataStorage;
/*!
* Class for generating a full combing actions from a travel move from a start point to an end point.
* A single Comb object is used for each layer.
*
* Comb::calc is the main function of this class.
*
* Typical output: A combing path to the boundary of the polygon + a move through air avoiding other parts in the layer + a combing path from the boundary of the ending polygon to the end point.
* Each of these three is a CombPath; the first and last are within Comb::boundary_inside while the middle is outside of Comb::boundary_outside.
* Between these there is a little gap where the nozzle crosses the boundary of an object approximately perpendicular to its boundary.
*
* As an optimization, the combing paths inside are calculated on specifically those PolygonsParts within which to comb, while the coundary_outside isn't split into outside parts,
* because generally there is only one outside part; encapsulated holes occur less often.
*/
class Comb
{
friend class LinePolygonsCrossings;
private:
/*!
* A crossing from the inside boundary to the outside boundary.
*
* 'dest' is either the startPoint or the endpoint of a whole combing move.
*/
class Crossing
{
public:
bool dest_is_inside; //!< Whether the startPoint or endPoint is inside the inside boundary
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)
/*!
* Simple constructor
*
* \param dest_point Either the eventual startPoint or the eventual endPoint of this combing move.
* \param dest_is_inside Whether the startPoint or endPoint is inside the inside boundary.
* \param dest_part_idx The index into Comb:partsView_inside of the part in which the \p dest_point is.
* \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);
/*!
* Find the not-outside location (Combing::in_or_mid) of the crossing between to the outside boundary
*
* \param partsView_inside Structured indices onto Comb::boundary_inside which shows which polygons belong to which part.
* \param close_to[in] Try to get a crossing close to this point
*/
void findCrossingInOrMid(const PartsView& partsView_inside, const Point close_to);
/*!
* Find the outside location (Combing::out)
*
* \param outside The outside boundary polygons
* \param close_to A point to get closer to when there are multiple candidates on the outside boundary which are almost equally close to the Crossing::in_or_mid
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \param comber[in] The combing calculator which has references to the offsets and boundaries to use in combing.
*/
bool findOutside(const Polygons& outside, const Point close_to, const bool fail_on_unavoidable_obstacles, Comb& comber);
private:
const Point dest_point; //!< Either the eventual startPoint or the eventual endPoint of this combing move
unsigned int dest_part_idx; //!< The index into Comb:partsView_inside of the part in which the \p dest_point is.
/*!
* Find the best crossing from some inside polygon to the outside boundary.
*
* The detour from \p estimated_start to \p estimated_end is minimized.
*
* \param outside The outside boundary polygons
* \param from From which inside boundary the crossing to the outside starts or ends
* \param estimated_start The one point to which to stay close when evaluating crossings which cross about the same distance
* \param estimated_end The other point to which to stay close when evaluating crossings which cross about the same distance
* \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);
};
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)
const int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
const int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
const int64_t offset_from_inside_to_outside; //!< The sum of the offsets for the inside and outside boundary Comb::offset_from_outlines and Comb::offset_from_outlines_outside
const int64_t max_crossing_dist2; //!< The maximal distance by which to cross the in_between area between inside and outside
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
const bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
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.
/*!
* 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();
/*!
* Move the startPoint or endPoint inside when it should be inside
* \param is_inside[in] Whether the \p dest_point should be inside
* \param dest_point[in,out] The point to move
* \param start_inside_poly[out] The polygon in which the point has been moved
* \return Whether we have moved the point inside
*/
bool moveInside(bool is_inside, Point& dest_point, unsigned int& start_inside_poly);
public:
/*!
* Initializes the combing areas for every mesh in the layer (not support)
* \param storage Where the layer polygon data is stored
* \param layer_nr The number of the layer for which to generate the combing areas.
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
* \param offset_from_outlines The offset from the outline polygon, to create the combing boundary in case there is no second wall.
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
*/
Comb(SliceDataStorage& storage, int layer_nr, 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
*
* \param startPoint Where to start moving from
* \param endPoint Where to move to
* \param combPoints Output parameter: The points along the combing path, excluding the \p startPoint (?) and \p endPoint
* \param startInside Whether we want to start inside the comb boundary
* \param endInside Whether we want to end up inside the comb boundary
* \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);
};
}//namespace cura
#endif//PATH_PLANNING_COMB_H
+17
Ver Arquivo
@@ -0,0 +1,17 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_COMB_PATH_H
#define PATH_PLANNING_COMB_PATH_H
#include "../utils/intpoint.h"
namespace cura
{
struct CombPath : public std::vector<Point> //!< A single path either inside or outise the parts
{
bool cross_boundary = false; //!< Whether the path crosses a boundary.
};
}//namespace cura
#endif//PATH_PLANNING_COMB_PATH_H
+17
Ver Arquivo
@@ -0,0 +1,17 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef PATH_PLANNING_COMB_PATHS_H
#define PATH_PLANNING_COMB_PATHS_H
#include "CombPath.h"
namespace cura
{
struct CombPaths : public std::vector<CombPath> //!< A list of paths alternating between inside a part and outside a part
{
bool throughAir = false; //!< Whether the path is one which moves through air.
};
}//namespace cura
#endif//PATH_PLANNING_COMB_PATHS_H
+227
Ver Arquivo
@@ -0,0 +1,227 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "LinePolygonsCrossings.h"
#include <algorithm>
#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"
namespace cura {
bool LinePolygonsCrossings::calcScanlineCrossings(bool fail_on_unavoidable_obstacles)
{
min_crossing_idx = NO_INDEX;
max_crossing_idx = NO_INDEX;
for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
{
PolyCrossings minMax(poly_idx);
PolygonRef poly = boundary[poly_idx];
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = transformation_matrix.apply(poly[point_idx]);
if ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
{ // if line segment crosses the line through the transformed start and end point (aka scanline)
if (p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
{
p0 = p1;
continue;
}
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); // intersection point between line segment and the scanline
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
{
if (!((p1.Y == transformed_startPoint.Y && p1.Y < p0.Y) || (p0.Y == transformed_startPoint.Y && p0.Y < p1.Y)))
{ // perform edge case only for line segments on and below the scanline, not for line segments on and above.
// \/ will be no crossings and /\ two, but most importantly | will be one crossing.
minMax.n_crossings++;
}
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
{
minMax.min.x = x;
minMax.min.point_idx = point_idx;
}
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
{
minMax.max.x = x;
minMax.max.point_idx = point_idx;
}
}
}
p0 = p1;
}
if (fail_on_unavoidable_obstacles && minMax.n_crossings % 2 == 1)
{ // if start area and end area are not the same
return false;
}
else if (minMax.min.point_idx != NO_INDEX) // then always also max.point_idx != NO_INDEX
{ // if this polygon crossed the scanline
if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
crossings.push_back(minMax);
}
}
return true;
}
bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
{
Point diff = endPoint - startPoint;
transformation_matrix = PointMatrix(diff);
transformed_startPoint = transformation_matrix.apply(startPoint);
transformed_endPoint = transformation_matrix.apply(endPoint);
for(PolygonRef poly : boundary)
{
Point p0 = transformation_matrix.apply(poly.back());
for(Point p1_ : poly)
{
Point p1 = transformation_matrix.apply(p1_);
// when the boundary just touches the line don't disambiguate between the boundary moving on to actually cross the line
// and the boundary bouncing back, resulting in not a real collision - to keep the algorithm simple.
//
// disregard overlapping line segments; probably the next or previous line segment is not overlapping, but will give a collision
// when the boundary line segment fully overlaps with the line segment this edge case is not viewed as a collision
if (p1.Y != p0.Y && ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y)))
{
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x > transformed_startPoint.X && x < transformed_endPoint.X)
{
return true;
}
}
p0 = p1;
}
}
return false;
}
bool LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
{
//We're not crossing any boundaries. So skip the comb generation.
combPath.push_back(startPoint);
combPath.push_back(endPoint);
return true;
}
bool success = calcScanlineCrossings(fail_on_unavoidable_obstacles);
if (!success)
{
return false;
}
CombPath basicPath;
getBasicCombingPath(basicPath);
optimizePath(basicPath, combPath);
// combPath = basicPath; // uncomment to disable comb path optimization
return true;
}
void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
{
for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
; crossing != nullptr
; crossing = getNextPolygonAlongScanline(crossing->max.x))
{
getBasicCombingPath(*crossing, combPath);
}
combPath.push_back(endPoint);
}
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
< poly.size() / 2 )
{ // follow the path in the same direction as the winding order of the boundary polygon
for(unsigned int point_idx = polyCrossings.min.point_idx
; point_idx != polyCrossings.max.point_idx
; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
else
{ // follow the path in the opposite direction of the winding order of the boundary polygon
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
}
LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
{
PolyCrossings* ret = nullptr;
for(PolyCrossings& crossing : crossings)
{
if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
{
ret = &crossing;
}
}
return ret;
}
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
optimized_comb_path.push_back(startPoint);
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
{
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
{
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
{
comb_path.cross_boundary = true;
}
optimized_comb_path.push_back(comb_path[point_idx - 1]);
}
else
{
// : dont add the newest point
// 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]))
{
break;
}
else
{
optimized_comb_path.pop_back();
}
}
}
}
optimized_comb_path.push_back(comb_path.back());
return true;
}
}//namespace cura
@@ -1,20 +1,13 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef COMB_H
#define COMB_H
#ifndef PATH_PLANNING_LINE_POLYGONS_CROSSINGS_H
#define PATH_PLANNING_LINE_POLYGONS_CROSSINGS_H
#include "utils/polygon.h"
#include "../utils/polygon.h"
#include "CombPath.h"
namespace cura
{
struct CombPath : public std::vector<Point> //!< A single path either inside or outise the parts
{
bool throughAir = false; //!< Whether the path is one which moves through air.
bool cross_boundary = false; //!< Whether the path crosses a boundary.
};
struct CombPaths : public std::vector<CombPath> //!< A list of paths alternating between inside a part and outside a part
{
};
/*!
* Class for generating a combing move action from point a to point b and avoiding collision with other parts when moving through air.
@@ -60,6 +53,7 @@ private:
unsigned int poly_idx; //!< The index of the polygon which crosses the scanline
Crossing min; //!< The point where the polygon first crosses the scanline.
Crossing max; //!< The point where the polygon last crosses the scanline.
int n_crossings; //!< The number of times the polygon crossed the scanline.
/*!
* Create a PolyCrossings with minimal initialization. PolyCrossings::min and PolyCrossings::max are not yet computed.
* \param poly_idx The index of the polygon in LinePolygonsCrossings::boundary
@@ -67,6 +61,7 @@ private:
PolyCrossings(unsigned int poly_idx)
: poly_idx(poly_idx)
, min(INT64_MAX, NO_INDEX), max(INT64_MIN, NO_INDEX)
, n_crossings(0)
{
}
};
@@ -91,8 +86,8 @@ private:
int64_t dist_to_move_boundary_point_outside; //!< The distance used to move outside or inside so that a boundary point doesn't intersect with the boundary anymore. Neccesary due to computational rounding problems. Use negative value for insicde combing.
PointMatrix transformation_matrix; //!< The transformation which rotates everything such that the scanline is aligned with the x-axis.
Point transformed_startPoint; //!< The LinePolygonsCrossings::startPoint as transformed by Comb::transformation_matrix
Point transformed_endPoint; //!< The LinePolygonsCrossings::endPoint as transformed by Comb::transformation_matrix
Point transformed_startPoint; //!< The LinePolygonsCrossings::startPoint as transformed by Comb::transformation_matrix such that it has (roughly) the same Y as transformed_endPoint
Point transformed_endPoint; //!< The LinePolygonsCrossings::endPoint as transformed by Comb::transformation_matrix such that it has (roughly) the same Y as transformed_startPoint
/*!
@@ -105,15 +100,19 @@ private:
/*!
* Calculate Comb::crossings, Comb::min_crossing_idx and Comb::max_crossing_idx.
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \return Whether combing succeeded, i.e. when fail_on_unavoidable_obstacles: we didn't cross any gaps/other parts
*/
void calcScanlineCrossings();
bool calcScanlineCrossings(bool fail_on_unavoidable_obstacles);
/*!
* Get the basic combing path and optimize it.
*
* \param combPath Output parameter: the points along the combing path.
* \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
*/
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
bool getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles);
/*!
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
@@ -177,81 +176,16 @@ public:
* \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 void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
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)
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
return linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
};
};
class SliceDataStorage;
/*!
* Class for generating a full combing actions from a travel move from a start point to an end point.
* A single Comb object is used for each layer.
*
* Comb::calc is the main function of this class.
*
* Typical output: A combing path to the boundary of the polygon + a move through air avoiding other parts in the layer + a combing path from the boundary of the ending polygon to the end point.
* Each of these three is a CombPath; the first and last are within Comb::boundary_inside while the middle is outside of Comb::boundary_outside.
* Between these there is a little gap where the nozzle crosses the boundary of an object approximately perpendicular to its boundary.
*
* As an optimization, the combing paths inside are calculated on specifically those PolygonsParts within which to comb, while the coundary_outside isn't split into outside parts,
* because generally there is only one outside part; encapsulated holes occur less often.
*/
class Comb
{
friend class LinePolygonsCrossings;
private:
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
Polygons& boundary_inside; //!< The boundary within which to comb.
Polygons* boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
/*!
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
*/
Polygons* getBoundaryOutside();
public:
/*!
* Initializes the combing areas for every mesh in the layer (not support)
* \param storage Where the layer polygon data is stored
* \param layer_nr The number of the layer for which to generate the combing areas.
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
* \param offset_from_outlines The offset from the outline polygon, to create the combing boundary in case there is no second wall.
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
*/
Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~Comb();
/*!
* Calculate the comb paths (if any) - one for each polygon combed alternated with travel paths
*
* \param startPoint Where to start moving from
* \param endPoint Where to move to
* \param combPoints Output parameter: The points along the combing path, excluding the \p startPoint (?) and \p endPoint
* \param startInside Whether we want to start inside the comb boundary
* \param endInside Whether we want to end up inside the comb boundary
* \return Whether combing has succeeded; otherwise a retraction is needed.
*/
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
};
}//namespace cura
#endif//COMB_H
#endif//PATH_PLANNING_LINE_POLYGONS_CROSSINGS_H
+11 -14
Ver Arquivo
@@ -1,30 +1,28 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "Progress.h"
#include "commandSocket.h"
#include "utils/gettime.h"
#include "../commandSocket.h"
#include "../utils/gettime.h"
namespace cura {
double Progress::times [] =
{
0.0,
5.269,
1.533,
22.953,
51.009,
48.858,
154.62,
0.1
0.0, // START = 0,
5.269, // SLICING = 1,
1.533, // PARTS = 2,
71.811, // INSET_SKIN = 3
51.009, // SUPPORT = 4,
154.62, // EXPORT = 5,
0.1 // FINISH = 6
};
std::string Progress::names [] =
{
"start",
"slice",
"layerparts",
"inset",
"inset+skin",
"support",
"skin",
"export",
"process"
};
@@ -39,9 +37,8 @@ const Progress::Stage Progress::stages[] =
Progress::Stage::START,
Progress::Stage::SLICING,
Progress::Stage::PARTS,
Progress::Stage::INSET,
Progress::Stage::INSET_SKIN,
Progress::Stage::SUPPORT,
Progress::Stage::SKIN,
Progress::Stage::EXPORT,
Progress::Stage::FINISH
};
+6 -7
Ver Arquivo
@@ -4,14 +4,14 @@
#include <string>
#include "utils/logoutput.h"
#include "utils/gettime.h"
#include "../utils/logoutput.h"
#include "../utils/gettime.h"
namespace cura {
class CommandSocket;
#define N_PROGRESS_STAGES 8
#define N_PROGRESS_STAGES 7
/*!
* Class for handling the progress bar and the progress logging.
@@ -30,11 +30,10 @@ public:
START = 0,
SLICING = 1,
PARTS = 2,
INSET = 3,
INSET_SKIN = 3,
SUPPORT = 4,
SKIN = 5,
EXPORT = 6,
FINISH = 7
EXPORT = 5,
FINISH = 6
};
private:
static double times [N_PROGRESS_STAGES]; //!< Time estimates per stage
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_ESTIMATOR_H
#define PROGRESS_PROGRESS_ESTIMATOR_H
#include <vector>
namespace cura
{
/*
* ProgressEstimator is a finger-tree with ProgressEstimatorLinear as leaves.
*
* Each (non-leaf) node consists of a ProgressStageEstimator which consists of several stages.
*
* The structure of this tree is an oversimplification of the call graph of CuraEngine.
*
*/
class ProgressEstimator
{
public:
virtual double progress(int current_step) = 0;
virtual ~ProgressEstimator()
{
}
};
} // namespace cura
#endif // PROGRESS_PROGRESS_ESTIMATOR_H
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
#define PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
#include <vector>
#include "ProgressEstimator.h"
namespace cura
{
class ProgressEstimatorLinear : public ProgressEstimator
{
unsigned int total_steps;
public:
ProgressEstimatorLinear(unsigned int total_steps)
: total_steps(total_steps)
{
}
double progress(int current_step)
{
return double(current_step) / double(total_steps);
}
};
} // namespace cura
#endif // PROGRESS_PROGRESS_ESTIMATOR_LINEAR_H
+52
Ver Arquivo
@@ -0,0 +1,52 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ProgressStageEstimator.h"
namespace cura
{
ProgressStageEstimator::ProgressStageEstimator(std::vector< double >& relative_time_estimates)
: total_estimated_time(0)
, accumulated_estimate(0)
, current_stage_idx(-1)
{
stages.reserve(relative_time_estimates.size());
for (double relative_estimated_time : relative_time_estimates)
{
stages.emplace_back(relative_estimated_time);
total_estimated_time += relative_estimated_time;
}
}
ProgressStageEstimator::~ProgressStageEstimator()
{
for (ProgressStage& stage : stages)
{
delete stage.stage;
}
}
double ProgressStageEstimator::progress(int current_step)
{
ProgressStage& current_stage = stages[current_stage_idx];
return (accumulated_estimate + current_stage.stage->progress(current_step) * current_stage.relative_estimated_time) / total_estimated_time;
}
void ProgressStageEstimator::nextStage(ProgressEstimator* stage)
{
if (current_stage_idx >= int(stages.size()) - 1)
{
return;
}
if (current_stage_idx >= 0)
{
ProgressStage& current_stage = stages[current_stage_idx];
accumulated_estimate += current_stage.relative_estimated_time;
}
current_stage_idx++;
stages[current_stage_idx].stage = stage;
}
} // namespace cura
+54
Ver Arquivo
@@ -0,0 +1,54 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
#define PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
#include <vector>
#include "ProgressEstimator.h"
namespace cura
{
/*!
* A staged progress estimator which estimates each stage to have different times.
*/
class ProgressStageEstimator : public ProgressEstimator
{
struct ProgressStage
{
double relative_estimated_time;
ProgressEstimator* stage;
ProgressStage(double relative_estimated_time)
: relative_estimated_time(relative_estimated_time)
, stage(nullptr)
{
}
};
protected:
std::vector<ProgressStage> stages;
double total_estimated_time;
private:
double accumulated_estimate;
int current_stage_idx;
public:
ProgressStageEstimator(std::vector<double>& relative_time_estimates);
double progress(int current_step);
/*!
*
* \warning This class is responsible for deleting the \p stage
*
*/
void nextStage(ProgressEstimator* stage);
~ProgressStageEstimator();
};
} // namespace cura
#endif // PROGRESS_PROGRESS_STAGE_ESTIMATOR_H
+67 -8
Ver Arquivo
@@ -1,23 +1,82 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <clipper/clipper.hpp>
#include "utils/math.h"
#include "raft.h"
#include "support.h"
namespace cura {
void generateRaft(SliceDataStorage& storage, int distance)
void Raft::generate(SliceDataStorage& storage, int distance)
{
assert(storage.raftOutline.size() == 0 && "Raft polygon isn't generated yet, so should be empty!");
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
const int shield_line_width = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"))->getSettingInMicrons("skirt_brim_line_width");
if (storage.draft_protection_shield.size() > 0)
{
storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance));
Polygons draft_shield_raft = storage.draft_protection_shield.offset(shield_line_width) // start half a line width outside shield
.difference(storage.draft_protection_shield.offset(-distance - shield_line_width / 2, ClipperLib::jtRound)); // end distance inside shield
storage.raftOutline = storage.raftOutline.unionPolygons(draft_shield_raft);
}
else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
{
storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance));
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
const Polygons& ooze_shield = storage.oozeShield[0];
Polygons ooze_shield_raft = ooze_shield.offset(shield_line_width) // start half a line width outside shield
.difference(ooze_shield.offset(-distance - shield_line_width / 2, ClipperLib::jtRound)); // end distance inside shield
storage.raftOutline = storage.raftOutline.unionPolygons(ooze_shield_raft);
}
storage.raftOutline = storage.raftOutline.offset(1000).offset(-1000); // remove small holes
}
int Raft::getTotalThickness(const SliceDataStorage& storage)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
return train.getSettingInMicrons("raft_base_thickness")
+ train.getSettingInMicrons("raft_interface_thickness")
+ train.getSettingAsCount("raft_surface_layers") * train.getSettingInMicrons("raft_surface_thickness");
}
int Raft::getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
{
return 0;
}
const int64_t airgap = std::max(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");
const int64_t z_diff_raft_to_bottom_of_layer_1 = std::max(int64_t(0), airgap + layer_height_0 - layer_0_overlap);
return z_diff_raft_to_bottom_of_layer_1;
}
int Raft::getFillerLayerCount(const SliceDataStorage& storage)
{
const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height");
const unsigned int filler_layer_count = round_divide(getZdiffBetweenRaftAndLayer1(storage), normal_layer_height);
return filler_layer_count;
}
int Raft::getFillerLayerHeight(const SliceDataStorage& storage)
{
if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
{
const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height");
return normal_layer_height;
}
const unsigned int filler_layer_height = round_divide(getZdiffBetweenRaftAndLayer1(storage), getFillerLayerCount(storage));
return filler_layer_height;
}
int Raft::getTotalExtraLayers(const SliceDataStorage& storage)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
return 2 + train.getSettingAsCount("raft_surface_layers") + getFillerLayerCount(storage);
}
}//namespace cura
+37 -1
Ver Arquivo
@@ -6,7 +6,43 @@
namespace cura {
void generateRaft(SliceDataStorage& storage, int distance);
class Raft
{
public:
static void generate(SliceDataStorage& storage, int distance);
/*!
* Get the height difference between the raft and the bottom of layer 1.
*
* This is used for the filler layers because they don't use the layer_0_z_overlap
*/
static int getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage);
/*!
* Get the amount of layers to fill the airgap and initial layer with helper parts (support, prime tower, etc.)
*
* The initial layer gets a separate filler layer because we don't want to apply the layer_0_z_overlap to it.
*/
static int getFillerLayerCount(const SliceDataStorage& storage);
/*!
* Get the layer height of the filler layers in between the raft and layer 1
*/
static int getFillerLayerHeight(const SliceDataStorage& storage);
/*!
* Get the total thickness of the raft (without airgap)
*/
static int getTotalThickness(const SliceDataStorage& storage);
/*!
* Get the total amount of extra layers below zero because there is a raft.
*
* This includes the filler layers which are introduced in the air gap.
*/
static int getTotalExtraLayers(const SliceDataStorage& storage);
};
}//namespace cura
-349
Ver Arquivo
@@ -1,349 +0,0 @@
#include "settingRegistry.h"
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <algorithm> // find_if
#include "utils/logoutput.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "utils/logoutput.h"
namespace cura
{
SettingRegistry SettingRegistry::instance; // define settingRegistry
std::string SettingRegistry::toString(rapidjson::Type type)
{
switch (type)
{
case rapidjson::Type::kNullType: return "null";
case rapidjson::Type::kFalseType: return "false";
case rapidjson::Type::kTrueType: return "true";
case rapidjson::Type::kObjectType: return "object";
case rapidjson::Type::kArrayType: return "array";
case rapidjson::Type::kStringType: return "string";
case rapidjson::Type::kNumberType: return "number";
default: return "Unknown";
}
}
SettingContainer::SettingContainer(std::string key, std::string label)
: key(key)
, label(label)
{
}
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
{
children.emplace_back(key, label, nullptr);
return &children.back();
}
SettingConfig::SettingConfig(std::string key, std::string label, SettingContainer* parent)
: SettingContainer(key, label)
, parent(parent)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
void SettingContainer::debugOutputAllSettings()
{
std::cerr << "CATEGORY: " << key << std::endl;
for (SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
bool SettingRegistry::settingExists(std::string key) const
{
return settings.find(key) != settings.end();
}
SettingConfig* SettingRegistry::getSettingConfig(std::string key)
{
auto it = settings.find(key);
if (it == settings.end())
return nullptr;
return it->second;
}
SettingContainer* SettingRegistry::getCategory(std::string key)
{
for (SettingContainer& cat : categories)
if (cat.getKey().compare(key) == 0)
return &cat;
return nullptr;
}
SettingRegistry::SettingRegistry()
{
}
bool SettingRegistry::settingsLoaded()
{
return settings.size() > 0;
}
int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
cura::logError("Couldn't open JSON file.\n");
return 1;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return 2;
}
return 0;
}
int SettingRegistry::loadJSONsettings(std::string filename)
{
rapidjson::Document json_document;
int err = loadJSON(filename, json_document);
if (err) { return err; }
if (json_document.HasMember("inherits"))
{
std::string filename_copy = std::string(filename.c_str()); // copy the string because dirname(.) changes the input string!!!
char* filename_cstr = (char*)filename_copy.c_str();
int err = loadJSONsettings(std::string(dirname(filename_cstr)) + std::string("/") + json_document["inherits"].GetString());
if (err) { return err; }
return loadJSONsettingsFromDoc(json_document, false);
}
else
{
return loadJSONsettingsFromDoc(json_document, true);
}
}
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates)
{
if (!json_document.IsObject())
{
cura::logError("JSON file is not an object.\n");
return 3;
}
if (json_document.HasMember("machine_extruder_trains"))
{
categories.emplace_back("machine_extruder_trains", "Extruder Trains Settings Objects");
SettingContainer* category_trains = &categories.back();
const rapidjson::Value& trains = json_document["machine_extruder_trains"];
if (trains.IsArray())
{
if (trains.Size() > 0 && trains[0].IsObject())
{
unsigned int idx = 0;
for (auto it = trains.Begin(); it != trains.End(); ++it)
{
SettingConfig* child = category_trains->addChild(std::to_string(idx), std::to_string(idx));
for (rapidjson::Value::ConstMemberIterator setting_iterator = it->MemberBegin(); setting_iterator != it->MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(child, setting_iterator, warn_duplicates, false);
}
idx++;
}
}
}
else
{
logError("Error: JSON machine_extruder_trains is not an array!\n");
}
}
if (json_document.HasMember("machine_settings"))
{
categories.emplace_back("machine_settings", "Machine Settings");
SettingContainer* category_machine_settings = &categories.back();
const rapidjson::Value& json_object_container = json_document["machine_settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category_machine_settings, setting_iterator, warn_duplicates);
}
}
if (json_document.HasMember("categories"))
{
for (rapidjson::Value::ConstMemberIterator category_iterator = json_document["categories"].MemberBegin(); category_iterator != json_document["categories"].MemberEnd(); ++category_iterator)
{
if (!category_iterator->value.IsObject())
{
continue;
}
if (!category_iterator->value.HasMember("settings") || !category_iterator->value["settings"].IsObject())
{
continue;
}
std::string cat_name = category_iterator->name.GetString();
std::list<SettingContainer>::iterator category_found = std::find_if(categories.begin(), categories.end(), [&cat_name](SettingContainer& cat) { return cat.getKey().compare(cat_name) == 0; });
if (category_found != categories.end())
{ // category is already present; add settings to category
SettingContainer* category = &*category_found;
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category, setting_iterator, warn_duplicates);
}
}
else
{
if (!category_iterator->value.HasMember("label") || !category_iterator->value["label"].IsString())
{
continue;
}
categories.emplace_back(cat_name, category_iterator->value["label"].GetString());
SettingContainer* category = &categories.back();
const rapidjson::Value& json_object_container = category_iterator->value["settings"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(category, setting_iterator, warn_duplicates);
}
}
}
}
if (json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
if (!conf) //Setting could not be found.
{
logWarning("Trying to override unknown setting %s.", setting.c_str());
continue;
}
_loadSettingValues(conf, override_iterator, false);
}
}
return 0;
}
void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
const rapidjson::Value& data = json_object_it->value;
if (data.HasMember("type") && data["type"].IsString() &&
(data["type"].GetString() == std::string("polygon") || data["type"].GetString() == std::string("polygons")))
{
logWarning("Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
/// When this setting has children, add those children to the parent setting.
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
}
}
return;
}
std::string label;
if (!json_object_it->value.HasMember("label") || !data["label"].IsString())
{
label = "N/A";
}
else
{
label = data["label"].GetString();
}
/// Create the new setting config object.
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
}
void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
const rapidjson::Value& data = json_object_it->value;
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (data.HasMember("default"))
{
const rapidjson::Value& dflt = data["default"];
if (dflt.IsString())
{
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
/// Register the setting in the settings map lookup.
if (warn_duplicates && settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
}
if (add_to_settings)
{
settings[config->getKey()] = config;
}
/// When this setting has children, add those children to this setting.
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(config, setting_iterator, warn_duplicates, add_to_settings);
}
}
}
}//namespace cura
-225
Ver Arquivo
@@ -1,225 +0,0 @@
#ifndef SETTING_REGISTRY_H
#define SETTING_REGISTRY_H
#include <vector>
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
// Forward declaration
class SettingConfig;
/*!
* Setting category.
* Filled from the fdmprinter.json file. Contains one or more children settings.
*/
class SettingContainer
{
friend class SettingConfig;
private:
std::string key;
std::string label;
std::list<SettingConfig> children;
public:
std::string getKey() const { return key; }
std::string getLabel() const { return label; }
SettingContainer(std::string key, std::string label);
SettingConfig* addChild(std::string key, std::string label);
/*!
* Get the \p idx th child.
*
* This is used to get a specific extruder train in Settingsbase::setExtruderTrainDefaults
*
* \param idx The index in the list of children
* \return The \p idx th child
*/
const SettingConfig* getChild(unsigned int idx) const
{
if (idx < children.size())
{
auto it = children.begin();
while (idx > 0) { ++it; idx--; }
return &*it;
}
else
return nullptr;
}
void debugOutputAllSettings();
};
/*!
* Single setting data.
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
* setting registry with it's key.
*/
class SettingConfig : public SettingContainer
{
private:
std::string type;
std::string default_value;
std::string unit;
SettingContainer* parent;
public:
SettingConfig(std::string key, std::string label, SettingContainer* parent);
/*!
* Get the SettingConfig::children.
*
* This is used to get the extruder trains; see Settingsbase::setExtruderTrainDefaults
*
* \return SettingConfig::children
*/
const std::list<SettingConfig>& getChildren() const { return children; }
std::string getKey() const
{
return key;
}
void setType(std::string type)
{
this->type = type;
}
std::string getType() const
{
return type;
}
void setDefault(std::string default_value)
{
this->default_value = default_value;
}
std::string getDefaultValue() const
{
return default_value;
}
void setUnit(std::string unit)
{
this->unit = unit;
}
std::string getUnit() const
{
return unit;
}
void debugOutputAllSettings()
{
std::cerr << key << std::endl;
for (SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
};
/*!
* Setting registry.
* There is a single global setting registry.
* This registry contains all known setting keys.
* The registry also contains the settings categories to build up the setting hiarcy from the json file.
*/
class SettingRegistry : NoCopy
{
private:
static SettingRegistry instance;
SettingRegistry();
std::unordered_map<std::string, SettingConfig*> settings;
std::list<SettingContainer> categories;
public:
/*!
* Get the SettingRegistry.
*
* This is a singleton class.
*
* \return The SettingRegistry
*/
static SettingRegistry* getInstance() { return &instance; }
bool settingExists(std::string key) const;
SettingConfig* getSettingConfig(std::string key);
/*!
* Return the first category with the given key as name, or a null pointer.
*
* \param key the key as it is in the JSON file
* \return The first category in the list having the \p key
*/
SettingContainer* getCategory(std::string key);
bool settingsLoaded();
/*!
* Load settings from a json file and all the parents it inherits from.
*
* Uses recursion to load the parent json file.
*
* \param filename The filename of the json file to parse
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename);
void debugOutputAllSettings()
{
for (SettingContainer& cat : categories)
{
cat.debugOutputAllSettings();
}
}
private:
/*!
* \param type type to convert to string
* \return human readable version of json type
*/
static std::string toString(rapidjson::Type type);
/*!
* Load a json document.
*
* \param filename The filename of the json file to parse
* \param json_document (output) the document to be loaded
* \return an error code or zero of succeeded
*/
int loadJSON(std::string filename, rapidjson::Document& json_document);
/*!
* Load settings from a single json file.
*
* \param filename The filename of the json file to parse
* \param warn_duplicates whether to warn for duplicate definitions
* \return an error code or zero of succeeded
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, bool warn_duplicates);
/*!
* Get the string from a json value (generally the default value field of a setting)
* \param dflt The value to convert to string
* \param setting_name The name of the setting (in case we need to display an error message)
* \return The string
*/
static std::string toString(const rapidjson::Value& dflt, std::string setting_name = "?");
/*!
* \param warn_duplicates whether to warn for duplicate definitions
*/
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
};
}//namespace cura
#endif//SETTING_REGISTRY_H
-357
Ver Arquivo
@@ -1,357 +0,0 @@
#include <cctype>
#include <fstream>
#include <stdio.h>
#include <sstream> // ostringstream
#include "utils/logoutput.h"
#include "settings.h"
#include "settingRegistry.h"
namespace cura
{
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
std::string toString(EGCodeFlavor flavor)
{
switch (flavor)
{
case EGCodeFlavor::BFB:
return "BFB";
case EGCodeFlavor::MACH3:
return "Mach3";
case EGCodeFlavor::MAKERBOT:
return "Makerbot";
case EGCodeFlavor::ULTIGCODE:
return "UltiGCode";
case EGCodeFlavor::REPRAP_VOLUMATRIC:
return "RepRap(Volumetric)";
case EGCodeFlavor::REPRAP:
default:
return "RepRap";
}
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
}
SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
: parent(parent)
{
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
{
}
SettingsBase::SettingsBase(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
SettingsMessenger::SettingsMessenger(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
void SettingsBase::setSetting(std::string key, std::string value)
{
if (SettingRegistry::getInstance()->settingExists(key))
{
setting_values[key] = value;
}
else
{
cura::logError("Warning: setting an unregistered setting %s\n", key.c_str() );
setting_values[key] = value; // Handy when programmers are in the process of introducing a new setting
}
}
std::string SettingsBase::getSettingString(std::string key)
{
if (setting_values.find(key) != setting_values.end())
{
return setting_values[key];
}
if (parent)
{
return parent->getSettingString(key);
}
if (SettingRegistry::getInstance()->settingExists(key))
{
setting_values[key] = SettingRegistry::getInstance()->getSettingConfig(key)->getDefaultValue();
}
else
{
setting_values[key] = "";
cura::logError("Unregistered setting %s\n", key.c_str());
}
return setting_values[key];
}
void SettingsMessenger::setSetting(std::string key, std::string value)
{
parent->setSetting(key, value);
}
std::string SettingsMessenger::getSettingString(std::string key)
{
return parent->getSettingString(key);
}
void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
{
const SettingContainer* machine_extruder_trains = SettingRegistry::getInstance()->getCategory(std::string("machine_extruder_trains"));
if (!machine_extruder_trains)
{
// no machine_extruder_trains setting present; just use defaults for each train..
return;
}
const SettingConfig* train = machine_extruder_trains->getChild(extruder_nr);
if (!train)
{
// not enough machine_extruder_trains settings present; just use defaults for this train..
return;
}
for (const SettingConfig& setting : train->getChildren())
{
if (setting_values.find(setting.getKey()) == setting_values.end())
{
setSetting(setting.getKey(), setting.getDefaultValue());
}
}
}
int SettingsBaseVirtual::getSettingAsIndex(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingAsCount(std::string key)
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) * 1000.0;
}
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 180.0 * M_PI;
}
bool SettingsBaseVirtual::getSettingBoolean(std::string key)
{
std::string value = getSettingString(key);
if (value == "on")
return true;
if (value == "yes")
return true;
if (value == "true" or value == "True") //Python uses "True"
return true;
int num = atoi(value.c_str());
return num != 0;
}
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key)
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key)
{
std::string value = getSettingString(key);
return std::max(1.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInPercentage(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInSeconds(std::string key)
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key)
{
FlowTempGraph ret;
const char* c_str = getSettingString(key).c_str();
char const* char_p = c_str;
while (*char_p != '[')
{
if (*char_p == '\0') //We've reached the end of string without encountering the first opening bracket.
{
return ret; //Empty at this point.
}
char_p++;
}
char_p++; // skip the '['
for (; *char_p != '\0'; char_p++)
{
while (*char_p != '[')
{
if (*char_p == '\0') //We've reached the end of string without finding the next opening bracket.
{
return ret; //Don't continue parsing this item then. Just stop and return.
}
char_p++;
}
char_p++; // skip the '['
char* end;
double first = strtod(char_p, &end); //If not a valid number, this becomes zero.
char_p = end;
while (*char_p != ',')
{
if (*char_p == '\0') //We've reached the end of string without finding the comma.
{
return ret; //This entry is incomplete.
}
char_p++;
}
char_p++; // skip the ','
double second = strtod(char_p, &end); //If not a valid number, this becomes zero.
ret.data.emplace_back(first, second);
char_p = end;
while (*char_p != ']')
{
if (*char_p == '\0') //We've reached the end of string without finding the closing bracket.
{
return ret; //This entry is probably complete and has been added, but stop searching.
}
char_p++;
}
char_p++; // skip the ']'
if (*char_p == ']' || *char_p == '\0')
{
break;
}
}
return ret;
}
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key)
{
std::string value = getSettingString(key);
if (value == "RepRap")
return EGCodeFlavor::REPRAP;
else if (value == "UltiGCode")
return EGCodeFlavor::ULTIGCODE;
else if (value == "Makerbot")
return EGCodeFlavor::MAKERBOT;
else if (value == "BFB")
return EGCodeFlavor::BFB;
else if (value == "MACH3")
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return EGCodeFlavor::REPRAP_VOLUMATRIC;
return EGCodeFlavor::REPRAP;
}
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key)
{
std::string value = getSettingString(key);
if (value == "lines")
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
}
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key)
{
std::string value = getSettingString(key);
if (value == "brim")
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
return EPlatformAdhesion::SKIRT;
}
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
{
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key)
{
std::string value = getSettingString(key);
if (value == "random")
return EZSeamType::RANDOM;
if (value == "shortest")
return EZSeamType::SHORTEST;
if (value == "back")
return EZSeamType::BACK;
return EZSeamType::SHORTEST;
}
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "normal")
return ESurfaceMode::NORMAL;
if (value == "surface")
return ESurfaceMode::SURFACE;
if (value == "both")
return ESurfaceMode::BOTH;
return ESurfaceMode::NORMAL;
}
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "nowhere")
{
return FillPerimeterGapMode::NOWHERE;
}
if (value == "everywhere")
{
return FillPerimeterGapMode::EVERYWHERE;
}
if (value == "skin")
{
return FillPerimeterGapMode::SKIN;
}
return FillPerimeterGapMode::NOWHERE;
}
}//namespace cura
+14
Ver Arquivo
@@ -0,0 +1,14 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingConfig.h"
namespace cura
{
SettingConfig::SettingConfig(std::string key, std::string label)
: SettingContainer(key, label)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
}//namespace cura
+76
Ver Arquivo
@@ -0,0 +1,76 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_CONFIG_H
#define SETTINGS_SETTING_CONFIG_H
#include <string>
#include <iostream> // debug out
#include "SettingContainer.h"
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
/*!
* Single setting data.
* Filled from the fdmprinter.json file. Can contain child settings, and is registered in the
* setting registry with it's key.
*/
class SettingConfig : public SettingContainer
{
private:
std::string type; //!< The type of the default_value, e.g. str, int, bool
std::string default_value; //!< The default value for this setting
std::string unit; //!< The unit of the physical quantity in which this setting is measured, e.g. "mm", "mm/s", ""
public:
SettingConfig(std::string key, std::string label);
std::string getKey() const
{
return key;
}
void setType(std::string type)
{
this->type = type;
}
std::string getType() const
{
return type;
}
void setDefault(std::string default_value)
{
this->default_value = default_value;
}
std::string getDefaultValue() const
{
return default_value;
}
void setUnit(std::string unit)
{
this->unit = unit;
}
std::string getUnit() const
{
return unit;
}
void debugOutputAllSettings() const
{
std::cerr << key << "(" << default_value << ")" << std::endl;
for (const SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
};
}//namespace cura
#endif//SETTINGS_SETTING_CONFIG_H
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingContainer.h"
#include "SettingConfig.h"
#include <string>
#include <algorithm> // find_if
namespace cura
{
SettingContainer::SettingContainer(std::string key, std::string label)
: key(key)
, label(label)
{
}
SettingConfig* SettingContainer::addChild(std::string key, std::string label)
{
children.emplace_back(key, label);
return &children.back();
}
SettingConfig& SettingContainer::getOrCreateChild(std::string key, std::string label)
{
auto child_it = std::find_if(children.begin(), children.end(), [&key](SettingConfig& child) { return child.key == key; } );
if (child_it == children.end())
{
children.emplace_back(key, label);
return children.back();
}
else
{
return *child_it;
}
}
void SettingContainer::debugOutputAllSettings() const
{
std::cerr << "\nSETTINGS BASE: " << key << std::endl;
for (const SettingConfig& child : children)
{
child.debugOutputAllSettings();
}
}
}//namespace cura
+83
Ver Arquivo
@@ -0,0 +1,83 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_CONTAINER_H
#define SETTINGS_SETTING_CONTAINER_H
#include <vector>
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
namespace cura
{
// Forward declaration
class SettingConfig;
class SettingRegistry;
/*!
* Setting container for a settings base of definitions and default values.
* Filled from the .def.json files. Contains one or more children settings.
*/
class SettingContainer
{
friend class SettingConfig;
friend class SettingRegistry;
private:
std::string key;
std::string label;
std::list<SettingConfig> children; // must be a list cause the pointers to individual children are mapped to in SettingRegistry::settings.
std::list<std::string> path; //!< The path of parents (internal names) to this container
public:
std::string getKey() const { return key; }
std::string getLabel() const { return label; }
SettingContainer(std::string key, std::string label);
/*!
* Get the SettingConfig::children.
*
* This is used to get the extruder trains; see Settingsbase::setExtruderTrainDefaults
*
* \return SettingConfig::children
*/
const std::list<SettingConfig>& getChildren() const { return children; }
SettingConfig* addChild(std::string key, std::string label);
/*!
* Get the \p idx th child.
*
* This is used to get a specific extruder train in Settingsbase::setExtruderTrainDefaults
*
* \param idx The index in the list of children
* \return The \p idx th child
*/
const SettingConfig* getChild(unsigned int idx) const
{
if (idx < children.size())
{
auto it = children.begin();
while (idx > 0) { ++it; idx--; }
return &*it;
}
else
return nullptr;
}
private:
/*!
* Get the (direct) child with key \p key, or create one with key \p key and label \p label as well.
*
* \param key the key
* \param label the label for creating a new child
* \return The existing or newly created child setting.
*/
SettingConfig& getOrCreateChild(std::string key, std::string label);
public:
void debugOutputAllSettings() const;
};
}//namespace cura
#endif//SETTINGS_SETTING_CONTAINER_H
+383
Ver Arquivo
@@ -0,0 +1,383 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "SettingRegistry.h"
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <cstring> // strtok (split string using delimiters) strcpy
#include <fstream> // ifstream (to see if file exists)
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"
namespace cura
{
SettingRegistry SettingRegistry::instance; // define settingRegistry
std::string SettingRegistry::toString(rapidjson::Type type)
{
switch (type)
{
case rapidjson::Type::kNullType: return "null";
case rapidjson::Type::kFalseType: return "false";
case rapidjson::Type::kTrueType: return "true";
case rapidjson::Type::kObjectType: return "object";
case rapidjson::Type::kArrayType: return "array";
case rapidjson::Type::kStringType: return "string";
case rapidjson::Type::kNumberType: return "number";
default: return "Unknown";
}
}
SettingConfig::SettingConfig(std::string key, std::string label)
: SettingContainer(key, label)
{
// std::cerr << key << std::endl; // debug output to show all frontend registered settings...
}
bool SettingRegistry::settingExists(std::string key) const
{
return setting_key_to_config.find(key) != setting_key_to_config.end();
}
SettingConfig* SettingRegistry::getSettingConfig(std::string key) const
{
auto it = setting_key_to_config.find(key);
if (it == setting_key_to_config.end())
return nullptr;
return it->second;
}
SettingRegistry::SettingRegistry()
: setting_definitions("settings", "Settings")
{
// load search paths from environment variable CURA_ENGINE_SEARCH_PATH
char* paths = getenv("CURA_ENGINE_SEARCH_PATH");
if (paths)
{
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
char delims[] = ":"; // colon
#else
char delims[] = ";"; // semicolon
#endif
char* path = strtok(paths, delims); // search for next path delimited by any of the characters in delims
while (path != NULL)
{
search_paths.emplace(path);
path = strtok(NULL, ";:,"); // continue searching in last call to strtok
}
}
}
int SettingRegistry::loadJSON(std::string filename, rapidjson::Document& json_document)
{
FILE* f = fopen(filename.c_str(), "rb");
if (!f)
{
cura::logError("Couldn't open JSON file.\n");
return 1;
}
char read_buffer[4096];
rapidjson::FileReadStream reader_stream(f, read_buffer, sizeof(read_buffer));
json_document.ParseStream(reader_stream);
fclose(f);
if (json_document.HasParseError())
{
cura::logError("Error parsing JSON(offset %u): %s\n", (unsigned)json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError()));
return 2;
}
return 0;
}
/*!
* Check whether a file exists.
* from https://techoverflow.net/blog/2013/01/11/cpp-check-if-file-exists/
*
* \param filename The path to a filename to check if it exists
* \return Whether the file exists.
*/
bool fexists(const char *filename)
{
std::ifstream ifile(filename);
return (bool)ifile;
}
bool SettingRegistry::getDefinitionFile(const std::string machine_id, std::string& result)
{
for (const std::string& search_path : search_paths)
{
result = search_path + std::string("/") + machine_id + std::string(".def.json");
if (fexists(result.c_str()))
{
return true;
}
}
return false;
}
int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base)
{
if (extruder_nr >= extruder_train_ids.size())
{
logWarning("Couldn't load extruder.def.json file for extruder %i. Index out of bounds.\n Loading first extruder definition instead.\n", extruder_nr);
extruder_nr = 0;
}
std::string definition_file;
bool found = getDefinitionFile(extruder_train_ids[extruder_nr], definition_file);
if (!found)
{
logError("Couldn't find extruder.def.json file for extruder %i.\n", extruder_nr);
return -1;
}
bool warn_base_file_duplicates = false;
return loadJSONsettings(definition_file, settings_base, warn_base_file_duplicates);
}
int SettingRegistry::loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates)
{
rapidjson::Document json_document;
log("Loading %s...\n", filename.c_str());
int err = loadJSON(filename, json_document);
if (err) { return err; }
{ // add parent folder to search paths
char filename_cstr[filename.size()];
std::strcpy(filename_cstr, filename.c_str()); // copy the string because dirname(.) changes the input string!!!
std::string folder_name = std::string(dirname(filename_cstr));
search_paths.emplace(folder_name);
}
if (json_document.HasMember("inherits") && json_document["inherits"].IsString())
{
std::string child_filename;
bool found = getDefinitionFile(json_document["inherits"].GetString(), child_filename);
if (!found)
{
cura::logError("Inherited JSON file \"%s\" not found\n", json_document["inherits"].GetString());
return -1;
}
err = loadJSONsettings(child_filename, settings_base, warn_base_file_duplicates); // load child first
if (err)
{
return err;
}
err = loadJSONsettingsFromDoc(json_document, settings_base, false);
}
else
{
err = loadJSONsettingsFromDoc(json_document, settings_base, warn_base_file_duplicates);
}
if (json_document.HasMember("metadata") && json_document["metadata"].IsObject())
{
const rapidjson::Value& json_metadata = json_document["metadata"];
if (json_metadata.HasMember("machine_extruder_trains") && json_metadata["machine_extruder_trains"].IsObject())
{
const rapidjson::Value& json_machine_extruder_trains = json_metadata["machine_extruder_trains"];
for (rapidjson::Value::ConstMemberIterator extr_train_iterator = json_machine_extruder_trains.MemberBegin(); extr_train_iterator != json_machine_extruder_trains.MemberEnd(); ++extr_train_iterator)
{
int extruder_train_nr = atoi(extr_train_iterator->name.GetString());
if (extruder_train_nr < 0)
{
continue;
}
const rapidjson::Value& json_id = extr_train_iterator->value;
if (!json_id.IsString())
{
continue;
}
const char* id = json_id.GetString();
if (extruder_train_nr >= (int) extruder_train_ids.size())
{
extruder_train_ids.resize(extruder_train_nr + 1);
}
extruder_train_ids[extruder_train_nr] = std::string(id);
}
}
}
return err;
}
int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates)
{
if (!json_document.IsObject())
{
cura::logError("JSON file is not an object.\n");
return 3;
}
if (json_document.HasMember("settings"))
{
std::list<std::string> path;
handleChildren(json_document["settings"], path, settings_base, warn_duplicates);
}
if (json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
if (!conf) //Setting could not be found.
{
logWarning("Trying to override unknown setting %s.\n", setting.c_str());
continue;
}
_loadSettingValues(conf, override_iterator, settings_base);
}
}
return 0;
}
void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
{
if (!settings_list.IsObject())
{
logError("json settings list is not an object!\n");
return;
}
for (rapidjson::Value::ConstMemberIterator setting_iterator = settings_list.MemberBegin(); setting_iterator != settings_list.MemberEnd(); ++setting_iterator)
{
handleSetting(setting_iterator, path, settings_base, warn_duplicates);
if (setting_iterator->value.HasMember("children"))
{
std::list<std::string> path_here = path;
path_here.push_back(setting_iterator->name.GetString());
handleChildren(setting_iterator->value["children"], path_here, settings_base, warn_duplicates);
}
}
}
bool SettingRegistry::settingIsUsedByEngine(const rapidjson::Value& setting)
{
if (setting.HasMember("children"))
{
return false;
}
else
{
return true;
}
}
void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates)
{
const rapidjson::Value& json_setting = json_setting_it->value;
if (!json_setting.IsObject())
{
logError("json setting is not an object!\n");
return;
}
std::string name = json_setting_it->name.GetString();
if (json_setting.HasMember("type") && json_setting["type"].IsString() && json_setting["type"].GetString() == std::string("category"))
{ // skip category objects
setting_key_to_config[name] = nullptr; // add the category name to the mapping, but don't instantiate a setting config for it.
return;
}
if (settingIsUsedByEngine(json_setting))
{
if (!json_setting.HasMember("label") || !json_setting["label"].IsString())
{
logError("json setting \"%s\" has no label!\n", name.c_str());
return;
}
std::string label = json_setting["label"].GetString();
SettingConfig* setting = getSettingConfig(name);
if (warn_duplicates && setting)
{
cura::logWarning("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", name.c_str(), label.c_str(), getSettingConfig(name)->getLabel().c_str());
}
if (!setting)
{
setting = &addSetting(name, label);
}
_loadSettingValues(setting, json_setting_it, settings_base);
}
else
{
setting_key_to_config[name] = nullptr; // add the setting name to the mapping, but don't instantiate a setting config for it.
}
}
SettingConfig& SettingRegistry::addSetting(std::string name, std::string label)
{
SettingConfig* config = setting_definitions.addChild(name, label);
setting_key_to_config[name] = config;
return *config;
}
void SettingRegistry::loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config)
{
const rapidjson::Value& setting_content = json_object_it->value;
if (setting_content.HasMember("default_value"))
{
const rapidjson::Value& dflt = setting_content["default_value"];
if (dflt.IsString())
{
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logWarning("WARNING: Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
}
void SettingRegistry::_loadSettingValues(SettingConfig* config, const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingsBase* settings_base)
{
const rapidjson::Value& data = json_object_it->value;
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (config->getType() == std::string("polygon") || config->getType() == std::string("polygons"))
{ // skip polygon settings : not implemented yet and not used yet (TODO)
// logWarning("WARNING: Loading polygon setting %s not implemented...\n", json_object_it->name.GetString());
return;
}
loadDefault(json_object_it, config);
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
settings_base->_setSetting(config->getKey(), config->getDefaultValue());
}
}//namespace cura
+191
Ver Arquivo
@@ -0,0 +1,191 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTING_REGISTRY_H
#define SETTINGS_SETTING_REGISTRY_H
#include <vector>
#include <unordered_set>
#include <list>
#include <unordered_map>
#include <string>
#include <iostream> // debug out
#include "SettingConfig.h"
#include "SettingContainer.h"
#include "../utils/NoCopy.h"
#include "rapidjson/document.h"
#include "settings.h"
namespace cura
{
/*!
* Setting registry.
* There is a single global setting registry.
* This registry contains all known setting keys and (some of) their attributes.
* The default values are stored and retrieved in case a given setting doesn't get a value from the command line or the frontend.
*/
class SettingRegistry : NoCopy
{
private:
static SettingRegistry instance;
SettingRegistry();
std::unordered_map<std::string, SettingConfig*> setting_key_to_config; //!< Mapping from setting keys to their configurations
SettingContainer setting_definitions; //!< All setting configurations (A flat list)
std::vector<std::string> extruder_train_ids; //!< The internal id's of each extruder (the filename without the extension)
std::unordered_set<std::string> search_paths; //!< The paths to search for json files.
public:
/*!
* Get the SettingRegistry.
*
* This is a singleton class.
*
* \return The SettingRegistry
*/
static SettingRegistry* getInstance() { return &instance; }
/*!
* Check whether a setting exists, according to the settings json files.
*
* \param key The internal key for the setting to test
* \return Whether a definition of the setting is recorded in this registry.
*/
bool settingExists(std::string key) const;
/*!
* Get the config of a setting with a given key.
*
* \param key the (internal) key for a setting
* \return the setting definition values
*/
SettingConfig* getSettingConfig(std::string key) const;
protected:
/*!
* Whether this json settings object is a definition of a CuraEngine setting,
* or only a shorthand setting to control other settings.
* Only settings used by the engine will be recordedd in the registry.
*
* \param setting The setting to check whether CuraEngine uses it.
* \return Whether CuraEngine uses the setting.
*/
bool settingIsUsedByEngine(const rapidjson::Value& setting);
/*!
* Get the filename for the machine definition with the given id.
* Check the directories in SettingRegistry::search_paths.
*
* \param machine_id The id and base filename (without extensions) of the machine definition to search for.
* \param result The filename of the machine definition
* \return Whether we found the file.
*/
bool getDefinitionFile(const std::string machine_id, std::string& result);
/*!
* Get the default value of a json setting object in the format used internally (c style).
*
* \param[in] json_object_it An iterator for a given setting json object
* \param[out] config Where the default value is stored
*/
static void loadDefault(const rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, SettingConfig* config);
public:
/*!
* Load settings from a json file and all the parents it inherits from.
*
* Uses recursion to load the parent json file.
*
* \param filename The filename of the json file to parse
* \param settings_base The settings base where to store the default values.
* \param warn_base_file_duplicates Whether to warn if there are duplicate definitions in the base file (the .def.json which has no inherits).
* \return an error code or zero of succeeded
*/
int loadJSONsettings(std::string filename, SettingsBase* settings_base, bool warn_base_file_duplicates = true);
void debugOutputAllSettings() const
{
setting_definitions.debugOutputAllSettings();
}
/*!
* Load settings from the extruder definition json file and all the parents it inherits from.
* Use the json file refered to in the machine_extruder_trains attribute of the last loaded machine json file.
*
* Uses recursion to load the parent json file.
*
* \param extruder_nr The number of the extruder to load
* \param settings_base The settings base where to store the default values. (The extruder settings base)
* \return an error code or zero of succeeded
*/
int loadExtruderJSONsettings(unsigned int extruder_nr, SettingsBase* settings_base);
private:
/*!
* \param type type to convert to string
* \return human readable version of json type
*/
static std::string toString(rapidjson::Type type);
public:
/*!
* Load a json document.
*
* \param filename The filename of the json file to parse
* \param json_document (output) the document to be loaded
* \return an error code or zero of succeeded
*/
static int loadJSON(std::string filename, rapidjson::Document& json_document);
private:
/*!
* Load settings from a single json file.
*
* \param filename The filename of the json file to parse
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate definitions
* \return an error code or zero of succeeded
*/
int loadJSONsettingsFromDoc(rapidjson::Document& json_document, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Create a new SettingConfig and add it to the registry.
*
* \param name The internal key of the setting
* \param label The human readable name for the frontend
* \return The config created
*/
SettingConfig& addSetting(std::string name, std::string label);
/*!
* Load inessential data about the setting, like its type and unit.
*
* \param[out] config Where to store the data
* \param[in] json_object_it Iterator to a setting json object
* \param[out] settings_base The settings base where to store the default values.
*/
void _loadSettingValues(SettingConfig* config, const rapidjson::Value::ConstMemberIterator& json_object_it, SettingsBase* settings_base);
/*!
* Handle a json object which contains a list of settings.
*
* \param settings_list The object containing one or more setting definitions
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleChildren(const rapidjson::Value& settings_list, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
/*!
* Handle a json object for a setting.
*
* \param json_setting_it Iterator for the setting which contains the key (setting name) and attributes info
* \param path The path of (internal) setting names traversed to get to this object
* \param settings_base The settings base where to store the default values.
* \param warn_duplicates whether to warn for duplicate setting definitions
*/
void handleSetting(const rapidjson::Value::ConstMemberIterator& json_setting_it, std::list<std::string>& path, SettingsBase* settings_base, bool warn_duplicates);
};
}//namespace cura
#endif//SETTINGS_SETTING_REGISTRY_H
+291
Ver Arquivo
@@ -0,0 +1,291 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_TO_GV_H
#define SETTINGS_TO_GV_H
#include <stdio.h> // for file output
#include <sstream>
#include <iostream> // debug IO
#include <libgen.h> // dirname
#include <string>
#include <algorithm> // find_if
#include <regex> // regex_search
#include <cassert>
#include <fstream>
#include <set>
#include <unordered_map>
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/filereadstream.h"
#include "../utils/logoutput.h"
#include "SettingRegistry.h"
namespace cura
{
class SettingsToGv
{
enum class RelationType
{
PARENT_CHILD,
INHERIT_FUNCTION,
ERROR_FUNCTION,
WARNING_FUNCTION
};
FILE* out;
std::set<std::string> engine_settings;
std::unordered_map<std::string, std::string> setting_to_color;
bool parent_child_viz, inherit_viz, error_viz, warning_viz, global_only_viz;
public:
SettingsToGv(std::string output_filename, std::string engine_settings_filename, bool parent_child_viz, bool inherit_viz, bool error_viz, bool warning_viz, bool global_only_viz)
: parent_child_viz(parent_child_viz)
, inherit_viz(inherit_viz)
, error_viz(error_viz)
, warning_viz(warning_viz)
, global_only_viz(global_only_viz)
{
out = fopen(output_filename.c_str(), "w");
fprintf(out, "digraph G {\n");
std::ifstream engine_settings_file(engine_settings_filename.c_str());
std::string line;
while (std::getline(engine_settings_file, line))
{
engine_settings.insert(line);
//fprintf(out, "%s [color=green];\n", line.c_str());
}
engine_settings_file.close();
}
private:
void generateEdge(const std::string& parent, const std::string& child, RelationType relation_type)
{
if (global_only_viz)
{
auto parent_it = setting_to_color.find(parent);
if (parent_it != setting_to_color.end())
{
fprintf(out, "%s [color=%s];\n", parent_it->first.c_str(), parent_it->second.c_str());
}
auto child_it = setting_to_color.find(child);
if (child_it != setting_to_color.end())
{
fprintf(out, "%s [color=%s];\n", child_it->first.c_str(), child_it->second.c_str());
}
}
else
{
if (engine_settings.find(parent) != engine_settings.end())
{
fprintf(out, "%s [color=green];\n", parent.c_str());
}
if (engine_settings.find(child) != engine_settings.end())
{
fprintf(out, "%s [color=green];\n", child.c_str());
}
}
std::string color;
switch (relation_type)
{
case SettingsToGv::RelationType::INHERIT_FUNCTION:
if (!inherit_viz)
{
return;
}
color = "blue";
break;
case SettingsToGv::RelationType::PARENT_CHILD:
if (!parent_child_viz)
{
return;
}
color = "black";
break;
case SettingsToGv::RelationType::ERROR_FUNCTION:
if (!error_viz)
{
return;
}
color = "red";
break;
case SettingsToGv::RelationType::WARNING_FUNCTION:
if (!warning_viz)
{
return;
}
color = "orange";
break;
}
fprintf(out, "edge [color=%s];\n", color.c_str());
fprintf(out, "%s -> %s;\n", parent.c_str(), child.c_str());
}
bool createFunctionEdges(const rapidjson::Value& data, std::string function_key, const std::string& parent, const std::string& name, const RelationType relation_type)
{
bool generated_edge = false;
if (data.HasMember(function_key.c_str()) && data[function_key.c_str()].IsString())
{
std::string function = data[function_key.c_str()].GetString();
std::regex setting_name_regex("[a-zA-Z0-9_]+"); // matches mostly with setting names
std::smatch regex_match;
while (std::regex_search (function, regex_match, setting_name_regex))
{
std::string inherited_setting_string = regex_match[0];
if (inherited_setting_string == "parent_value")
{
generateEdge(parent, name, RelationType::PARENT_CHILD);
generated_edge = true;
}
else if ( ! std::regex_match(inherited_setting_string, std::regex("[0-9]+")) && // exclude numbers
// result != "parent_value" &&
inherited_setting_string != "if" && inherited_setting_string != "else" && inherited_setting_string != "and"
&& inherited_setting_string != "or" && inherited_setting_string != "math" && inherited_setting_string != "ceil"
&& inherited_setting_string != "int" && inherited_setting_string != "round" && inherited_setting_string != "max" // exclude operators and functions
&& inherited_setting_string != "log" // exclude functions
&& inherited_setting_string != "grid" && inherited_setting_string != "triangles" // exclude enum values
&& inherited_setting_string != "cubic" && inherited_setting_string != "tetrahedral" // exclude enum values
&& inherited_setting_string != "raft" // exclude enum values
&& function.c_str()[regex_match.position() + regex_match.length()] != '\'') // exclude enum terms
{
if (inherited_setting_string == parent)
{
generated_edge = true;
generateEdge(inherited_setting_string, name, RelationType::PARENT_CHILD);
}
else
{
generateEdge(inherited_setting_string, name, relation_type);
}
}
function = regex_match.suffix().str();
}
}
return generated_edge;
}
void parseSetting(const std::string& parent, rapidjson::Value::ConstMemberIterator json_object_it)
{
std::string name = json_object_it->name.GetString();
// std::cerr << "parsed: " << name <<"\n";
bool generated_edge = false;
const rapidjson::Value& data = json_object_it->value;
if (data.HasMember("type") && data["type"].IsString() && data["type"].GetString() != std::string("category"))
{
if (global_only_viz)
{
std::string color;
if (!data.HasMember("settable_per_mesh") || data["settable_per_mesh"].GetBool() == true)
{
color = "green";
}
else if (data.HasMember("settable_per_mesh") && data["settable_per_mesh"].GetBool() == false)
{
if (!data.HasMember("settable_per_extruder") || data["settable_per_extruder"].GetBool() == true)
{
color = "yellow";
}
else if (data.HasMember("settable_per_extruder") && data["settable_per_extruder"].GetBool() == false)
{
if (!data.HasMember("settable_per_meshgroup") || data["settable_per_meshgroup"].GetBool() == true)
{
color = "orange";
}
else if (data.HasMember("settable_per_meshgroup") && data["settable_per_meshgroup"].GetBool() == false)
{
color = "red";
}
}
}
setting_to_color.emplace(name, color);
// fprintf(out, "%s [color=%s];\n", name.c_str(), color.c_str());
}
bool generated_edge_inherit = createFunctionEdges(data, "value", parent, name, RelationType::INHERIT_FUNCTION);
bool generated_edge_max = createFunctionEdges(data, "maximum_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_min = createFunctionEdges(data, "minimum_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_max_warn = createFunctionEdges(data, "maximum_value_warning", parent, name, RelationType::WARNING_FUNCTION);
bool generated_edge_min_warn = createFunctionEdges(data, "minimum_value_warning", parent, name, RelationType::WARNING_FUNCTION);
if (generated_edge_inherit || generated_edge_max_warn || generated_edge_min_warn || generated_edge_max || generated_edge_min)
{
generated_edge = true;
}
if (!generated_edge && parent != "")
{
generateEdge(parent, name, RelationType::PARENT_CHILD);
}
}
else
{
name = "";
}
// recursive part
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
parseSetting(name, setting_iterator);
}
}
}
void parseJson(const rapidjson::Document& json_document)
{
if (json_document.HasMember("settings"))
{
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_document["settings"].MemberBegin(); setting_iterator != json_document["settings"].MemberEnd(); ++setting_iterator)
{
parseSetting("", setting_iterator);
}
}
}
int generateRecursive(std::string filename)
{
rapidjson::Document json_document;
int err = SettingRegistry::loadJSON(filename, json_document);
if (err) { return err; }
if (json_document.HasMember("inherits"))
{
std::string filename_copy = std::string(filename.c_str()); // copy the string because dirname(.) changes the input string!!!
char* filename_cstr = (char*)filename_copy.c_str();
int err = generate(std::string(dirname(filename_cstr)) + std::string("/") + json_document["inherits"].GetString());
if (err)
{
return err;
}
}
parseJson(json_document);
return 0;
}
public:
int generate(std::string json_filename)
{
int err = generateRecursive(json_filename);
fprintf(out, "}\n");
fclose(out);
return err;
}
};
} // namespace cura
#endif // SETTINGS_TO_GV_H
+437
Ver Arquivo
@@ -0,0 +1,437 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include <cctype>
#include <fstream>
#include <stdio.h>
#include <sstream> // ostringstream
#include <regex> // regex parsing for temp flow graph
#include <string> // stod (string to double)
#include "../utils/logoutput.h"
#include "settings.h"
#include "SettingRegistry.h"
namespace cura
{
//c++11 no longer defines M_PI, so add our own constant.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
std::string toString(EGCodeFlavor flavor)
{
switch (flavor)
{
case EGCodeFlavor::BFB:
return "BFB";
case EGCodeFlavor::MACH3:
return "Mach3";
case EGCodeFlavor::MAKERBOT:
return "Makerbot";
case EGCodeFlavor::ULTIGCODE:
return "UltiGCode";
case EGCodeFlavor::REPRAP_VOLUMATRIC:
return "RepRap(Volumetric)";
case EGCodeFlavor::GRIFFIN:
return "Griffin";
case EGCodeFlavor::REPETIER:
return "Repetier";
case EGCodeFlavor::REPRAP:
default:
return "RepRap";
}
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
}
SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
: parent(parent)
{
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
{
}
SettingsBase::SettingsBase(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
SettingsMessenger::SettingsMessenger(SettingsBaseVirtual* parent)
: SettingsBaseVirtual(parent)
{
}
void SettingsBase::_setSetting(std::string key, std::string value)
{
setting_values[key] = value;
}
void SettingsBase::setSetting(std::string key, std::string value)
{
if (SettingRegistry::getInstance()->settingExists(key))
{
_setSetting(key, value);
}
else
{
cura::logWarning("Setting an unregistered setting %s to %s\n", key.c_str(), value.c_str());
_setSetting(key, value); // Handy when programmers are in the process of introducing a new setting
}
}
void SettingsBase::setSettingInheritBase(std::string key, const SettingsBaseVirtual& parent)
{
setting_inherit_base.emplace(key, &parent);
}
std::string SettingsBase::getSettingString(std::string key) const
{
if (setting_values.find(key) != setting_values.end())
{
return setting_values.at(key);
}
if (setting_inherit_base.find(key) != setting_inherit_base.end())
{
return setting_inherit_base.at(key)->getSettingString(key);
}
if (parent)
{
return parent->getSettingString(key);
}
const_cast<SettingsBase&>(*this).setting_values[key] = "";
cura::logWarning("Unregistered setting %s\n", key.c_str());
return "";
}
void SettingsMessenger::setSetting(std::string key, std::string value)
{
parent->setSetting(key, value);
}
void SettingsMessenger::setSettingInheritBase(std::string key, const SettingsBaseVirtual& new_parent)
{
parent->setSettingInheritBase(key, new_parent);
}
std::string SettingsMessenger::getSettingString(std::string key) const
{
return parent->getSettingString(key);
}
int SettingsBaseVirtual::getSettingAsIndex(std::string key) const
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
int SettingsBaseVirtual::getSettingAsCount(std::string key) const
{
std::string value = getSettingString(key);
return atoi(value.c_str());
}
unsigned int SettingsBaseVirtual::getSettingAsLayerNumber(std::string key) const
{
const unsigned int indicated_layer_number = stoul(getSettingString(key));
if (indicated_layer_number < 1) //Input checking: Layer 0 is not allowed.
{
cura::logWarning("Invalid layer number %i for setting %s.", indicated_layer_number, key.c_str());
return 0; //Assume layer 1.
}
return indicated_layer_number - 1; //Input starts counting at layer 1, but engine code starts counting at layer 0.
}
double SettingsBaseVirtual::getSettingInMillimeters(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
int SettingsBaseVirtual::getSettingInMicrons(std::string key) const
{
return getSettingInMillimeters(key) * 1000.0;
}
double SettingsBaseVirtual::getSettingInAngleDegrees(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBaseVirtual::getSettingInAngleRadians(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str()) / 180.0 * M_PI;
}
bool SettingsBaseVirtual::getSettingBoolean(std::string key) const
{
std::string value = getSettingString(key);
if (value == "on")
return true;
if (value == "yes")
return true;
if (value == "true" or value == "True") //Python uses "True"
return true;
int num = atoi(value.c_str());
return num != 0;
}
double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key) const
{
std::string value = getSettingString(key);
return atof(value.c_str());
}
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInPercentage(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
{
std::string value = getSettingString(key);
return std::max(0.0, atof(value.c_str()));
}
DraftShieldHeightLimitation SettingsBaseVirtual::getSettingAsDraftShieldHeightLimitation(const std::string key) const
{
const std::string value = getSettingString(key);
if (value == "full")
{
return DraftShieldHeightLimitation::FULL;
}
else if (value == "limited")
{
return DraftShieldHeightLimitation::LIMITED;
}
return DraftShieldHeightLimitation::FULL; //Default.
}
FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) const
{
FlowTempGraph ret;
std::string value_string = getSettingString(key);
if (value_string.empty())
{
return ret; //Empty at this point.
}
std::regex regex("(\\[([^,\\[]*),([^,\\]]*)\\])");
// match with:
// - the last opening bracket '['
// - then a bunch of characters until the first comma
// - a comma
// - a bunch of cahracters until the first closing bracket ']'
// matches with any substring which looks like "[ 124.512 , 124.1 ]"
// default constructor = end-of-sequence:
std::regex_token_iterator<std::string::iterator> rend;
int submatches[] = { 1, 2, 3 }; // match whole pair, first number and second number of a pair
std::regex_token_iterator<std::string::iterator> match_iter(value_string.begin(), value_string.end(), regex, submatches);
while (match_iter != rend)
{
match_iter++; // match the whole pair
if (match_iter == rend)
{
break;
}
std::string first_substring = *match_iter++;
std::string second_substring = *match_iter++;
try
{
double first = std::stod(first_substring);
double second = std::stod(second_substring);
ret.data.emplace_back(first, second);
}
catch (const std::invalid_argument& e)
{
logError("Couldn't read 2D graph element [%s,%s] in setting '%s'. Ignored.\n", first_substring.c_str(), second_substring.c_str(), key.c_str());
}
}
return ret;
}
FMatrix3x3 SettingsBaseVirtual::getSettingAsPointMatrix(std::string key) const
{
FMatrix3x3 ret;
std::string value_string = getSettingString(key);
if (value_string.empty())
{
return ret; // standard matrix ([1,0,0],[0,1,0],[0,0,1])
}
std::string num("([^,\\] ]*)"); // match with anything but the next ',' ']' or space and capture the match
std::ostringstream row; // match with "[num,num,num]" and ignore whitespace
row << "\\s*\\[\\s*" << num << "\\s*,\\s*" << num << "\\s*,\\s*" << num << "\\s*\\]\\s*";
std::ostringstream matrix; // match with "[row,row,row]" and ignore whitespace
matrix << "\\s*\\[" << row.str() << "\\s*,\\s*" << row.str() << "\\s*,\\s*" << row.str() << "\\]\\s*";
std::regex point_matrix_regex(matrix.str());
std::cmatch sub_matches; // same as std::match_results<const char*> cm;
std::regex_match(value_string.c_str(), sub_matches, point_matrix_regex);
if (sub_matches.size() != 10) // one match for the whole string
{
logWarning("Mesh transformation matrix could not be parsed!\n\tFormat should be [[f,f,f],[f,f,f],[f,f,f]] allowing whitespace anywhere in between.\n\tWhile what was given was \"%s\".\n", value_string.c_str());
return ret; // standard matrix ([1,0,0],[0,1,0],[0,0,1])
}
unsigned int sub_match_idx = 1; // skip the first because the first submatch is the whole string
for (unsigned int x = 0; x < 3; x++)
{
for (unsigned int y = 0; y < 3; y++)
{
std::sub_match<const char*> sub_match = sub_matches[sub_match_idx];
ret.m[y][x] = strtod(std::string(sub_match.str()).c_str(), nullptr);
sub_match_idx++;
}
}
return ret;
}
EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
{
std::string value = getSettingString(key);
if (value == "Griffin")
return EGCodeFlavor::GRIFFIN;
else if (value == "UltiGCode")
return EGCodeFlavor::ULTIGCODE;
else if (value == "Makerbot")
return EGCodeFlavor::MAKERBOT;
else if (value == "BFB")
return EGCodeFlavor::BFB;
else if (value == "MACH3")
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return EGCodeFlavor::REPRAP_VOLUMATRIC;
else if (value == "Repetier")
return EGCodeFlavor::REPETIER;
return EGCodeFlavor::REPRAP;
}
EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
{
std::string value = getSettingString(key);
if (value == "lines")
return EFillMethod::LINES;
if (value == "grid")
return EFillMethod::GRID;
if (value == "cubic")
return EFillMethod::CUBIC;
if (value == "tetrahedral")
return EFillMethod::TETRAHEDRAL;
if (value == "triangles")
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
}
EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string key) const
{
std::string value = getSettingString(key);
if (value == "brim")
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
return EPlatformAdhesion::SKIRT;
}
ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key) const
{
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
EZSeamType SettingsBaseVirtual::getSettingAsZSeamType(std::string key) const
{
std::string value = getSettingString(key);
if (value == "random")
return EZSeamType::RANDOM;
if (value == "shortest")
return EZSeamType::SHORTEST;
if (value == "back")
return EZSeamType::BACK;
return EZSeamType::SHORTEST;
}
ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
{
std::string value = getSettingString(key);
if (value == "normal")
return ESurfaceMode::NORMAL;
if (value == "surface")
return ESurfaceMode::SURFACE;
if (value == "both")
return ESurfaceMode::BOTH;
return ESurfaceMode::NORMAL;
}
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "off")
{
return CombingMode::OFF;
}
if (value == "all")
{
return CombingMode::ALL;
}
if (value == "noskin")
{
return CombingMode::NO_SKIN;
}
return CombingMode::ALL;
}
SupportDistPriority SettingsBaseVirtual::getSettingAsSupportDistPriority(std::string key)
{
std::string value = getSettingString(key);
if (value == "xy_overrides_z")
{
return SupportDistPriority::XY_OVERRIDES_Z;
}
if (value == "z_overrides_xy")
{
return SupportDistPriority::Z_OVERRIDES_XY;
}
return SupportDistPriority::XY_OVERRIDES_Z;
}
}//namespace cura
+112 -48
Ver Arquivo
@@ -1,14 +1,15 @@
#ifndef SETTINGS_H
#define SETTINGS_H
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef SETTINGS_SETTINGS_H
#define SETTINGS_SETTINGS_H
#include <vector>
#include <map>
#include <unordered_map>
#include <sstream>
#include "utils/floatpoint.h"
#include "../utils/floatpoint.h"
#include "FlowTempGraph.h"
#include "../FlowTempGraph.h"
namespace cura
{
@@ -75,6 +76,18 @@ enum class EGCodeFlavor
* M106 Sxxx and M107 are used to turn the fan on/off.
**/
REPRAP_VOLUMATRIC = 5,
/**
* Griffin flavored is Marlin based GCode.
* This is a type of RepRap used for machines with multiple extruder trains.
* G0 for moves, G1 for extrusion.
* E values give mm of filament extrusion.
* E values are stored separately per extruder train.
* Retraction is done on E values with G1. Start/end code is added.
* M227 is used to initialize a single extrusion train.
**/
GRIFFIN = 6,
REPETIER = 7,
};
/*!
@@ -91,6 +104,8 @@ enum class EFillMethod
{
LINES,
GRID,
CUBIC,
TETRAHEDRAL,
TRIANGLES,
CONCENTRIC,
ZIG_ZAG,
@@ -98,7 +113,7 @@ enum class EFillMethod
};
/*!
* Type of platform adheasion
* Type of platform adhesion.
*/
enum class EPlatformAdhesion
{
@@ -131,11 +146,26 @@ enum class ESurfaceMode
BOTH
};
enum class FillPerimeterGapMode
enum class CombingMode
{
NOWHERE,
EVERYWHERE,
SKIN
OFF,
ALL,
NO_SKIN
};
/*!
* How the draft shield height is limited.
*/
enum class DraftShieldHeightLimitation
{
FULL, //Draft shield takes full height of the print.
LIMITED //Draft shield is limited by draft_shield_height setting.
};
enum class SupportDistPriority
{
XY_OVERRIDES_Z,
Z_OVERRIDES_XY
};
#define MAX_EXTRUDERS 16
@@ -155,10 +185,19 @@ class SettingsBaseVirtual
protected:
SettingsBaseVirtual* parent;
public:
virtual std::string getSettingString(std::string key) = 0;
virtual std::string getSettingString(std::string key) const = 0;
virtual void setSetting(std::string key, std::string value) = 0;
/*!
* Set the parent settings base for inheriting a setting to a specific setting base.
* This overrides the use of \ref SettingsBaseVirtual::parent.
*
* \param key The setting for which to override the inheritance
* \param parent The setting base from which to obtain the setting instead.
*/
virtual void setSettingInheritBase(std::string key, const SettingsBaseVirtual& parent) = 0;
virtual ~SettingsBaseVirtual() {}
SettingsBaseVirtual(); //!< SettingsBaseVirtual without a parent settings object
@@ -167,31 +206,45 @@ public:
void setParent(SettingsBaseVirtual* parent) { this->parent = parent; }
SettingsBaseVirtual* getParent() { return parent; }
int getSettingAsIndex(std::string key);
int getSettingAsCount(std::string key);
double getSettingInAngleRadians(std::string key);
int getSettingInMicrons(std::string key);
bool getSettingBoolean(std::string key);
double getSettingInDegreeCelsius(std::string key);
double getSettingInMillimetersPerSecond(std::string key);
double getSettingInCubicMillimeters(std::string key);
double getSettingInPercentage(std::string key);
double getSettingInSeconds(std::string key);
FlowTempGraph getSettingAsFlowTempGraph(std::string key);
std::vector<std::pair<double, double>> getSettingAsPointVector(std::string key);
EGCodeFlavor getSettingAsGCodeFlavor(std::string key);
EFillMethod getSettingAsFillMethod(std::string key);
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key);
ESupportType getSettingAsSupportType(std::string key);
EZSeamType getSettingAsZSeamType(std::string key);
ESurfaceMode getSettingAsSurfaceMode(std::string key);
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
int getSettingAsIndex(std::string key) const;
int getSettingAsCount(std::string key) const;
/*!
* \brief Interprets a setting as a layer number.
*
* The input of the layer number is one-based. This translates it to
* zero-based numbering.
*
* \return Zero-based numbering of a layer number setting.
*/
unsigned int getSettingAsLayerNumber(std::string key) const;
double getSettingInAngleDegrees(std::string key) const;
double getSettingInAngleRadians(std::string key) const;
double getSettingInMillimeters(std::string key) const;
int getSettingInMicrons(std::string key) const;
bool getSettingBoolean(std::string key) const;
double getSettingInDegreeCelsius(std::string key) const;
double getSettingInMillimetersPerSecond(std::string key) const;
double getSettingInCubicMillimeters(std::string key) const;
double getSettingInPercentage(std::string key) const;
double getSettingInSeconds(std::string key) const;
FlowTempGraph getSettingAsFlowTempGraph(std::string key) const;
FMatrix3x3 getSettingAsPointMatrix(std::string key) const;
DraftShieldHeightLimitation getSettingAsDraftShieldHeightLimitation(const std::string key) const;
EGCodeFlavor getSettingAsGCodeFlavor(std::string key) const;
EFillMethod getSettingAsFillMethod(std::string key) const;
EPlatformAdhesion getSettingAsPlatformAdhesion(std::string key) const;
ESupportType getSettingAsSupportType(std::string key) const;
EZSeamType getSettingAsZSeamType(std::string key) const;
ESurfaceMode getSettingAsSurfaceMode(std::string key) const;
CombingMode getSettingAsCombingMode(std::string key);
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
};
class SettingRegistry;
/*!
* Base class for every object that can hold settings.
* The SettingBase object can hold multiple key-value pairs that define settings.
@@ -201,26 +254,28 @@ public:
*/
class SettingsBase : public SettingsBaseVirtual
{
friend class SettingRegistry;
private:
std::unordered_map<std::string, std::string> setting_values;
/*!
* Mapping for each setting which must inherit from a different setting base than \ref SettingsBaseVirtual::parent
*/
std::unordered_map<std::string, const SettingsBaseVirtual*> setting_inherit_base;
public:
SettingsBase(); //!< SettingsBase without a parent settings object
SettingsBase(SettingsBaseVirtual* parent); //!< construct a SettingsBase with a parent settings object
/*!
* Retrieve the defaults for each extruder train from the machine_extruder_trains settings
* and set the general settings to those defaults if they haven't been set yet.
*
* Only sets those settings which haven't already been set on that level - not looking at its parent (FffProcessor, meshgroup) or children (meshes).
*
* \param extruder_nr The index of which extruder train in machine_extruder_trains to get the settings from
* Set a setting to a value.
* \param key the setting
* \param value the value
*/
void setExtruderTrainDefaults(unsigned int extruder_nr);
void setSetting(std::string key, std::string value);
std::string getSettingString(std::string key); //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
void setSettingInheritBase(std::string key, const SettingsBaseVirtual& parent); //!< See \ref SettingsBaseVirtual::setSettingInheritBase
std::string getSettingString(std::string key) const; //!< Get a setting from this SettingsBase (or any ancestral SettingsBase)
std::string getAllLocalSettingsString()
std::string getAllLocalSettingsString() const
{
std::stringstream sstream;
for (auto pair : setting_values)
@@ -233,11 +288,18 @@ public:
return sstream.str();
}
void debugOutputAllLocalSettings()
void debugOutputAllLocalSettings() const
{
for (auto pair : setting_values)
std::cerr << pair.first << " : " << pair.second << std::endl;
}
protected:
/*!
* Set a setting without checking if it's registered.
*
* Used in SettingsRegistry
*/
void _setSetting(std::string key, std::string value);
};
/*!
@@ -251,9 +313,11 @@ public:
SettingsMessenger(SettingsBaseVirtual* parent); //!< construct a SettingsMessenger with a parent settings object
void setSetting(std::string key, std::string value); //!< Set a setting of the parent SettingsBase to a given value
std::string getSettingString(std::string key); //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
void setSettingInheritBase(std::string key, const SettingsBaseVirtual& parent); //!< See \ref SettingsBaseVirtual::setSettingInheritBase
std::string getSettingString(std::string key) const; //!< Get a setting from the parent SettingsBase (or any further ancestral SettingsBase)
};
}//namespace cura
#endif//SETTINGS_H
#endif//SETTINGS_SETTINGS_H
+152 -82
Ver Arquivo
@@ -1,5 +1,8 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <cmath> // std::ceil
#include "skin.h"
#include "utils/math.h"
#include "utils/polygonUtils.h"
#define MIN_AREA_SIZE (0.4 * 0.4)
@@ -8,21 +11,21 @@ namespace cura
{
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
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, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
generateSkinAreas(layerNr, mesh, innermost_wall_line_width, downSkinCount, upSkinCount, wall_line_count, no_small_gaps_heuristic);
SliceLayer* layer = &storage.layers[layerNr];
SliceLayer* layer = &mesh.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
generateSkinInsets(part, extrusionWidth, insetCount, avoidOverlappingPerimeters_0, avoidOverlappingPerimeters);
generateSkinInsets(part, innermost_wall_line_width, insetCount);
}
}
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
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 = storage.layers[layer_nr];
SliceLayer& layer = mesh.layers[layer_nr];
if (downSkinCount == 0 && upSkinCount == 0)
{
@@ -35,11 +38,11 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
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 skin.
continue; // the last wall is not present, the part should only get inter perimeter gaps, but no skin.
}
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
Polygons upskin = part.insets.back().offset(-innermost_wall_line_width / 2);
Polygons downskin = (downSkinCount == 0) ? Polygons() : upskin;
if (upSkinCount == 0) upskin = Polygons();
auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
@@ -49,7 +52,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
{
if (part.boundaryBox.hit(part2.boundaryBox))
{
unsigned int wall_idx = std::min(wall_line_count, (int) part2.insets.size()) - 1;
unsigned int wall_idx = std::max(0, std::min(wall_line_count, (int) part2.insets.size()) - 1);
result.add(part2.insets[wall_idx]);
}
}
@@ -60,32 +63,32 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
{
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
{
downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
downskin = downskin.difference(getInsidePolygons(mesh.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
}
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(mesh.layers.size()))
{
upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
upskin = upskin.difference(getInsidePolygons(mesh.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
}
}
else
{
if (layer_nr >= downSkinCount && downSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr - 1]);
for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
not_air = not_air.intersection(getInsidePolygons(mesh.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(storage.layers.size()) - upSkinCount && upSkinCount > 0)
if (layer_nr < static_cast<int>(mesh.layers.size()) - 1 - upSkinCount && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
not_air = not_air.intersection(getInsidePolygons(mesh.layers[upskin_layer_nr]));
}
upskin = upskin.difference(not_air); // skin overlaps with the walls
}
@@ -104,7 +107,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wa
}
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount)
{
if (insetCount == 0)
{
@@ -118,15 +121,14 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
skin_part.insets.push_back(Polygons());
if (i == 0)
{
PolygonUtils::offsetSafe(skin_part.outline, - extrusionWidth/2, extrusionWidth, skin_part.insets[0], avoidOverlappingPerimeters_0);
Polygons in_between = skin_part.outline.difference(skin_part.insets[0].offset(extrusionWidth/2));
skin_part.perimeterGaps.add(in_between);
} else
skin_part.insets[0] = skin_part.outline.offset(-wall_line_width / 2);
}
else
{
PolygonUtils::offsetExtrusionWidth(skin_part.insets[i-1], true, extrusionWidth, skin_part.insets[i], &skin_part.perimeterGaps, avoidOverlappingPerimeters);
skin_part.insets[i] = skin_part.insets[i - 1].offset(-wall_line_width);
}
// optimize polygons: remove unnnecesary verts
// optimize polygons: remove unnecessary verts
skin_part.insets[i].simplify();
if (skin_part.insets[i].size() < 1)
{
@@ -137,18 +139,17 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = storage.layers[layerNr];
SliceLayer& layer = mesh.layers[layerNr];
for(SliceLayerPart& part : layer.parts)
{
if (int(part.insets.size()) < wall_line_count)
{
part.infill_area.emplace_back(); // put empty polygon as (uncombined) infill
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
Polygons infill = part.insets.back().offset(-innermost_wall_line_width / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
@@ -162,17 +163,101 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_e
}
infill.removeSmallAreas(MIN_AREA_SIZE);
part.infill_area.push_back(infill.offset(infill_skin_overlap));
part.infill_area = infill.offset(infill_skin_overlap);
}
}
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh, unsigned int gradual_infill_step_height, unsigned int max_infill_steps)
{
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
// no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density]
float layer_skip_count = 8; // skip every so many layers as to ignore small gaps in the model making computation more easy
if (!mesh.getSettingBoolean("skin_no_small_gaps_heuristic"))
{
layer_skip_count = 1;
}
unsigned int gradual_infill_step_layer_count = round_divide(gradual_infill_step_height, mesh.getSettingInMicrons("layer_height")); // The difference in layer count between consecutive density infill areas
// make gradual_infill_step_height divisable by layer_skip_count
float n_skip_steps_per_gradual_step = std::max(1.0f, std::ceil(gradual_infill_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_infill_step_layer_count
layer_skip_count = gradual_infill_step_layer_count / n_skip_steps_per_gradual_step;
size_t min_layer = mesh.getSettingAsCount("bottom_layers");
size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
for (size_t layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{ // loop also over layers which don't contain infill cause of bottom_ and top_layer to initialize their infill_area_per_combine_per_density
SliceLayer& layer = mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
assert(part.infill_area_per_combine_per_density.size() == 0 && "infill_area_per_combine_per_density is supposed to be uninitialized");
const Polygons& infill_area = part.getOwnInfillArea();
if (infill_area.size() == 0 || layer_idx < min_layer || layer_idx > max_layer)
{ // initialize infill_area_per_combine_per_density empty
part.infill_area_per_combine_per_density.emplace_back(); // create a new infill_area_per_combine
part.infill_area_per_combine_per_density.back().emplace_back(); // put empty infill area in the newly constructed infill_area_per_combine
// note: no need to copy part.infill_area, cause it's the empty vector anyway
continue;
}
Polygons less_dense_infill = infill_area; // one step less dense with each infill_step
for (unsigned int infill_step = 0; infill_step < max_infill_steps; infill_step++)
{
size_t min_layer = layer_idx + infill_step * gradual_infill_step_layer_count + layer_skip_count;
size_t max_layer = layer_idx + (infill_step + 1) * gradual_infill_step_layer_count;
for (float upper_layer_idx = min_layer; static_cast<unsigned int>(upper_layer_idx) <= max_layer; upper_layer_idx += layer_skip_count)
{
if (static_cast<unsigned int>(upper_layer_idx) >= mesh.layers.size())
{
less_dense_infill.clear();
break;
}
SliceLayer& upper_layer = mesh.layers[static_cast<unsigned int>(upper_layer_idx)];
Polygons relevent_upper_polygons;
for (SliceLayerPart& upper_layer_part : upper_layer.parts)
{
if (!upper_layer_part.boundaryBox.hit(part.boundaryBox))
{
continue;
}
relevent_upper_polygons.add(upper_layer_part.getOwnInfillArea());
}
less_dense_infill = less_dense_infill.intersection(relevent_upper_polygons);
}
if (less_dense_infill.size() == 0)
{
break;
}
// add new infill_area_per_combine for the current density
part.infill_area_per_combine_per_density.emplace_back();
std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back();
const Polygons more_dense_infill = infill_area.difference(less_dense_infill);
infill_area_per_combine_current_density.push_back(more_dense_infill);
if (less_dense_infill.size() == 0)
{
break;
}
}
part.infill_area_per_combine_per_density.emplace_back();
std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back();
infill_area_per_combine_current_density.push_back(infill_area);
part.infill_area_own = nullptr; // clear infill_area_own, it's not needed any more.
assert(part.infill_area_per_combine_per_density.size() != 0 && "infill_area_per_combine_per_density is now initialized");
}
}
}
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount)
{
if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
{
return;
}
if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
{
return;
}
@@ -180,72 +265,57 @@ void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
divisible index. Otherwise we get some parts that have infill at divisible
layers and some at non-divisible layers. Those layers would then miss each
other. */
size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
size_t min_layer = mesh.getSettingAsCount("bottom_layers") + amount - 1;
min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
{
SliceLayer* layer = &storage.layers[layer_idx];
for(unsigned int n = 1;n < amount;n++)
SliceLayer* layer = &mesh.layers[layer_idx];
for(unsigned int combine_count_here = 1; combine_count_here < amount; combine_count_here++)
{
if(layer_idx < n)
if(layer_idx < combine_count_here)
{
break;
}
SliceLayer* layer2 = &storage.layers[layer_idx - n];
for(SliceLayerPart& part : layer->parts)
size_t lower_layer_idx = layer_idx - combine_count_here;
if (lower_layer_idx < min_layer)
{
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
{
if(part.boundaryBox.hit(part2.boundaryBox))
break;
}
SliceLayer* lower_layer = &mesh.layers[lower_layer_idx];
for (SliceLayerPart& part : layer->parts)
{
for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++)
{ // go over each density of gradual infill (these density areas overlap!)
std::vector<Polygons>& infill_area_per_combine = part.infill_area_per_combine_per_density[density_idx];
Polygons result;
for (SliceLayerPart& lower_layer_part : lower_layer->parts)
{
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
if (part.boundaryBox.hit(lower_layer_part.boundaryBox))
{
Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.infill_area).offset(-200).offset(200);
result.add(intersection); // add area to be thickened
infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here
if (density_idx < lower_layer_part.infill_area_per_combine_per_density.size())
{ // only remove from *same density* areas on layer below
// If there are no same density areas, then it's ok to print them anyway
// Don't remove other density areas
unsigned int lower_density_idx = density_idx;
std::vector<Polygons>& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx];
lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (thickened) layer
}
}
}
}
part.infill_area.push_back(result);
infill_area_per_combine.push_back(result);
}
}
}
}
}
void generatePerimeterGaps(int layer_nr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
{
SliceLayer& layer = storage.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{ // handle gaps between perimeters etc.
if (downSkinCount > 0 && upSkinCount > 0 && // note: if both are zero or less, then all gaps will be used
layer_nr >= downSkinCount && layer_nr < static_cast<int>(storage.layers.size() - upSkinCount)) // remove gaps which appear within print, i.e. not on the bottom most or top most skin
{
Polygons outlines_above;
for (SliceLayerPart& part_above : storage.layers[layer_nr + upSkinCount].parts)
{
if (part.boundaryBox.hit(part_above.boundaryBox))
{
outlines_above.add(part_above.outline);
}
}
Polygons outlines_below;
for (SliceLayerPart& part_below : storage.layers[layer_nr - downSkinCount].parts)
{
if (part.boundaryBox.hit(part_below.boundaryBox))
{
outlines_below.add(part_below.outline);
}
}
part.perimeterGaps = part.perimeterGaps.intersection(outlines_above.xorPolygons(outlines_below));
}
part.perimeterGaps.removeSmallAreas(MIN_AREA_SIZE);
}
}
}//namespace cura
+47 -35
Ver Arquivo
@@ -6,59 +6,48 @@
namespace cura
{
/*!
* Generate the gap areas which occur between consecutive insets.
*
* \param layerNr The index of the layer for which to generate the gaps.
* \param storage The storage where the layer outline information (input) is stored and where the gap areas (output) are stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom gaps
* \param upSkinCount The number of layers of top gaps
*/
void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
/*!
* Generate the skin areas and its insets.
*
* \param layerNr The index of the layer for which to generate the skins.
* \param storage The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
* \param extrusionWidth extrusionWidth
* \param mesh The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param innermost_wall_extrusion_width The line width of the inner most wall
* \param innermost_wall_line_width The line width of the inner most wall
* \param insetCount The number of perimeters to surround the skin
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
*/
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
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);
/*!
* Generate the skin areas (outlines)
*
* \param layerNr The index of the layer for which to generate the skins.
* \param storage The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
* \param mesh The storage where the layer outline information (input) is stored
* and where the skin outline (output) is stored.
* \param innermost_wall_line_width The line width of the walls around the skin, by which
* we must inset for each wall.
* \param downSkinCount The number of layers of bottom skin.
* \param upSkinCount The number of layers of top skin.
* \param wall_line_count The number of walls, i.e. the number of the wall from
* which to offset.
* \param no_small_gaps_heuristic A heuristic which assumes there will be no
* small gaps between bottom and top skin with a z size smaller than the skin
* size itself.
*/
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic);
void generateSkinAreas(int layerNr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic);
/*!
* Generate the skin insets.
*
* \param layerNr The index of the layer for which to generate the skins.
* \param part The part where the skin outline information (input) is stored and where the skin insets (output) are stored.
* \param extrusionWidth extrusionWidth
* \param insetCount The number of perimeters to surround the skin
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
* \param part The part where the skin outline information (input) is stored and
* where the skin insets (output) are stored.
* \param wall_line_width The width of the perimeters around the skin.
* \param insetCount The number of perimeters to surround the skin.
*/
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount);
/*!
* Generate Infill by offsetting from the last wall.
@@ -68,12 +57,13 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
* After this function has been called on a layer of a mesh, each SliceLayerPart of that layer should have an infill_area consisting of exactly one Polygons : the normal uncombined infill area.
*
* \param layerNr The index of the layer for which to generate the infill
* \param mesh The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
* \param innermost_wall_extrusion_width width of the innermost wall lines
* \param innermost_wall_line_width width of the innermost wall lines
* \param infill_skin_overlap overlap distance between infill and skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
*/
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
void generateInfill(int layerNr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int infill_skin_overlap, int wall_line_count);
/*!
* \brief Combines the infill of multiple layers for a specified mesh.
@@ -82,10 +72,32 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_e
* multiplied such that the infill should fill up again to the full height of
* all combined layers.
*
* \param storage The mesh to combine the infill layers of.
* \param mesh The mesh to combine the infill layers of.
* \param amount The number of layers to combine.
*/
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount);
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount);
/*!
* Class containing all skin and infill area computation functions
*/
class SkinInfillAreaComputation
{
public:
/*!
* Generate infill areas which cause a gradually less dense infill structure from top to bottom.
*
* The areas generated overlap, so that more dense infill adds on to less dense infill.
* That way you don't have infill lines which are broken when they cross a border between separated infill areas - if they would be as such.
*
* This function also guarantees that the SliceLayerPart::infill_area_per_combine_per_density is initialized with at least one item.
* The last item in the list will be equal to the infill_area after this function.
*
* \param gradual_infill_step_height // The height difference between consecutive density infill areas
* \param max_infill_steps the maximum exponent of division of infill density. At 5 the least dense infill will be 2^4 * infill_line_distance i.e. one 16th as dense
*/
static void generateGradualInfill(SliceMeshStorage& mesh, unsigned int gradual_infill_step_height, unsigned int max_infill_steps);
};
}//namespace cura
-89
Ver Arquivo
@@ -1,89 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skirt.h"
#include "support.h"
#include <queue>
namespace cura
{
void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength)
{
if (count == 0) return;
bool externalOnly = (distance > 0); // whether to include holes or not
int primary_extruder = 0; // TODO allow for other extruder to be primary
int primary_extrusion_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");
Polygons& skirt_primary_extruder = storage.skirt[primary_extruder];
bool get_convex_hull = count == 1 && distance > 0;
Polygons first_layer_outline = storage.getLayerOutlines(0, true, externalOnly);
std::vector<Polygons> skirts;
for(int skirtNr=0; skirtNr<count;skirtNr++)
{
int offsetDistance = distance + primary_extrusion_width * skirtNr + primary_extrusion_width / 2;
skirts.emplace_back(first_layer_outline.offset(offsetDistance, ClipperLib::jtRound));
Polygons& skirt_polygons = skirts.back();
//Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for(unsigned int n=0; n<skirt_polygons.size(); n++)
{
double area = skirt_polygons[n].area();
if (area < 0 && area > -primary_extrusion_width * primary_extrusion_width * 100)
skirt_polygons.remove(n--);
}
if (get_convex_hull)
{
skirt_polygons = skirt_polygons.convexHull();
}
skirt_primary_extruder.add(skirt_polygons);
int length = skirt_primary_extruder.polygonLength();
if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
count++;
}
if (false) // the code below is for the old prime tower
{ //Add a skirt UNDER the prime tower to make it stick better.
Polygons prime_tower = storage.primeTower.ground_poly.offset(-primary_extrusion_width / 2);
std::queue<Polygons> prime_tower_insets;
while(prime_tower.size() > 0)
{
prime_tower_insets.emplace(prime_tower);
prime_tower = prime_tower.offset(-primary_extrusion_width);
}
while (!prime_tower_insets.empty())
{
Polygons& inset = prime_tower_insets.back();
skirt_primary_extruder.add(inset);
prime_tower_insets.pop();
}
}
{ // process other extruders' brim/skirt (as one brim line around the old brim)
int offset_distance = 0;
int last_width = primary_extrusion_width;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{
if (extruder == primary_extruder) { continue; }
int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
offset_distance += last_width / 2 + width/2;
last_width = width;
while (storage.skirt[extruder].polygonLength() < minLength)
{
storage.skirt[extruder].add(skirts.back().offset(offset_distance, ClipperLib::jtRound));
offset_distance += width;
}
}
}
}
}//namespace cura
-22
Ver Arquivo
@@ -1,22 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIRT_H
#define SKIRT_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Generate skirt or brim (depending on parameters); when \p distance > 0 and \p count == 1 the skirt is generated, which has slighly different configuration.
*
* \param storage Storage containing the parts at the first layer
* \param distance The distance of the first outset from the parts at the first layer
* \param count Number of outsets / brim lines
* \param minLength The minimum length the skirt should have (enforced by taking more outsets)
*/
void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength);
}//namespace cura
#endif//SKIRT_H
+116 -35
Ver Arquivo
@@ -5,47 +5,59 @@
namespace cura
{
Polygons SliceLayer::getOutlines(bool external_polys_only)
Polygons& SliceLayerPart::getOwnInfillArea()
{
if (infill_area_own)
{
return *infill_area_own;
}
else
{
return infill_area;
}
}
Polygons SliceLayer::getOutlines(bool external_polys_only) const
{
Polygons ret;
getOutlines(ret, external_polys_only);
return ret;
}
void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
{
for (SliceLayerPart& part : parts)
for (const SliceLayerPart& part : parts)
{
if (external_polys_only)
{
result.add(part.outline.outerPolygon());
result.add(const_cast<SliceLayerPart&>(part).outline.outerPolygon()); // TODO: make a const version of outerPolygon()
}
else
{
result.add(part.outline);
result.add(part.print_outline);
}
}
}
Polygons SliceLayer::getSecondOrInnermostWalls()
Polygons SliceLayer::getSecondOrInnermostWalls() const
{
Polygons ret;
getSecondOrInnermostWalls(ret);
return ret;
}
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
{
for (SliceLayerPart& part : parts)
for (const SliceLayerPart& part : parts)
{
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(part.insets[1]);
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[1]); // TODO const cast!
continue;
}
// but we'll also take the inner wall if the 2nd doesn't exist
if (part.insets.size() == 1) {
layer_walls.add(part.insets[0]);
layer_walls.add(const_cast<SliceLayerPart&>(part).insets[0]); // TODO const cast!
continue;
}
// offset_from_outlines was so large that it completely destroyed our isle,
@@ -55,24 +67,51 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
}
}
std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
{
std::vector<RetractionConfig> ret;
ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeTravelConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
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.
retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config(&retraction_config, PrintFeatureType::MoveCombing),
skirt_config(initializeSkirtConfigs()),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_extruder_nr")], PrintFeatureType::Support),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
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)
{
}
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
{
if (layer_nr < 0)
if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
{ // when processing raft
if (include_helper_parts)
{
@@ -99,21 +138,28 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
else
{
Polygons total;
for (SliceMeshStorage& mesh : meshes)
if (layer_nr >= 0)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
for (const SliceMeshStorage& mesh : meshes)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
if (mesh.getSettingBoolean("infill_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??
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
}
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
}
@@ -121,9 +167,9 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
}
}
Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts)
Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const
{
if (layer_nr < 0)
if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
{ // when processing raft
if (include_helper_parts)
{
@@ -137,21 +183,24 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
else
{
Polygons total;
for (SliceMeshStorage& mesh : meshes)
if (layer_nr >= 0)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(total);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
for (const SliceMeshStorage& mesh : meshes)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getSecondOrInnermostWalls(total);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make getSetting const? make settings.setting_values mapping mutable??
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
}
}
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
total.add(support.supportLayers[std::max(0, layer_nr)].skin);
}
total.add(primeTower.ground_poly);
}
@@ -160,7 +209,39 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt_brim[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield ..?
// support
// support is presupposed to be present...
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
ret[getSettingAsIndex("support_interface_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
return ret;
}
+107 -62
Ver Arquivo
@@ -3,12 +3,15 @@
#define SLICE_DATA_STORAGE_H
#include "utils/intpoint.h"
#include "utils/optional.h"
#include "utils/polygon.h"
#include "utils/NoCopy.h"
#include "utils/AABB.h"
#include "mesh.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "PrimeTower.h"
#include "GCodePathConfig.h"
namespace cura
{
@@ -16,13 +19,12 @@ namespace cura
* A SkinPart is a connected area designated as top and/or bottom skin.
* Surrounding each non-bridged skin area with an outline may result in better top skins.
* It's filled during FffProcessor.processSliceData(.) and used in FffProcessor.writeGCode(.) to generate the final gcode.
*/
*/
class SkinPart
{
public:
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
PolygonsPart outline; //!< The skinOutline is the area which needs to be 100% filled to generate a proper top&bottom filling. It's filled by the "skin" module.
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
/*!
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
@@ -34,10 +36,44 @@ class SliceLayerPart
public:
AABB boundaryBox; //!< The boundaryBox is an axis-aligned bounardy box which is used to quickly check for possible collision between different parts on different layers. It's an optimalization used during skin calculations.
PolygonsPart outline; //!< The outline is the first member that is filled, and it's filled with polygons that match a cross section of the 3D model. The first polygon is the outer boundary polygon and the rest are holes.
Polygons print_outline; //!< An approximation to the outline of what's actually printed, based on the outer wall. Too small parts will be omitted compared to the outline.
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
/*!
* The areas inside of the mesh.
* Like SliceLayerPart::outline, this class member is not used to actually determine the feature area,
* but is used to compute the inside comb boundary.
*/
Polygons infill_area;
/*!
* The areas which need to be filled with sparse (0-99%) infill.
* Like SliceLayerPart::outline, this class member is not used to actually determine the feature area,
* but is used to compute the infill_area_per_combine_per_density.
*
* These polygons may be cleared once they have been used to generate gradual infill and/or infill combine.
*
* If these polygons are not initialized, simply use the normal infill area.
*/
std::optional<Polygons> infill_area_own;
/*!
* The areas which need to be filled with sparse (0-99%) infill for different thicknesses.
* The infill_area is an array to support thicker layers of sparse infill and areas of different infill density.
* infill_area[x][n] is infill_area of (n+1) layers thick.
*
* infill_area[0] corresponds to the most dense infill area.
* infill_area[x] will lie fully inside infill_area[x+1].
* infill_area_per_combine_per_density.back()[0] == part.infill area initially
*/
std::vector<std::vector<Polygons>> infill_area_per_combine_per_density;
/*!
* Get the infill_area_own (or when it's not instantiated: the normal infill_area)
* \see SliceLayerPart::infill_area_own
* \return the own infill area
*/
Polygons& getOwnInfillArea();
};
/*!
@@ -51,15 +87,15 @@ public:
int printZ; //!< The height at which this layer needs to be printed. Can differ from sliceZ due to the raft.
std::vector<SliceLayerPart> parts; //!< An array of LayerParts which contain the actual data. The parts are printed one at a time to minimize travel outside of the 3D model.
Polygons openPolyLines; //!< A list of lines which were never hooked up into a 2D polygon. (Currently unused in normal operation)
/*!
* Get the all outlines of all layer parts in this layer.
*
* \param external_polys_only Whether to only include the outermost outline of each layer part
* \return A collection of all the outline polygons
*/
Polygons getOutlines(bool external_polys_only = false);
Polygons getOutlines(bool external_polys_only = false) const;
/*!
* Get the all outlines of all layer parts in this layer.
* Add those polygons to @p result.
@@ -67,20 +103,20 @@ public:
* \param external_polys_only Whether to only include the outermost outline of each layer part
* \param result The result: a collection of all the outline polygons
*/
void getOutlines(Polygons& result, bool external_polys_only = false);
void getOutlines(Polygons& result, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* \return The collection of all polygons thus obtained
*/
Polygons getSecondOrInnermostWalls();
Polygons getSecondOrInnermostWalls() const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
* Add those polygons to @p result.
* \param result The result: the collection of all polygons thus obtained
*/
void getSecondOrInnermostWalls(Polygons& result);
void getSecondOrInnermostWalls(Polygons& result) const;
};
/******************/
@@ -88,16 +124,16 @@ class SupportLayer
{
public:
Polygons supportAreas; //!< normal support areas
Polygons roofs; //!< the support areas which are to be printed as denser roofs. Note that the roof areas and support areas are mutually exclusive.
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.
};
class SupportStorage
{
public:
bool generated; //!< whether generateSupportGrid(.) has completed (successfully)
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
std::vector<SupportLayer> supportLayers;
SupportStorage() : generated(false), layer_nr_max_filled_layer(-1) { }
@@ -110,20 +146,24 @@ class SliceMeshStorage : public SettingsMessenger // passes on settings from a M
public:
std::vector<SliceLayer> layers;
int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content
RetractionConfig retraction_config;
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;
SliceMeshStorage(SettingsBaseVirtual* settings)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
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)
{
layers.reserve(slice_layer_count);
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
infill_config.emplace_back(PrintFeatureType::Infill);
}
};
@@ -131,53 +171,51 @@ class SliceDataStorage : public SettingsMessenger, NoCopy
{
public:
MeshGroup* meshgroup; // needed to pass on the per extruder settings.. (TODO: put this somewhere else? Put the per object settings here directly, or a pointer only to the per object settings.)
Point3 model_size, model_min, model_max;
std::vector<SliceMeshStorage> meshes;
std::vector<RetractionConfig> retraction_config_per_extruder; //!< used for support, skirt, etc.
RetractionConfig retraction_config; //!< The retraction config used as fallback when getting the per_extruder_config or the mesh config was impossible (for travelConfig)
GCodePathConfig travel_config; //!< The config used for travel moves (only the speed and retraction config are set!)
std::vector<GCodePathConfig> skirt_config; //!< config for skirt per extruder
std::vector<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_roof_config;
GCodePathConfig support_skin_config; //!< The config to use to print the dense roofs and bottoms of support
SupportStorage support;
Polygons skirt[MAX_EXTRUDERS]; //!< Skirt polygons per extruder, ordered from inner to outer polygons
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
PrimeTower primeTower;
std::vector<Polygons> oozeShield; //oozeShield per layer
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
Point wipePoint;
std::vector<RetractionConfig> initializeRetractionConfigs()
{
std::vector<RetractionConfig> ret;
ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
return ret;
}
std::vector<GCodePathConfig> initializeSkirtConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
}
return ret;
}
/*!
* 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.
@@ -189,11 +227,11 @@ public:
* \param meshgroup The mesh group to load into this data storage, if any.
*/
SliceDataStorage(MeshGroup* meshgroup);
~SliceDataStorage()
{
}
/*!
* Get all outlines within a given layer.
*
@@ -201,8 +239,8 @@ public:
* \param include_helper_parts whether to include support and prime tower
* \param external_polys_only whether to disregard all hole polygons
*/
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false);
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false) const;
/*!
* Collects the second wall of every part, or the outer wall if it has no second, or the outline, if it has no outer wall.
*
@@ -211,7 +249,14 @@ public:
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
* \param include_helper_parts whether to include support and prime tower
*/
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts);
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const;
/*!
* Get the extruders used.
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
};
}//namespace cura
+784 -269
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+468 -105
Ver Arquivo
@@ -2,6 +2,8 @@
#ifndef SLICER_H
#define SLICER_H
#include <queue>
#include "mesh.h"
#include "utils/polygon.h"
/*
@@ -14,8 +16,13 @@ class SlicerSegment
{
public:
Point start, end;
int faceIndex;
bool addedToPolygon;
int faceIndex = -1;
// The index of the other face connected via the edge that created end
int endOtherFaceIdx = -1;
// If end corresponds to a vertex of the mesh, then this is populated
// with the vertex that it ended on.
const MeshVertex *endVertex = nullptr;
bool addedToPolygon = false;
};
class ClosePolygonResult
@@ -23,138 +30,494 @@ class ClosePolygonResult
//The line on which the point lays is between pointIdx-1 and pointIdx
public:
Point intersectionPoint;
int polygonIdx;
unsigned int pointIdx;
int polygonIdx = -1;
unsigned int pointIdx = -1;
};
class GapCloserResult
{
public:
int64_t len;
int polygonIdx;
unsigned int pointIdxA;
unsigned int pointIdxB;
bool AtoB;
int64_t len = -1;
int polygonIdx = -1;
unsigned int pointIdxA = -1;
unsigned int pointIdxB = -1;
bool AtoB = false;
};
class SlicerLayer
{
public:
std::vector<SlicerSegment> segmentList;
std::unordered_map<int, int> face_idx_to_segment_index; // topology
int z;
Polygons polygonList;
std::vector<SlicerSegment> segments;
std::unordered_map<int, int> face_idx_to_segment_idx; // topology
int z = -1;
Polygons polygons;
Polygons openPolylines;
void makePolygons(Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
/*!
* Connect the segments into polygons for this layer of this \p mesh
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param keepNoneClosed Whether to throw away the data for segments which we couldn't stitch into a polygon
* \param extensiveStitching Whether to perform extra work to try and close polylines into polygons when there are large gaps
*/
void makePolygons(const Mesh* mesh, bool keepNoneClosed, bool extensiveStitching);
protected:
/*!
* Connect the segments into loops which correctly form polygons (don't perform stitching here)
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
*/
void makeBasicPolygonLoops(const Mesh* mesh, Polygons& open_polylines);
/*!
* Connect the segments into a loop, starting from the segment with index \p start_segment_idx
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
* \param[in] start_segment_idx The index into SlicerLayer::segments for the first segment from which to start the polygon loop
*/
void makeBasicPolygonLoop(const Mesh* mesh, Polygons& open_polylines, unsigned int start_segment_idx);
/*!
* Get the next segment connected to the end of \p segment.
* Used to make closed polygon loops.
* Return ASAP if segment is (also) connected to SlicerLayer::segments[\p start_segment_idx]
*
* \param[in] mesh The mesh data for which we are connecting sliced segments (The face data is used)
* \param[in] segment The segment from which to start looking for the next
* \param[in] start_segment_idx The index to the segment which when conected to \p segment will immediately stop looking for further candidates.
*/
int getNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment, unsigned int start_segment_idx);
/*!
* Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons.
* First link up polygon ends that are within 2 microns.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop
*/
void connectOpenPolylines(Polygons& open_polylines);
/*!
* Link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop yet
*/
void stitch(Polygons& open_polylines);
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1);
ClosePolygonResult findPolygonPointClosestTo(Point input);
/*!
* Try to close up polylines into polygons while they have large gaps in them.
*
* Clears all open polylines which are used up in the process
*
* \param[in,out] open_polylines The polylines which are stiched, but couldn't be closed into a loop yet
*/
void stitch_extensive(Polygons& open_polylines);
private:
GapCloserResult findPolygonGapCloser(Point ip0, Point ip1)
/*!
* \brief This class represents the location of an end point of a
* polyline in a polyline vector.
*
* The location records the index in the polyline vector and
* whether this is the vertex at the start of the polyline or the
* vertex at the end.
*/
class Terminus
{
GapCloserResult ret;
ClosePolygonResult c1 = findPolygonPointClosestTo(ip0);
ClosePolygonResult c2 = findPolygonPointClosestTo(ip1);
if (c1.polygonIdx < 0 || c1.polygonIdx != c2.polygonIdx)
{
ret.len = -1;
return ret;
}
ret.polygonIdx = c1.polygonIdx;
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
ret.len = vSize(ip0 - ip1);
}else{
//Find out if we have should go from A to B or the other way around.
Point p0 = polygonList[ret.polygonIdx][ret.pointIdxA];
int64_t lenA = vSize(p0 - ip0);
for(unsigned int i = ret.pointIdxA; i != ret.pointIdxB; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenA += vSize(p0 - p1);
p0 = p1;
}
lenA += vSize(p0 - ip1);
public:
/*! A representation of Terminus that can be used as an array index.
*
* See \ref asIndex() for more information.
*/
using Index = size_t;
p0 = polygonList[ret.polygonIdx][ret.pointIdxB];
int64_t lenB = vSize(p0 - ip1);
for(unsigned int i = ret.pointIdxB; i != ret.pointIdxA; i = (i + 1) % polygonList[ret.polygonIdx].size())
{
Point p1 = polygonList[ret.polygonIdx][i];
lenB += vSize(p0 - p1);
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
ret.len = lenA;
}else{
ret.AtoB = false;
ret.len = lenB;
}
}
return ret;
}
/*! A Terminus value representing an invalid value.
*
* This is used to record when Terminus are removed.
*/
static const Terminus INVALID_TERMINUS;
ClosePolygonResult findPolygonPointClosestTo(Point input)
/*! Constructor leaving uninitialized. */
Terminus()
{}
/*! Constructor from Index representation.
*
* Terminus{t.asIndex()} == t for all Terminus t.
*/
Terminus(Index idx)
{
m_idx = idx;
}
/*! Constuctor from the polyline index and which end of the polyline.
*
* Terminus{t.getPolylineIdx(), t.isEnd()} == t for all Terminus t.
*/
Terminus(size_t polyline_idx, bool is_end)
{
m_idx = polyline_idx * 2 + (is_end ? 1 : 0);
}
/*! Gets the polyline index for this Terminus. */
size_t getPolylineIdx() const
{
return m_idx / 2;
}
/*! Gets whether this Terminus represents the end point of the polyline. */
bool isEnd() const
{
return (m_idx & 1) == 1;
}
/*! Gets the Index representation of this Terminus.
*
* The index representation much satisfy the following:
* 1. for all Terminus t0, t1: t0 == t1 implies t0.asIndex() == t1.asIndex()
* 2. for all Terminus t0, t1: t0 != t1 implies t0.asIndex() != t1.asIndex()
* 3. t0.asIndex() >= 0
* 4. if y = \ref endIndexFromPolylineEndIndex(x), then for all Terminus t
* if t.getPolylineIdx() < x then t.asIndex() < y
*
* In addition, the Index representation should be reasonably
* compact for efficiency. This means that for polyline index
* in [0,x) and Terminus t with t.getPolylineIdx() < x, the
* set of containing all t.asIndex() union {0} should be
* small. In other words, t.asIndex() should map to [0,y)
* where y is as small as possible.
*/
Index asIndex() const
{
return m_idx;
}
/*! Calculates the Terminus end Index from the polyline vector end index.
*
* \param[in] polyline_end_idx The index of the first invalid
* element of the polyline vector.
* \return The Index for the first invalid Terminus for the polyline
* vector.
*/
static Index endIndexFromPolylineEndIndex(unsigned int polyline_end_idx)
{
return polyline_end_idx*2;
}
/*! Tests for equality.
*
* Two Terminus are equal if they return the same results for
* \ref getPolylineIdx() and \ref isEnd().
*/
bool operator==(const Terminus &other)
{
return m_idx == other.m_idx;
}
/*! Tests for inequality. */
bool operator!=(const Terminus &other)
{
return m_idx != other.m_idx;
}
private:
/*! The Index representation of the Terminus.
*
* The polyline_idx and end flags are calculated from this on demand.
*/
Index m_idx = -1;
};
/*!
* \brief Represents a possible stitch between two polylines.
*
* This represents the possibility of creating a new merged
* polyline from appending terminus_1.getPolylineIdx() onto
* terminus_0.getPolylineIdx() using the Terminus points as the
* join point. Consider polylines A -> B and C -> D. If
* terminus_0 is B and terminus_1 is C, then this stitch
* represents A -> B -> C -> D. If terminus_0 is C and terminus_1
* is A, then this stitch represents D -> C -> A -> B. In
* general, this stitch represents the polyline:
* the other terminus of polyline 0 -> terminus_0 -> terminus_1
* -> the other terminus of polyline 1.
*
* This class also stores the squared distance involved in making
* the stitch.
*/
struct PossibleStitch
{
ClosePolygonResult ret;
for(unsigned int n=0; n<polygonList.size(); n++)
/*! Squared distance from terminus_0 to terminus_1. */
int64_t dist2 = -1;
/*! The Terminus representing the end of polyline_0 where the
* join would happen. */
Terminus terminus_0;
/*! The Terminus representing the end of polyline_1 where the
* join would happen. */
Terminus terminus_1;
/*! True if this stitch doesn't require any polyline reversals.
*
* If this is true, then the polylines can be appended using
* their natural order.
*/
bool in_order() const
{
Point p0 = polygonList[n][polygonList[n].size()-1];
for(unsigned int i=0; i<polygonList[n].size(); i++)
{
Point p1 = polygonList[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
if (lineLength > 1)
{
int64_t distOnLine = dot(pDiff, input - p0) / lineLength;
if (distOnLine >= 0 && distOnLine <= lineLength)
{
Point q = p0 + pDiff * distOnLine / lineLength;
if (shorterThen(q - input, 100))
{
ret.intersectionPoint = q;
ret.polygonIdx = n;
ret.pointIdx = i;
return ret;
}
}
}
p0 = p1;
}
// in order if using back of line 0 and front of line 1
return terminus_0.isEnd() &&
!terminus_1.isEnd();
}
ret.polygonIdx = -1;
return ret;
}
/*! Orders PossibleStitch by goodness.
*
* Better PossibleStitch are > then worse PossibleStitch.
* priority_queue will give greatest first so greatest
* must be most desirable stitch
*/
bool operator<(const PossibleStitch &other) const;
};
/*!
* \brief Tracks movements of polyline end point locations (Terminus).
*
* Tracks the movement of polyline end point locations within the
* polyline vector as polylines are joined, reversed, and used to
* form polygons.
*/
class TerminusTrackingMap
{
public:
/*! Initializes the TerminusTrackingMap with the size indicated.
*
* \param end_idx The first invalid Terminus::Index. This usually
* comes from \ref Terminus::endIndexFromPolylineEndIndex().
*/
TerminusTrackingMap(Terminus::Index end_idx);
/*! Given the old Terminus location returns the current location.
*
* If the old location is no longer the endpoint of a polyline
* in the polyline vector, then this returns
* Terminus::INVALID_TERMINUS. As long as the old location is
* still an endpoint in the polyline vector, then
* getCurFromOld(old) will always refer to the same point.
* Endpoints are removed from the polyline vector as polylines
* are merged or converted to Polygons.
*
* \param old The old Terminus location. Must not be
* INVALID_TERMINUS.
* \return The current Terminus location or INVALID_TERMINUS
* if the old endpoint is no longer an endpoint.
*/
Terminus getCurFromOld(const Terminus &old) const
{
return m_terminus_old_to_cur_map[old.asIndex()];
}
/*! Given the current Terminus location returns the old location.
*
* \param cur The current Terminus location. Must not be
* INVALID_TERMINUS.
* \return The old Terminus location. Returns
* INVALID_TERMINUS if the old Terminus location was
* removed (used to form a Polygon).
*/
Terminus getOldFromCur(const Terminus &cur) const
{
return m_terminus_cur_to_old_map[cur.asIndex()];
}
/*! Mark the current Terminus as being removed.
*
* This marks the current Terminus as being removed from the
* polyline vector.
*/
void markRemoved(const Terminus &cur)
{
Terminus old = getOldFromCur(cur);
m_terminus_old_to_cur_map[old.asIndex()] = Terminus::INVALID_TERMINUS;
m_terminus_cur_to_old_map[cur.asIndex()] = Terminus::INVALID_TERMINUS;
}
/*! Update the map for movement of Terminus.
*
* This updates the map for the movement / removal of Terminus
* locations. next_terms[i] should refer to the same point as
* cur_terms[i] for i < num_terms, unless the Terminus was
* removed. If the Terminus was removed, next_terms[i] should
* be INVALID_TERMINUS.
*
* removed_cur_terms should refer to those Terminus that are
* no longer present after the update. removed_cur_terms
* should be the set of terminus values that are in cur_terms
* but not in next_terms, i.e. viewing the inputs as sets:
* removed_cur_terms = next_terms - cur_terms. It is passed
* separately to avoid calculating the set difference since
* the caller generally has this information readily
* available.
*
* \param num_terms The number of Terminus that changed.
* \param cur_terms The current Terminus locations. Must be
* of size num_terms. Must not contain INVALID_TERMINUS.
* \param next_terms The Terminus locations after the update.
* Must be of size num_terms. A value of INVALID_TERMINUS
* indicates that the Terminus was removed.
* \param num_removed_terms The number of Terminus locations
* that are being removed by the update.
* \param removed_cur_terms The Terminus locations that will
* be removed after the update.
*/
void updateMap(size_t num_terms,
const Terminus *cur_terms, const Terminus *next_terms,
size_t num_removed_terms,
const Terminus *removed_cur_terms);
private:
/*! map from old terminus location to current terminus location */
std::vector<Terminus> m_terminus_old_to_cur_map;
/*! map from current terminus location to old terminus location */
std::vector<Terminus> m_terminus_cur_to_old_map;
};
/*!
* Try to find a segment from face \p face_idx to continue \p segment.
*
* \param[in] mesh The mesh being sliced.
* \param[in] segment The previous segment that we want to find a continuation for.
* \param[in] face_idx The index of the face that might have generated a continuation segment.
* \param[in] start_segment_idx The index of the segment that started this polyline.
*/
int tryFaceNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment,
int face_idx, unsigned int start_segment_idx) const;
/*!
* Find possible allowed stitches in goodness order.
*
* This finds all stitches that are allowed by the parameters.
* The stitches are returned in a priority_queue that returns them
* in order from best to worst stitch.
*
* \param open_polylines The polylines to try to stitch together.
* \param max_dist The maximum distance between end points for an
* allowed stitch.
* \param cell_size The cell size to use for the SparsePointGridInclusive. This
* affects speed, but does not otherwise affect the results.
* This value should generally be close to max_dist.
* \param allow_reverse Whether stitches are allowed that reverse
* the order of a polyline.
* \return The stitches that are allowed in order from best to worst.
*/
std::priority_queue<PossibleStitch> findPossibleStitches(
const Polygons& open_polylines, coord_t max_dist, coord_t cell_size,
bool allow_reverse) const;
/*! Plans the best way to perform a stitch.
*
* Let polyline_0 be open_polylines[terminus_0.getPolylineIdx()] and
* polyline_1 be open_polylines[terminus_1.getPolylineIdx()].
*
* The plan consists of appending polyline_1 to polyline_0. If
* reverse[0] is true, then polyline_0 should be reversed before
* appending. If reverse[1] is true, then polyline_1 should be
* reversed before appending. Note that terminus_0 and terminus_1
* may be swapped by this function.
*
* \param[in] open_polylines The polyline storage vector.
* \param[in,out] terminus_0 the Terminus on polyline_0 to join at.
* \param[in,out] terminus_1 the Terminus on polyline_1 to join at.
* \param[out] reverse Whether the polylines need to be reversed.
*/
void planPolylineStitch(const Polygons& open_polylines,
Terminus& terminus_0, Terminus& terminus_1,
bool reverse[2]) const;
/*! Joins polyline_1 onto polyline_0.
*
* Appends polyline_1 to polyline_0. It reverses the polylines first if either
* reverse[i] is true. Clears polyline_1.
*
* \param[in,out] polyline_0 On input, the polyline that will form
* the first part of the joined polyline. On output, the
* joined polyline.
* \param[in,out] polyline_1 On input, the polyline that will form
* the second of the joined polyline. On output, an empty
* polyline.
* \param[in] reverse Whether to reverse the polylines before
* joining. reverse[0] indicates whether to reverse
* polyline_0 and reverse[1] indicates whether to reverse
* polyline_1
*/
void joinPolylines(PolygonRef& polyline_0, PolygonRef& polyline_1,
const bool reverse[2]) const;
/*!
* Connecting polylines that are not closed yet.
*
* Any polylines that are closed by this function are added to
* this->polygons. All possible polyline joins that meet the
* distance and reversal criteria will be performed. This
* function will not introduce any copies of the same polyline
* segment.
*
* \param[in,out] open_polylines The polylines which couldn't be
* closed into a loop
* \param[in] max_dist The maximum distance that polyline ends can
* be separated and still be joined.
* \param[in] cell_size The cell size to use internally in the
* grid. This affects speed but not results.
* \param[in] allow_reverse If true, then this function is allowed
* to reverse edge directions to merge polylines.
*/
void connectOpenPolylinesImpl(Polygons& open_polylines,
coord_t max_dist, coord_t cell_size,
bool allow_reverse);
};
class Slicer
{
public:
std::vector<SlicerLayer> layers;
Slicer(Mesh* mesh, int initial, int thickness, int layer_count, bool keepNoneClosed, bool extensiveStitching);
const Mesh* mesh = nullptr; //!< The sliced mesh
Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keepNoneClosed, bool extensiveStitching);
/*!
* Linear interpolation
*
* Get the Y of a point with X \p x in the line through (\p x0, \p y0) and (\p x1, \p y1)
*/
int64_t interpolate(int64_t x, int64_t x0, int64_t x1, int64_t y0, int64_t y1) const
{
int64_t dx_01 = x1 - x0;
int64_t num = (y1 - y0) * (x - x0);
num += num > 0 ? dx_01/2 : -dx_01/2; // add in offset to round result
int64_t y = y0 + num / dx_01;
return y;
}
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
{
SlicerSegment seg;
seg.start.X = p0.x + int64_t(p1.x - p0.x) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.start.Y = p0.y + int64_t(p1.y - p0.y) * int64_t(z - p0.z) / int64_t(p1.z - p0.z);
seg.end.X = p0.x + int64_t(p2.x - p0.x) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
seg.end.Y = p0.y + int64_t(p2.y - p0.y) * int64_t(z - p0.z) / int64_t(p2.z - p0.z);
seg.start.X = interpolate(z, p0.z, p1.z, p0.x, p1.x);
seg.start.Y = interpolate(z, p0.z, p1.z, p0.y, p1.y);
seg.end .X = interpolate(z, p0.z, p2.z, p0.x, p2.x);
seg.end .Y = interpolate(z, p0.z, p2.z, p0.y, p2.y);
return seg;
}
void dumpSegmentsToHTML(const char* filename);
};
+214 -104
Ver Arquivo
@@ -1,15 +1,19 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "support.h"
#include <cmath> // sqrt
#include <utility> // pair
#include "Progress.h"
#include <deque>
#include <cmath> // round
#include "support.h"
#include "utils/math.h"
#include "progress/Progress.h"
namespace cura
{
Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_this, int64_t supportJoinDistance, int64_t smoothing_distance, int min_smoothing_area, bool conical_support, int64_t conical_support_offset, int64_t conical_smallest_breadth)
Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_this, int64_t supportJoinDistance, int64_t smoothing_distance, int max_smoothing_angle, bool conical_support, int64_t conical_support_offset, int64_t conical_smallest_breadth)
{
Polygons joined;
if (conical_support)
@@ -29,9 +33,33 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
joined = joined.offset(supportJoinDistance)
.offset(-supportJoinDistance);
}
if (smoothing_distance > 0)
joined = joined.smooth(smoothing_distance, min_smoothing_area);
// remove jagged line pieces introduced by unioning separate overhang areas for consectuive layers
//
// support may otherwise look like:
// _____________________ .
// / \ } dist_from_lower_layer
// /__ __\ /
// /''--...........--''\ `\ .
// / \ } dist_from_lower_layer
// /__ __\ ./
// /''--...........--''\ `\ .
// / \ } dist_from_lower_layer
// /_______________________\ ,/
// rather than
// _____________________
// / \ .
// / \ .
// | |
// | |
// | |
// | |
// | |
// |_______________________|
//
// dist_from_lower_layer may be up to max_dist_from_lower_layer (see below), but that value may be extremely high
joined = joined.smooth_outward(max_smoothing_angle, smoothing_distance);
return joined;
}
@@ -44,13 +72,17 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh"))
{
continue;
}
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
if (mesh.getSettingBoolean("support_roof_enable"))
if (mesh.getSettingBoolean("support_interface_enable"))
{
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
generateSupportInterface(storage, mesh, supportAreas, layer_count);
}
else
{
@@ -63,10 +95,9 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
{
storage.support.supportLayers[layer_idx].supportAreas = storage.support.supportLayers[layer_idx].supportAreas.unionPolygons();
Polygons& support_areas = storage.support.supportLayers[layer_idx].supportAreas;
support_areas = support_areas.unionPolygons();
}
storage.support.generated = true;
}
/*
@@ -85,55 +116,65 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
// given settings
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
ESupportType support_type = storage.getSettingAsSupportType("support_type");
if (!mesh.getSettingBoolean("support_enable"))
return;
if (support_type == ESupportType::NONE)
return;
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
int join_distance = mesh.getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing");
int extension_offset = mesh.getSettingInMicrons("support_offset");
int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
int z_layer_distance_tower = 1; // start tower directly below overhang point
int layerThickness = mesh.getSettingInMicrons("layer_height");
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
if (conical_support_angle == 0)
{
conical_support = false;
}
const double supportAngle = mesh.getSettingInAngleRadians("support_angle");
const bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
const int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
const int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
const int join_distance = mesh.getSettingInMicrons("support_join_distance");
const int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
const int extension_offset = mesh.getSettingInMicrons("support_offset");
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 int layerThickness = storage.getSettingInMicrons("layer_height");
const int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
const int support_xy_distance_overhang = mesh.getSettingInMicrons("support_xy_distance_overhang");
const bool use_support_xy_distance_overhang = mesh.getSettingAsSupportDistPriority("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs
const double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
const bool conical_support = mesh.getSettingBoolean("support_conical_enabled") && conical_support_angle != 0;
const int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr");
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr");
bool interface_enable = mesh.getSettingBoolean("support_interface_enable");
// derived settings:
const int max_smoothing_angle = 135; // maximum angle of inner corners to be smoothed
int smoothing_distance;
{ // compute best smoothing_distance
ExtruderTrain& infill_train = *storage.meshgroup->getExtruderTrain(support_infill_extruder_nr);
int support_infill_line_width = infill_train.getSettingInMicrons("support_interface_line_width");
smoothing_distance = support_infill_line_width;
if (interface_enable)
{
ExtruderTrain& interface_train = *storage.meshgroup->getExtruderTrain(support_skin_extruder_nr);
int support_interface_line_width = interface_train.getSettingInMicrons("support_interface_line_width");
smoothing_distance = std::max(support_interface_line_width, smoothing_distance);
}
}
const int z_layer_distance_tower = 1; // start tower directly below overhang point
int supportLayerThickness = layerThickness;
int layerZdistanceTop = std::max(0, supportZDistanceTop / supportLayerThickness) + 1; // support must always be 1 layer below overhang
unsigned int layerZdistanceBottom = std::max(0, supportZDistanceBottom / supportLayerThickness);
const unsigned int layerZdistanceTop = std::max(0U, round_up_divide(supportZDistanceTop, supportLayerThickness)) + 1; // support must always be 1 layer below overhang
const unsigned int layerZdistanceBottom = std::max(0U, round_up_divide(supportZDistanceBottom, supportLayerThickness));
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged
int max_dist_from_lower_layer = tanAngle * supportLayerThickness; // max dist which can be bridged
int64_t conical_support_offset;
if (conical_support_angle > 0)
@@ -153,9 +194,8 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
// early out
if ( layerZdistanceTop + 1 > (int) support_layer_count )
if ( layerZdistanceTop + 1 > support_layer_count )
{
storage.support.generated = false; // no (first layer) support can be generated
return;
}
@@ -164,57 +204,37 @@ 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, extrusionWidth);
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--)
{
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
}
bool still_in_upper_empty_layers = true;
int overhang_points_pos = overhang_points.size() - 1;
Polygons supportLayer_last;
std::vector<Polygons> towerRoofs;
for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--)
{
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
Polygons overhang;
{
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
// Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
//
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
// presumably the computation above is slower than the one below
Polygons overhang_extented = basic_overhang.offset(maxDistFromLowerLayer + 100); // +100 for easier joining with support from layer above
overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
/* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^ supporter
* ^^^^^^^^^^^^^^^^^ supported
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
overhang = basic_and_full_overhang_above.back().second;
basic_and_full_overhang_above.pop_back();
}
Polygons& supportLayer_this = overhang;
if (extension_offset)
{
supportLayer_this = supportLayer_this.offset(extension_offset);
}
supportLayer_this.simplify(50); // TODO: hardcoded value!
if (supportMinAreaSqrt > 0)
{
// handle straight walls
@@ -225,7 +245,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
if (layer_idx+1 < support_layer_count)
{ // join with support from layer up
supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, min_smoothing_area, conical_support, conical_support_offset, conical_smallest_breadth);
supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, max_smoothing_angle, conical_support, conical_support_offset, conical_smallest_breadth);
}
@@ -243,10 +263,26 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
// inset using X/Y distance
if (supportLayer_this.size() > 0)
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
{
Polygons& basic_overhang = basic_and_full_overhang_above.front().first; // basic overhang on this layer
Polygons outlines = storage.getLayerOutlines(layer_idx, false);
if (use_support_xy_distance_overhang)
{
Polygons xy_overhang_disallowed = basic_overhang.offset(supportZDistanceTop * tanAngle);
Polygons xy_non_overhang_disallowed = outlines.difference(basic_overhang.offset(supportXYDistance)).offset(supportXYDistance);
Polygons xy_disallowed = xy_overhang_disallowed.unionPolygons(xy_non_overhang_disallowed.unionPolygons(outlines.offset(support_xy_distance_overhang)));
supportLayer_this = supportLayer_this.difference(xy_disallowed);
}
else
{
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
}
}
supportAreas[layer_idx] = supportLayer_this;
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
@@ -289,18 +325,54 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
supportAreas[layer_idx] = touching_buildplate;
}
}
storage.support.generated = true;
}
/* layer 2
* layer 1 ______________|
* _______| ^^^^^ basic overhang
*
* ^^^^^^^ supporter
* ^^^^^^^^^^^^^^^^^ supported
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
* ^^^^^^^^^ overhang extensions
* ^^^^^^^^^^^^^^ overhang
*/
std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx, const int64_t max_dist_from_lower_layer)
{
Polygons supportLayer_supportee = mesh.layers[layer_idx].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
//
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
// presumably the computation above is slower than the one below
Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 100); // +100 for easier joining with support from layer above
Polygons full_overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
return std::make_pair(basic_overhang, full_overhang);
}
void AreaSupport::detectOverhangPoints(
SliceDataStorage& storage,
SliceMeshStorage& mesh,
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points, // stores overhang_points along with the layer index at which the overhang point occurs)
int layer_count,
int supportMinAreaSqrt,
int extrusionWidth
)
int supportMinAreaSqrt
)
{
for (int layer_idx = 0 ; layer_idx < layer_count ; layer_idx++)
ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr"));
const unsigned int support_line_width = infill_extr->getSettingInMicrons("support_line_width");
for (int layer_idx = 0; layer_idx < layer_count; layer_idx++)
{
SliceLayer& layer = mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
@@ -308,8 +380,11 @@ void AreaSupport::detectOverhangPoints(
if (part.outline.outerPolygon().area() < supportMinAreaSqrt * supportMinAreaSqrt)
{
Polygons part_poly_computed;
Polygons& part_poly = (part.insets.size() > 0)? part.insets[0] : part_poly_computed; // don't copy inset if its already computed
if (part.insets.size() == 0) { part_poly_computed = part.outline.offset(-extrusionWidth/2); }
Polygons& part_poly = (part.insets.size() > 0) ? part.insets[0] : part_poly_computed; // don't copy inset if its already computed
if (part.insets.size() == 0)
{
part_poly_computed = part.outline.offset(-support_line_width / 2);
}
if (part_poly.size() > 0)
{
@@ -430,25 +505,60 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count)
{
int roof_layer_count = support_roof_height / layerThickness;
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"));
const unsigned int z_distance_bottom = round_up_divide(mesh.getSettingInMicrons("support_bottom_distance"), storage.getSettingInMicrons("layer_height"));
const unsigned int z_distance_top = round_up_divide(mesh.getSettingInMicrons("support_top_distance"), storage.getSettingInMicrons("layer_height"));
const int skip_layer_count = std::max(1u, round_divide(mesh.getSettingInMicrons("support_interface_skip_height"), storage.getSettingInMicrons("layer_height")));
const int interface_line_width = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr"))->getSettingInMicrons("support_interface_line_width");
std::vector<SupportLayer>& supportLayers = storage.support.supportLayers;
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
{
SupportLayer& layer = supportLayers[layer_idx];
if (layer_idx + roof_layer_count < supportLayers.size())
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())
{
Polygons roofs = supportAreas[layer_idx].difference(supportAreas[layer_idx + roof_layer_count]);
roofs.removeSmallAreas(1.0);
layer.roofs.add(roofs);
layer.supportAreas.add(supportAreas[layer_idx].difference(layer.roofs));
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));
}
else
{
layer.roofs.add(layer.supportAreas);
layer.skin.add(support_areas[layer_idx]);
}
}
}

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