Comparar commits

...

818 Commits

Autor SHA1 Mensagem Data
Tim Kuipers 1730923083 safety: skip mesh if layer nr too high (CURA-1134) 2016-11-30 17:54:37 +01:00
Tim Kuipers 8e5a936522 fix: wrong indexing in part.insets for GCodePlanner::moveInsideCombBoundary (CURA-1134) 2016-11-30 17:44:03 +01:00
Tim Kuipers d29da8f7f8 refactor: change is_inside and was_inside from bool to SliceLayerPart* so that we know inside what part we were/are inside (CURA-1134)
makes for a better moveInsideCombBoundary functionality
2016-11-30 17:43:25 +01:00
Tim Kuipers ad56ce8866 fix: don't comb on raft layers or filler layers (CURA-1134) 2016-11-30 17:40:04 +01:00
Tim Kuipers 565b09e37d feat: SliceDataStorage::getPartInside (CURA-1134) 2016-11-30 17:38:40 +01:00
Tim Kuipers edc9767912 fix: made Polygons::inside virtual and let PolygonsPart::inside override it (CURA-1134) 2016-11-30 17:38:12 +01:00
Tim Kuipers ce6f58f136 info: comment on combing move inside dist (CURA-1134) 2016-11-30 16:50:58 +01:00
Tim Kuipers 5b941d07ff refactor: max_moveInside_distance2 ==> max_move_inside_distance2 (CURA-1134) 2016-11-30 16:48:01 +01:00
Tim Kuipers 350b42052a fix: better move inside so that we don't move inside the wrong part (CURA-1134)
the move inside is performed so that we don't perform the retraction on the outer wall, which causes a clearly visible z seam
2016-11-28 17:57:01 +01:00
Tim Kuipers 0ba152d081 fix: only conclude the startPoint is not inside after all when it doesn't collide with the outline and we are combing also over skin (CURA-1134) 2016-11-28 17:14:48 +01:00
Tim Kuipers 2c70c2ecb7 refactor/fix: compute was_inside for new layer rather than using the last is_inside of the previous layer heuristically (CURA-1134) 2016-11-28 15:44:09 +01:00
Tim Kuipers 4da8c6d8e7 fix: no more aabb hit check fails (CURA-2992) 2016-11-28 14:35:13 +01:00
Tim Kuipers c5ce425924 fix: no more use of deleted memory by optional (CURA-3008) 2016-11-28 14:34:45 +01:00
Tim Kuipers c74d4f7550 Revert "feat: PolygonPointer as distinct from PolygonRef (CURA-3008)"
This reverts commit 20cd4275fc.
2016-11-28 13:43:03 +01:00
Tim Kuipers 19f093e0cc safety: assert on optional::instance (CURA-3008) 2016-11-28 13:42:36 +01:00
Tim Kuipers 2a4cca0402 fix: use ClosestPolygonPoint::isValid where neccesary (CURA-3008) 2016-11-28 13:40:54 +01:00
Tim Kuipers 32804c102c fix: use optional<PolygonRef> instead of PolygonPointer (CURA-3008) 2016-11-28 13:40:02 +01:00
Tim Kuipers 776f56fc37 lil fix: no more un/signed int comaprison 2016-11-28 12:18:13 +01:00
Tim Kuipers a92cd23e62 safety: extra assert for too many nozzle switches in a layer 2016-11-25 16:10:10 +01:00
Tim Kuipers 4725001564 fix: remove printing_temperature_command if precooling goes further back (CURA-1932)
If the precooling command will precede the printing temeprature command to heat to the temp of the next layer, then don't use that temp;
we should already be cooling down, rather than heating toward the temp of the next layer.
2016-11-25 14:00:26 +01:00
Tim Kuipers a18595877f safety: extra assert on temp in NozzleTempInsert (CURA-1932) 2016-11-25 14:00:26 +01:00
Tim Kuipers 40e7c450d5 fix: getCoolDownPointAfterWarmUp outer_temp was switched (CURA-1932)
This caused the timing of the precool command to be off
2016-11-25 14:00:26 +01:00
Tim Kuipers 13a0b11d68 fix: don't use material_print_temperature_layer_0 if it's zero (CURA-1932) 2016-11-25 14:00:26 +01:00
Ghostkeeper 4fc69f608a Fix max extruder height if only using bed adhesion
If an extruder only prints bed adhesion, the maximum extruder height for that extruder is now correct.

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

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

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

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

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

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

cherry-picked from a2a8604c72
2016-11-21 15:38:12 +01:00
Tim Kuipers 67697d5258 fix: handle modulo operations for negative layer numbers (CURA-2789) 2016-11-21 14:18:39 +01:00
Tim Kuipers 7d8e4de7ba fix: prime tower didn't get added in filler layers (CURA-2789)
PrimeTower always received layer_nr=0 for those layers, so i had to propagate the negative layer numbers to addSupportToGcode and to PrimeTower
2016-11-21 14:17:32 +01:00
Tim Kuipers 2486120a38 fix: don't send polygons over command socket during planning phase (CURA-2789)
The polygons of the prime tower were also sent on the wrong layer / height, causing it to be visualized within raft layers
2016-11-21 14:13:56 +01:00
Tim Kuipers 43c8f2a913 fix: print prime tower directly after platform adhesion on layer 0 (CURA-2789) 2016-11-21 13:19:25 +01:00
Ghostkeeper dd8b57b666 Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-11-18 15:51:10 +01:00
Jaime van Kessel 72ed492a61 Merge branch 'feature_alternate_carving' of github.com:Ultimaker/CuraEngine 2016-11-18 15:14:12 +01:00
Ghostkeeper c02482590f Merge branch 'feature_fill_small_gaps' 2016-11-18 15:05:58 +01:00
Tim Kuipers f32e2d9554 Merge branch 'master' into feature_anti_support 2016-11-18 14:07:29 +01:00
Jaime van Kessel bf61814849 Merge branch 'feature_fan_speed_0' of github.com:Ultimaker/CuraEngine 2016-11-18 10:43:56 +01:00
Jaime van Kessel 5741e79ade Merge branch 'master' of github.com:Ultimaker/CuraEngine 2016-11-18 10:42:17 +01:00
Jaime van Kessel 09419fd6be Merge branch 'feature_bed_adhesion_none' of github.com:Ultimaker/CuraEngine 2016-11-18 10:35:29 +01:00
Tim Kuipers a002f4b3b2 feat: alternate_carve_order (CURA-2992) 2016-11-17 19:58:31 +01:00
Tim Kuipers 25c7ccb0d3 fix: outside combing put starting and ending points of basic comb path on the wrong side (CURA-2988)
basic comb paths started on the wrong side, then followed the polygon on the right side and ended on the wrong side again
2016-11-17 17:22:53 +01:00
Tim Kuipers 58a99a403b fix: createLocToLineGrid didn't create any cells and copied the hashmap (CURA-2988)
std::bind doesn't allow for binding by reference
2016-11-17 17:01:57 +01:00
Tim Kuipers dd8594b200 fix: inside combing put starting and ending points of basic comb path on the wrong side (CURA-2988)
basic comb paths started on the wrong side, then followed the polygon on the right side and ended on the wrong side again
2016-11-17 17:00:23 +01:00
Ghostkeeper 6df5368cb9 Update variable names in documentation
The variable names were changed but changed incorrectly here.

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

Contributes to issue CURA-2625.
2016-11-17 13:35:59 +01:00
Tim Kuipers 9613e186a3 docs: fixed docs location and included precool diagram in LayerPlanBuffer (CURA-1932) 2016-11-16 17:54:11 +01:00
Tim Kuipers 82b2362b2d lil compiler warning removal (CURA-1932) 2016-11-16 16:52:54 +01:00
Tim Kuipers a6ee34602c refactor: rename rediculous timeBeforeEndToInsertPreheatCommand_coolDownWarmUp ==> sensible getWarmUpPointAfterCoolDown and vice versa (CURA-1932) 2016-11-16 16:50:05 +01:00
Tim Kuipers 05d29eabcd refactor: timeBeforeEndToInsertPreheatCommand arguments better names (CURA-1932) 2016-11-16 16:41:18 +01:00
Tim Kuipers f7e3534a79 fix: update outdated function signature (add const and better naming) (CURA-1932) 2016-11-16 16:30:56 +01:00
Tim Kuipers bd4972e466 refactor: rename function argument to better match the intent (CURA-1932) 2016-11-16 16:27:44 +01:00
Tim Kuipers 0acf3beec2 fix: insertFinalPrintTempCommand: compute weighted average_print_temp by taking time into account (CURA-1932)
note that the print temp statistic doesn't really matter that much
2016-11-16 16:20:51 +01:00
Tim Kuipers 081055be46 fix: better documentation for LayerPlanBuffer::insertFinalPrintTempCommand (CURA-1932) 2016-11-16 16:17:42 +01:00
Ghostkeeper 560d23d3f2 Simplify skipping current extruder in calculateExtruderOrder
These are equivalent, but the code is now simpler.

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

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

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

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

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

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

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

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

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

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

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

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

Contributes to issue CURA-2627.
2016-10-17 11:06:22 +02:00
Tim Kuipers 1d251fef70 fix: limit prime tower height when support is touching buildplate only (CURA-2633) 2016-10-15 16:15:18 +02:00
Tim Kuipers 16a0cf0fd5 fix: make first layer brim under support round (CURA-2686) 2016-10-15 15:59:48 +02:00
Tim Kuipers 3c7c352bfc fix: add support during generation, causing all support settings to be applied to support meshes (CURA-2077)
This has the disadvantage that support meshes are re-evaluated for each normal mesh.
However, otherwise support meshes could overlap with each other mesh..
2016-10-14 16:44:50 +02:00
Tim Kuipers 66e6375942 fix: lil typo in AreaSupport::generateSupportAreas for support meshes (CURA-2077) 2016-10-14 16:43:26 +02:00
Tim Kuipers 8d3f66c2cb fix: don't switch extruder if support interface is too small to be printed (CURA-2077) 2016-10-14 16:41:15 +02:00
Tim Kuipers 1fd540c231 fix: also print support of support meshes above normal support (CURA-2077) 2016-10-14 16:00:45 +02:00
Tim Kuipers ac799dd00e fix: don't do multi volumes functions on support meshes (CURA-2077) 2016-10-14 16:00:22 +02:00
Tim Kuipers 699406b044 refactor: anti_support_mesh ==> anti_overhang_mesh (CURA-2077) 2016-10-14 15:31:53 +02:00
Tim Kuipers 2f3333e87c feat: anti support meshes (CURA-2077) 2016-10-14 15:30:19 +02:00
Tim Kuipers 58e2e1a4e1 fix: better buffer for double to stream and more tests (CURA-2627) 2016-10-13 17:39:59 +02:00
Tim Kuipers 4ebbceb3e3 Merge branch '2.3' 2016-10-11 16:43:05 +02:00
Tim Kuipers c0495edf48 fix: turn off fans if fans are disabled (CURA-2603) 2016-10-11 16:42:56 +02:00
Tim Kuipers ace0045109 Merge branch '2.3' 2016-10-11 16:14:44 +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 7a4a7fe46a Merge branch '2.3' 2016-10-11 15:01:03 +02:00
Tim Kuipers 18bf08a1b5 fix: retrieve adhesion_type globally (CURA-2605) 2016-10-11 15:00:51 +02:00
Tim Kuipers 977d02a9a2 Updated link to code conventions 2016-10-11 09:22:02 +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
Johan K 71ea91b61b Implement adding extruder number to path compiler 2016-07-14 12:13:14 +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
Johan K a50d6fe5a9 Merge branch 'master' of https://github.com/Ultimaker/CuraEngine into layerview_dev 2016-07-13 14:52:11 +02:00
Johan K 0bd5ae9e01 Added documentaion
More documentation for PathCompiler and SliceDataStruct.
2016-07-13 14:03:46 +02:00
Scott Lenser ce85280971 Merge remote-tracking branch 'origin/master' into slicer-speedup-clean 2016-07-12 09:13:41 -04: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
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 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 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
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
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
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
133 arquivos alterados com 11209 adições e 4184 exclusões
+11 -4
Ver Arquivo
@@ -28,10 +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)
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
@@ -65,10 +66,11 @@ set(engine_SRCS # Except main.cpp.
src/MeshGroup.cpp
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/Preheat.cpp
src/PrimeTower.cpp
src/raft.cpp
src/skin.cpp
src/skirt.cpp
src/SkirtBrim.cpp
src/sliceDataStorage.cpp
src/slicer.cpp
src/support.cpp
@@ -100,9 +102,12 @@ set(engine_SRCS # Except main.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.
@@ -112,9 +117,11 @@ set(engine_TEST
set(engine_TEST_INFILL
)
set(engine_TEST_UTILS
BucketGrid2DTest
SparseGridTest
LinearAlg2DTest
PolygonUtilsTest
PolygonTest
StringTest
)
# Generating ProtoBuf protocol
@@ -166,4 +173,4 @@ add_custom_command(TARGET CuraEngine POST_BUILD
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
include(CPackConfig.cmake)
+33 -3
Ver Arquivo
@@ -13,6 +13,7 @@ message Slice
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
@@ -55,19 +56,42 @@ message Polygon {
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
SupportInterfaceType = 10;
}
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 PrintTimeMaterialEstimates { // The print time for the whole print and material estimates for each extruder
float time = 1; // Total time estimate
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
}
@@ -86,8 +110,14 @@ message Setting {
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; // Header string to be prenpended before the rest of the gcode sent from the engine
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
@@ -832,7 +832,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = documentation/assets
IMAGE_PATH = docs/assets
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
+14 -13
Ver Arquivo
@@ -19,33 +19,34 @@ 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
=======

Antes

Largura:  |  Altura:  |  Tamanho: 18 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 18 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 70 KiB

Antes

Largura:  |  Altura:  |  Tamanho: 20 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 20 KiB

+1 -1
Ver Arquivo
@@ -7,4 +7,4 @@ This is the documentation for CuraEngine, the back-end slicer of Cura.
[Glossary](documentation/glossary.md)
[Code Conventions](documentation/code_conventions.md)
[Code Conventions](https://github.com/Ultimaker/Meta/blob/master/code_conventions.md)
-1
Ver Arquivo
@@ -1 +0,0 @@
html/index.html
+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
};
//------------------------------------------------------------------------------
+1 -1
Ver Arquivo
@@ -9,7 +9,7 @@ cd ~/Development/CuraEngine/output/reflection/
run setting inheritance reflection
cd ~/Development/CuraEngine
./build/CuraEngine analyse ../Cura/resources/machines/fdmprinter.json meta/refl_ff.gv output/reflection/engineSettingLiterals.txt -piew
./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
-52
Ver Arquivo
@@ -1,52 +0,0 @@
{
"version": 2,
"name": "Command line setting defaults CuraEngine",
"metadata":
{
"author": "Ultimaker B.V."
},
"settings": {
"command_line_settings": {
"label": "Command Line Settings",
"type": "category",
"children": {
"center_object": {
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
"type": "bool",
"label": "Center object",
"default_value": true
},
"machine_print_temp_wait": {
"description": "Whether to wait for the nozzle temperature to be reached when preheating the nozzles at the start of the gcode.",
"type": "bool",
"label": "Machine print temp wait",
"default_value": true
},
"mesh_position_x": {
"description": "Offset applied to the object in the x direction.",
"type": "float",
"label": "Mesh position x",
"default_value": 0
},
"mesh_position_y": {
"description": "Offset applied to the object in the y direction.",
"type": "float",
"label": "Mesh position y",
"default_value": 0
},
"mesh_position_z": {
"description": "Offset applied to the object in the z direction. With this you can perform what was used to call 'Object Sink'.",
"type": "float",
"label": "Mesh position z",
"default_value": 0
},
"prime_tower_dir_outward": {
"description": "Whether to start printing in the middle of the prime tower and end up at the perimeter, or the other way around. This is only used for certain types of prime tower.",
"type": "bool",
"label": "Prime tower direction outward",
"default_value": false
}
}
}
}
}
+1 -1
Ver Arquivo
@@ -20,7 +20,7 @@ void ConicalOverhang::apply(Slicer* slicer, double angle, int layer_thickness)
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, 100*100);
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
+11
Ver Arquivo
@@ -13,4 +13,15 @@ ExtruderTrain::ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
{
}
bool ExtruderTrain::getIsUsed() const
{
return is_used;
}
void ExtruderTrain::setIsUsed(bool used)
{
is_used = used;
}
}//namespace cura
+4
Ver Arquivo
@@ -10,9 +10,13 @@ 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();
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);
};
+1
Ver Arquivo
@@ -11,6 +11,7 @@ struct FanSpeedLayerTimeSettings
public:
double cool_min_layer_time;
double cool_min_layer_time_fan_speed_max;
double cool_fan_speed_0;
double cool_fan_speed_min;
double cool_fan_speed_max;
double cool_min_speed;
+420 -243
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+55 -44
Ver Arquivo
@@ -60,32 +60,23 @@ private:
std::ofstream output_file;
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
* Whether the skirt or brim polygons have been processed into planned paths
* for each extruder train.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
bool skirt_is_processed[MAX_EXTRUDERS]; //!< Whether the skirt polygons have been processed into planned paths for each extruder train
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) // changed somewhere early in FffGcodeWriter::writeGCode
, is_inside_mesh_layer_part(false)
{
max_object_height = 0;
for (unsigned int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
{
skirt_is_processed[extruder_nr] = false;
}
}
/*!
@@ -173,11 +164,13 @@ private:
void setConfigRetraction(SliceDataStorage& storage);
/*!
* Initialize the GcodePathConfig config parameters which don't change over all layers, for each feature.
* Initialize the GcodePathConfig config parameters which don't change over
* all layers, for each feature.
*
* The features are: skirt, support and for each mesh: outer wall, inner walls, skin, infill (and combined infill)
* 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
* \param[out] storage The data storage to which to save the configurations.
*/
void initConfigs(SliceDataStorage& storage);
@@ -200,29 +193,40 @@ private:
/*!
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
*
* \param[in] storage where the slice data is stored.
* \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);
/*!
* 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 layer plan \p gcodeLayer.
*
* Plan priming of all used extruders which haven't been primed yet
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param extruder_nr The extrudewr train for which to process the skirt
* \param layer_plan The initial planning of the g-code of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processSkirt(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
void ensureAllExtrudersArePrimed(SliceDataStorage& storage, GCodePlanner& layer_plan, const int layer_nr);
/*!
* 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 processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
/*!
* Adds the ooze shield to the layer plan \p gcodeLayer.
@@ -241,16 +245,25 @@ private:
* \param layer_nr The index of the layer to write the gcode of.
*/
void processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Calculate in which order to print the meshes.
* Calculate in which order to plan the extruders
*
* \param[in] storage where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of mesh indices ordered on print order.
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int current_extruder);
std::vector<int> calculateExtruderOrder(SliceDataStorage& storage, int current_extruder);
/*!
* Calculate in which order to plan the meshes of a specific extruder
*
* \param[in] storage where the slice data is stored.
* \param extruder_nr The extruder for which to determine the order
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int extruder_nr);
/*!
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
*
@@ -294,9 +307,8 @@ private:
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int 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.
@@ -307,9 +319,8 @@ private:
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param extrusionWidth extrusionWidth
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int 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.
@@ -330,33 +341,33 @@ private:
* \param layer_nr The current layer number.
* \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, int skin_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 layer plan \p gcodeLayer of the current layer.
* Add the support to the layer plan \p gcodeLayer of the current layer for all support parts with the given \p extruder_nr.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr_before The extruder number at the start of the layer (before other print parts aka the rest)
* \param before_rest Whether the function has been called before adding the rest to the layer plan \p gcodeLayer, or after.
* \return whether any support was added to the layer plan
*/
void addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr_before, bool before_rest);
bool addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr);
/*!
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support infill was added to the layer plan
*/
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
bool addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support roofs to the layer plan \p gcodeLayer of the current layer.
* Add the support skins to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support skin was added to the layer plan
*/
void addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
bool addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
+229 -121
Ver Arquivo
@@ -3,6 +3,8 @@
#include <algorithm>
#include <map> // multimap (ordered map allowing duplicate keys)
#include "utils/math.h"
#include "utils/algorithm.h"
#include "slicer.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
@@ -11,11 +13,10 @@
#include "multiVolumes.h"
#include "layerPart.h"
#include "WallsComputation.h"
#include "skirt.h"
#include "SkirtBrim.h"
#include "skin.h"
#include "infill.h"
#include "raft.h"
#include "debug.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "ConicalOverhang.h"
@@ -27,7 +28,7 @@
namespace cura
{
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (!sliceModel(meshgroup, timeKeeper, storage))
@@ -40,6 +41,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);
@@ -91,23 +108,62 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("conical_overhang_enabled"))
if (mesh.getSettingBoolean("conical_overhang_enabled") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
}
}
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
//carveMultipleVolumes(storage.meshes);
if (storage.getSettingBoolean("carve_multiple_volumes"))
{
carveMultipleVolumes(slicerList, storage.getSettingBoolean("alternate_carve_order"));
}
generateMultipleVolumesOverlap(slicerList);
storage.print_layer_count = 0;
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
Slicer* slicer = slicerList[meshIdx];
if (!mesh.getSettingBoolean("anti_overhang_mesh") && !mesh.getSettingBoolean("infill_mesh"))
{
storage.print_layer_count = std::max(storage.print_layer_count, (unsigned int)slicer->layers.size());
}
}
storage.support.supportLayers.resize(storage.print_layer_count);
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Slicer* slicer = slicerList[meshIdx];
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("anti_overhang_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.anti_overhang = support_layer.anti_overhang.unionPolygons(slicer_layer.polygons);
}
continue;
}
if (mesh.getSettingBoolean("support_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.support_mesh.add(slicer_layer.polygons);
}
continue;
}
createLayerParts(meshStorage, slicer, mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
@@ -124,9 +180,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("adhesion_extruder_nr"));
layer.printZ +=
train->getSettingInMicrons("raft_base_thickness")
+ train->getSettingInMicrons("raft_interface_thickness")
+ train->getSettingAsCount("raft_surface_layers") * train->getSettingInMicrons("raft_surface_thickness")
Raft::getTotalThickness(storage)
+ train->getSettingInMicrons("raft_airgap")
- train->getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
@@ -153,7 +207,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
unsigned int slice_layer_count = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
slice_layer_count = std::max<unsigned int>(slice_layer_count, mesh.layers.size());
}
@@ -182,89 +236,92 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
}
for (unsigned int mesh_order_idx(0); mesh_order_idx < mesh_order.size(); ++mesh_order_idx)
{
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, slice_layer_count, inset_skin_progress_estimate);
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_order_idx + 1, storage.meshes.size());
}
unsigned int print_layer_count = 0;
if (CommandSocket::isInstantiated())
{ // send layer info
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)
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)
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer->printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
layer = &mesh.layers[layer_nr];
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);
log("Layer count: %i\n", storage.print_layer_count);
//layerparts2HTML(storage, "output/output.html");
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, storage.print_layer_count);
// we need to remove empty layers after we have procesed the insets
// processInsets might throw away parts if they have no wall at all (cause it doesn't fit)
// brim depends on the first layer not being empty
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), print_layer_count); // changes total_layers!
if (print_layer_count == 0)
// 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"), storage.print_layer_count); // changes storage.print_layer_count!
if (storage.print_layer_count == 0)
{
log("Stopping process because there are no non-empty layers.\n");
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, print_layer_count);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
for (unsigned int layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
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"));
}
}
*/
computePrintHeightStatistics(storage);
// handle helpers
storage.primeTower.computePrimeTowerMax(storage);
storage.primeTower.generatePaths(storage, print_layer_count);
storage.primeTower.generatePaths(storage);
logDebug("Processing ooze shield\n");
processOozeShield(storage);
processOozeShield(storage, print_layer_count);
processDraftShield(storage, print_layer_count);
logDebug("Processing draft shield\n");
processDraftShield(storage);
logDebug("Processing platform adhesion\n");
processPlatformAdhesion(storage);
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
processDerivedWallsSkinInfill(mesh, print_layer_count);
processDerivedWallsSkinInfill(mesh);
}
}
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate)
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
size_t mesh_layer_count = mesh.layers.size();
if (mesh.getSettingBoolean("infill_mesh"))
{
processInfillMesh(storage, mesh_order_idx, mesh_order, total_layers);
processInfillMesh(storage, mesh_order_idx, mesh_order);
}
// TODO: make progress more accurate!!
@@ -274,19 +331,20 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
inset_skin_progress_estimate.nextStage(mesh_inset_skin_progress_estimator); // the stage of this function call
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(total_layers);
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(inset_estimator);
// walls
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
logDebug("Processing insets for layer %i of %i\n", layer_number, mesh_layer_count);
processInsets(mesh, layer_number);
double progress = inset_skin_progress_estimate.progress(layer_number);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(total_layers);
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
bool process_infill = mesh.getSettingInMicrons("infill_line_distance") > 0;
@@ -300,8 +358,6 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
{
AABB3D aabb = storage.meshgroup->meshes[mesh_idx].getAABB();
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx].getAABB();
aabb.expandXY(mesh.getSettingInMicrons("xy_offset"));
other_aabb.expandXY(other_mesh.getSettingInMicrons("xy_offset"));
if (aabb.hit(other_aabb))
{
process_infill = true;
@@ -316,8 +372,9 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
logDebug("Processing skins and infill layer %i of %i\n", layer_number, mesh_layer_count);
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(mesh, layer_number, process_infill);
@@ -327,7 +384,7 @@ void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage,
}
}
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers)
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
@@ -398,13 +455,13 @@ void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers)
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
// combine infill
unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness.
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
@@ -420,37 +477,35 @@ void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int lay
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && ((layer_nr % 2) + 2) % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
{
inset_count += ((layer_nr % 2) + 2) % 2;
}
bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable");
WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall);
walls_computation.generateInsets(layer);
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (PolygonRef polyline : layer->openPolyLines)
{
Polygons segments;
for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++)
{
PolygonRef segment = segments.newPoly();
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
}
}
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers)
{
{
int n_empty_first_layers = 0;
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
bool layer_is_empty = true;
if (storage.support.generated && layer_idx < storage.support.supportLayers.size())
{
SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
if (support_layer.supportAreas.size() > 0 || support_layer.skin.size() > 0)
{
layer_is_empty = false;
break;
}
}
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_idx];
@@ -481,8 +536,12 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, cons
{
layer.printZ -= n_empty_first_layers * layer_height;
}
mesh.layer_nr_max_filled_layer -= n_empty_first_layers;
}
total_layers -= n_empty_first_layers;
storage.support.layer_nr_max_filled_layer -= n_empty_first_layers;
std::vector<SupportLayer>& support_layers = storage.support.supportLayers;
support_layers.erase(support_layers.begin(), support_layers.begin() + n_empty_first_layers);
}
}
@@ -492,11 +551,10 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned
{
return;
}
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"));
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
@@ -505,64 +563,116 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned
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;
infill_skin_overlap = innermost_wall_line_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
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::computePrintHeightStatistics(SliceDataStorage& storage)
{
int extruder_count = storage.meshgroup->getExtruderCount();
std::vector<int>& max_print_height_per_extruder = storage.max_print_height_per_extruder;
assert(max_print_height_per_extruder.size() == 0 && "storage.max_print_height_per_extruder shouldn't have been initialized yet!");
max_print_height_per_extruder.resize(extruder_count, -1); //Initialize all as -1.
{ // compute max_object_height_per_extruder
//Height of the meshes themselves.
for (SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("anti_overhang_mesh") || mesh.getSettingBoolean("support_mesh"))
{
continue; //Special type of mesh that doesn't get printed.
}
const unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_print_height_per_extruder[extr_nr] = std::max(max_print_height_per_extruder[extr_nr], mesh.layer_nr_max_filled_layer);
}
//Height of where the support reaches.
const unsigned int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_print_height_per_extruder[support_infill_extruder_nr] =
std::max(max_print_height_per_extruder[support_infill_extruder_nr],
storage.support.layer_nr_max_filled_layer);
const unsigned int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr"); // TODO: support skin extruder should be configurable per object
max_print_height_per_extruder[support_skin_extruder_nr] =
std::max(max_print_height_per_extruder[support_skin_extruder_nr],
storage.support.layer_nr_max_filled_layer);
//Height of where the platform adhesion reaches.
if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
max_print_height_per_extruder[adhesion_extruder_nr] =
std::max(0, max_print_height_per_extruder[adhesion_extruder_nr]);
}
}
storage.max_print_height_order = order(max_print_height_per_extruder);
if (extruder_count >= 2)
{
int second_highest_extruder = storage.max_print_height_order[extruder_count - 2];
storage.max_print_height_second_to_last_extruder = max_print_height_per_extruder[second_highest_extruder];
}
else
{
storage.max_print_height_second_to_last_extruder = -1;
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
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_print_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_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr - 1].offset(-allowed_angle_offset));
}
for (int layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr > 0; layer_nr--)
{
storage.oozeShield[layer_nr - 1] = storage.oozeShield[layer_nr - 1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
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_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
}
for(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)
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage)
{
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(storage.print_layer_count);
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 < storage.print_layer_count && layer_nr < draft_shield_layers; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
const int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
storage.draft_protection_shield = draft_shield.approxConvexHull(draft_shield_dist);
}
@@ -572,22 +682,20 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
switch(getSettingAsPlatformAdhesion("adhesion_type"))
{
case EPlatformAdhesion::SKIRT:
if (train->getSettingInMicrons("draft_shield_height") == 0)
{ // draft screen replaces skirt
generateSkirt(storage, train->getSettingInMicrons("skirt_gap"), train->getSettingAsCount("skirt_line_count"), train->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, train->getSettingAsCount("brim_line_count"), train->getSettingInMicrons("skirt_minimal_length"));
SkirtBrim::generate(storage, 0, train->getSettingAsCount("brim_line_count"), train->getSettingBoolean("brim_outside_only"));
break;
case EPlatformAdhesion::RAFT:
generateRaft(storage, train->getSettingInMicrons("raft_margin"));
Raft::generate(storage, train->getSettingInMicrons("raft_margin"));
break;
case EPlatformAdhesion::NONE:
break;
}
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
}
+29 -18
Ver Arquivo
@@ -46,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.
*
@@ -72,10 +84,9 @@ private:
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed (used for infill meshes)
* \param total_layers The total number of layers over all objects
* \param inset_skin_progress_estimate The progress stage estimate calculator
*/
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers, ProgressStageEstimator& inset_skin_progress_estimate);
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
@@ -83,18 +94,16 @@ private:
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed
* \param total_layers The total number of layers over all objects
*/
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, size_t total_layers);
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order);
/*!
* Process features which are derived from the basic walls, skin, and infill:
* fuzzy skin, infill combine
*
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers over all objects
*/
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers);
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh);
/*!
* Remove all bottom layers which are empty.
@@ -106,7 +115,14 @@ private:
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers);
/*!
* Set \ref SliceDataStorage::max_print_height_per_extruder and \ref SliceDataStorage::max_print_height_order and \ref SliceDataStorage::max_print_height_second_to_last_extruder
*
* \param[in,out] storage Where to retrieve mesh and support etc settings from and where the print height statistics are saved.
*/
void computePrintHeightStatistics(SliceDataStorage& storage);
/*!
* Generate the inset polygons which form the walls.
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
@@ -117,9 +133,8 @@ private:
/*!
* 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.
@@ -133,17 +148,15 @@ private:
* Generate the polygons where the draft screen should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param total_layers The total number of layers
*/
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
void processDraftShield(SliceDataStorage& storage);
/*!
* Generate the skirt/brim/raft areas/insets.
* \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'
*
@@ -154,10 +167,8 @@ private:
* \param[in,out] mesh where the outer wall is retrieved and stored in.
*/
void processFuzzyWalls(SliceMeshStorage& mesh);
};
}//namespace cura
#endif // FFF_AREA_GENERATOR_H
+2 -25
Ver Arquivo
@@ -45,29 +45,6 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
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, meshgroup_number == 0).c_str()); }
@@ -83,7 +60,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
bool empty = true;
for (Mesh& mesh : meshgroup->meshes)
{
if (!mesh.getSettingBoolean("infill_mesh"))
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
empty = false;
}
@@ -126,7 +103,7 @@ 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());
-7
Ver Arquivo
@@ -151,13 +151,6 @@ public:
gcode_writer.finalize();
}
/*!
* Process all files into one meshgroup
*
* \warning Unused!
*/
bool processFiles(const std::vector<std::string> &files);
/*!
* Generate gcode for a given \p meshgroup
* The primary function of this class.
+190 -31
Ver Arquivo
@@ -13,7 +13,7 @@ void LayerPlanBuffer::flush()
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the very last layer
insertTempCommands(); // insert preheat commands of the very last layer
}
while (!buffer.empty())
{
@@ -38,41 +38,51 @@ void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, d
if (acc_time > time_after_extruder_plan_start)
{
const double time_before_path_end = acc_time - time_after_extruder_plan_start;
extruder_plan_before.insertCommand(path_idx, extruder, temp, false, time_this_path - time_before_path_end);
bool wait = false;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait, time_this_path - time_before_path_end);
return;
}
}
extruder_plan_before.insertCommand(0, extruder, temp, false); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
bool wait = false;
unsigned int path_idx = 0;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
}
Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
double initial_print_temp = extruder_plan.initial_printing_temperature;
double in_between_time = 0.0;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{ // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_before_idx];
if (extruder_plan.extruder == extruder)
ExtruderPlan& extruder_plan_before = *extruder_plans[extruder_plan_before_idx];
if (extruder_plan_before.extruder == extruder)
{
Preheat::WarmUpResult warm_up = preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
double temp_before = preheat_config.getFinalPrintTemp(extruder);
if (temp_before == 0)
{
temp_before = extruder_plan_before.printing_temperature;
}
constexpr bool during_printing = false;
Preheat::WarmUpResult warm_up = preheat_config.getWarmUpPointAfterCoolDown(in_between_time, extruder, temp_before, preheat_config.getStandbyTemp(extruder), initial_print_temp, during_printing);
warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + extra_preheat_time);
return warm_up;
}
in_between_time += extruder_plan.estimates.getTotalTime();
in_between_time += extruder_plan_before.estimates.getTotalTime();
}
// The last extruder plan with the same extruder falls outside of the buffer
// assume the nozzle has cooled down to strandby temperature already.
Preheat::WarmUpResult warm_up;
warm_up.total_time_window = in_between_time;
warm_up.lowest_temperature = preheat_config.getStandbyTemp(extruder);
warm_up.heating_time = preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(warm_up.lowest_temperature, extruder, required_temp, false);
constexpr bool during_printing = false;
warm_up.heating_time = preheat_config.getTimeToGoFromTempToTemp(extruder, warm_up.lowest_temperature, initial_print_temp, during_printing);
if (warm_up.heating_time > in_between_time)
{
warm_up.heating_time = in_between_time;
warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder);
warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder, during_printing);
}
warm_up.heating_time = warm_up.heating_time + extra_preheat_time;
return warm_up;
@@ -82,7 +92,8 @@ Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vecto
void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
{
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
constexpr bool during_printing = true;
double time_before_extruder_plan_end = 0.5 * preheat_config.getTimeToGoFromTempToTemp(extruder, prev_extruder_plan.printing_temperature, required_temp, during_printing);
time_before_extruder_plan_end = std::min(prev_extruder_plan.estimates.getTotalTime(), time_before_extruder_plan_end);
insertPreheatCommand(prev_extruder_plan, time_before_extruder_plan_end, extruder, required_temp);
@@ -111,13 +122,13 @@ void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<ExtruderPl
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
double initial_print_temp = extruder_plan.initial_printing_temperature;
Preheat::WarmUpResult heating_time_and_from_temp = timeBeforeExtruderPlanToInsert(extruder_plans, extruder_plan_idx);
if (heating_time_and_from_temp.total_time_window < preheat_config.getMinimalTimeWindow(extruder))
{
handleStandbyTemp(extruder_plans, extruder_plan_idx, required_temp);
handleStandbyTemp(extruder_plans, extruder_plan_idx, initial_print_temp);
return; // don't insert preheat command and just stay on printing temperature
}
else
@@ -125,30 +136,32 @@ void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<ExtruderPl
handleStandbyTemp(extruder_plans, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);
}
// handle preheat command
double time_before_extruder_plan_to_insert = heating_time_and_from_temp.heating_time;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan_before = *extruder_plans[extruder_plan_before_idx];
assert (extruder_plan_before.extruder != extruder);
double time_here = extruder_plan_before.estimates.getTotalTime();
if (time_here >= time_before_extruder_plan_to_insert)
{
insertPreheatCommand(extruder_plan_before, time_before_extruder_plan_to_insert, extruder, required_temp);
insertPreheatCommand(extruder_plan_before, time_before_extruder_plan_to_insert, extruder, initial_print_temp);
return;
}
time_before_extruder_plan_to_insert -= time_here;
}
// time_before_extruder_plan_to_insert falls before all plans in the buffer
extruder_plans[0]->insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
bool wait = false;
unsigned int path_idx = 0;
extruder_plans[0]->insertCommand(path_idx, extruder, initial_print_temp, wait); // insert preheat command at verfy beginning of buffer
}
void LayerPlanBuffer::insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
void LayerPlanBuffer::insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double required_temp = extruder_plan.required_temp;
ExtruderPlan* prev_extruder_plan = extruder_plans[extruder_plan_idx - 1];
@@ -162,19 +175,154 @@ void LayerPlanBuffer::insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_
if (prev_extruder == extruder)
{
if (preheat_config.usesFlowDependentTemp(extruder))
{
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
}
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, extruder_plan.printing_temperature);
prev_extruder_plan->printing_temperature_command = --prev_extruder_plan->inserts.end();
}
else
{
insertPreheatCommand_multiExtrusion(extruder_plans, extruder_plan_idx);
insertFinalPrintTempCommand(extruder_plans, extruder_plan_idx - 1);
insertPrintTempCommand(extruder_plan);
}
}
void LayerPlanBuffer::insertPreheatCommands()
void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan)
{
unsigned int extruder = extruder_plan.extruder;
double print_temp = extruder_plan.printing_temperature;
double heated_pre_travel_time = 0;
if (preheat_config.getInitialPrintTemp(extruder) != 0)
{ // handle heating from initial_print_temperature to printing_tempreature
unsigned int path_idx;
for (path_idx = 0; path_idx < extruder_plan.paths.size(); path_idx++)
{
GCodePath& path = extruder_plan.paths[path_idx];
heated_pre_travel_time += path.estimates.getTotalTime();
if (!path.isTravelPath())
{
break;
}
}
bool wait = false;
extruder_plan.insertCommand(path_idx, extruder, print_temp, wait);
}
extruder_plan.heated_pre_travel_time = heated_pre_travel_time;
}
void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx)
{
ExtruderPlan& last_extruder_plan = *extruder_plans[last_extruder_plan_idx];
int extruder = last_extruder_plan.extruder;
double final_print_temp = preheat_config.getFinalPrintTemp(extruder);
if (final_print_temp == 0)
{
return;
}
double heated_post_travel_time = 0; // The time after the last extrude move toward the end of the extruder plan during which the nozzle is stable at the final print temperature
{ // compute heated_post_travel_time
unsigned int path_idx;
for (path_idx = last_extruder_plan.paths.size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = last_extruder_plan.paths[path_idx];
if (!path.isTravelPath())
{
break;
}
heated_post_travel_time += path.estimates.getTotalTime();
}
}
double time_window = 0; // The time window within which the nozzle needs to heat from the initial print temp to the printing temperature and then back to the final print temp; i.e. from the first to the last extrusion move with this extruder
double weighted_average_print_temp = 0; // The average of the normal printing temperatures of the extruder plans (which might be different due to flow dependent temp or due to initial layer temp) Weighted by time
double initial_print_temp = -1; // The initial print temp of the first extruder plan with this extruder
{ // compute time window and print temp statistics
double heated_pre_travel_time = -1; // The time before the first extrude move from the start of the extruder plan during which the nozzle is stable at the initial print temperature
for (unsigned int prev_extruder_plan_idx = last_extruder_plan_idx; (int)prev_extruder_plan_idx >= 0; prev_extruder_plan_idx--)
{
ExtruderPlan& prev_extruder_plan = *extruder_plans[prev_extruder_plan_idx];
if (prev_extruder_plan.extruder != extruder)
{
break;
}
double prev_extruder_plan_time = prev_extruder_plan.estimates.getTotalTime();
time_window += prev_extruder_plan_time;
heated_pre_travel_time = prev_extruder_plan.heated_pre_travel_time;
if (prev_extruder_plan.estimates.getTotalUnretractedTime() > 0 && prev_extruder_plan.estimates.getMaterial() > 0)
{ // handle temp statistics
assert(prev_extruder_plan.printing_temperature != -1 && "Previous extruder plan should already have a temperature planned");
weighted_average_print_temp += prev_extruder_plan.printing_temperature * prev_extruder_plan_time;
initial_print_temp = prev_extruder_plan.initial_printing_temperature;
}
}
weighted_average_print_temp /= time_window;
time_window -= heated_pre_travel_time + heated_post_travel_time;
assert(heated_pre_travel_time != -1 && "heated_pre_travel_time must have been computed; there must have been an extruder plan!");
}
assert((time_window >= 0 || last_extruder_plan.estimates.getMaterial() == 0) && "Time window should always be positive if we actually extrude");
// ,layer change .
// : ,precool command ,layer change .
// : ____: : ,precool command .
// :/ \ _____:_____: .
// _____/ \ / \ .
// / \ / \ .
// / / .
// / / .
// .
// approximate ^ by ^ .
// This approximation is quite ok since it only determines where to insert the precool temp command,
// which means the stable temperature of the previous extruder plan and the stable temperature of the next extruder plan couldn't be reached
constexpr bool during_printing = true;
Preheat::CoolDownResult warm_cool_result = preheat_config.getCoolDownPointAfterWarmUp(time_window, extruder, initial_print_temp, weighted_average_print_temp, final_print_temp, during_printing);
double cool_down_time = warm_cool_result.cooling_time;
assert(cool_down_time >= 0);
// find extruder plan in which to insert cooling command
ExtruderPlan* precool_extruder_plan = &last_extruder_plan;
{
for (unsigned int precool_extruder_plan_idx = last_extruder_plan_idx; (int)precool_extruder_plan_idx >= 0; precool_extruder_plan_idx--)
{
precool_extruder_plan = extruder_plans[precool_extruder_plan_idx];
if (precool_extruder_plan->printing_temperature_command)
{ // the precool command ends up before the command to go to the print temperature of the next extruder plan, so remove that print temp command
precool_extruder_plan->inserts.erase(*precool_extruder_plan->printing_temperature_command);
}
double time_here = precool_extruder_plan->estimates.getTotalTime();
if (cool_down_time < time_here)
{
break;
}
cool_down_time -= time_here;
}
}
// at this point cool_down_time is what time is left if cool down time of extruder plans after precool_extruder_plan (up until last_extruder_plan) are already taken into account
{ // insert temp command in precool_extruder_plan
double extrusion_time_seen = 0;
unsigned int path_idx;
for (path_idx = precool_extruder_plan->paths.size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = precool_extruder_plan->paths[path_idx];
extrusion_time_seen += path.estimates.getTotalTime();
if (extrusion_time_seen >= cool_down_time)
{
break;
}
}
bool wait = false;
double time_after_path_start = extrusion_time_seen - cool_down_time;
precool_extruder_plan->insertCommand(path_idx, extruder, final_print_temp, wait, time_after_path_start);
}
}
void LayerPlanBuffer::insertTempCommands()
{
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
{ // disregard empty layer
@@ -197,7 +345,9 @@ void LayerPlanBuffer::insertPreheatCommands()
GCodePlanner& layer_plan = buffer.back();
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
{
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double time = extruder_plan.estimates.getTotalUnretractedTime();
if (time <= 0.0
|| extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
@@ -206,8 +356,18 @@ 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);
double avg_flow = extruder_plan.estimates.getMaterial() / time;
extruder_plan.printing_temperature = preheat_config.getTemp(extruder, avg_flow, extruder_plan.is_initial_layer);
extruder_plan.initial_printing_temperature = preheat_config.getInitialPrintTemp(extruder);
if (extruder_plan.initial_printing_temperature == 0
|| !extruder_used_in_meshgroup[extruder]
|| (overall_extruder_plan_idx > 0 && extruder_plans[overall_extruder_plan_idx - 1]->extruder == extruder)
)
{
extruder_plan.initial_printing_temperature = extruder_plan.printing_temperature;
extruder_used_in_meshgroup[extruder] = true;
}
assert(extruder_plan.printing_temperature != -1 && "extruder_plan.printing_temperature should now have been set");
if (buffer.size() == 1 && extruder_plan_idx == 0)
{ // the very first extruder plan of the current meshgroup
@@ -221,7 +381,7 @@ void LayerPlanBuffer::insertPreheatCommands()
// see FffGcodeWriter::processStartingCode
if (extruder_idx == extruder)
{
gcode.setInitialTemp(extruder_idx, extruder_plan.required_temp);
gcode.setInitialTemp(extruder_idx, extruder_plan.printing_temperature);
}
else
{
@@ -239,8 +399,7 @@ void LayerPlanBuffer::insertPreheatCommands()
continue;
}
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
insertPreheatCommand(extruder_plans, overall_extruder_plan_idx);
insertTempCommands(extruder_plans, overall_extruder_plan_idx);
}
}
+43 -5
Ver Arquivo
@@ -1,3 +1,4 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef LAYER_PLAN_BUFFER_H
#define LAYER_PLAN_BUFFER_H
@@ -15,6 +16,19 @@
namespace cura
{
/*!
* Class for buffering multiple layer plans (\ref GCodePlanner) / extruder plans within those layer plans, so that temperature commands can be inserted in earlier layer plans.
*
* This class handles where to insert temperature commands for:
* - initial layer temperature
* - flow dependent temperature
* - starting to heat up from the standby temperature
* - initial printing temperature | printing temperature | final printing temperature
*
* \image html assets/precool.png "Temperature Regulation" width=10cm
* \image latex assets/precool.png "Temperature Regulation" width=10cm
*
*/
class LayerPlanBuffer : SettingsMessenger
{
GCodeExport& gcode;
@@ -26,12 +40,14 @@ class LayerPlanBuffer : SettingsMessenger
static constexpr const double extra_preheat_time = 1.0; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones.
std::vector<bool> extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the printing_temperature
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
: SettingsMessenger(settings)
, gcode(gcode)
, extruder_used_in_meshgroup(MAX_EXTRUDERS, false)
{ }
void setPreheatConfig(MeshGroup& settings)
@@ -48,7 +64,7 @@ public:
{
if (buffer.size() > 0)
{
insertPreheatCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
insertTempCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
}
buffer.emplace_back(constructor_args...);
if (buffer.size() > buffer_size)
@@ -67,7 +83,8 @@ public:
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
*/
void flush();
private:
/*!
* Insert the preheat command for @p extruder into @p extruder_plan_before
*
@@ -117,13 +134,34 @@ public:
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to generate the preheat command
*/
void insertPreheatCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
void insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the temperature command to heat from the initial print temperature to the printing temperature
*
* The temperature command is insert at the start of the very first extrusion move
*
* \param extruder_plan The extruder plan in which to insert the heat up command
*/
void insertPrintTempCommand(ExtruderPlan& extruder_plan);
/*!
* Insert the temp command to start cooling from the printing temperature to the final print temp
*
* The print temp is inserted before the last extrusion move of the extruder plan corresponding to \p last_extruder_plan_idx
*
* The command is inserted at a timed offset before the end of the last extrusion move
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param last_extruder_plan_idx The index of the last extruder plan in \p extruder_plans with the same extruder as previous extruder plans
*/
void insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx);
/*!
* Insert the preheat commands for the last added layer (unless that layer was empty)
*/
void insertPreheatCommands();
private:
void insertTempCommands();
/*!
* Reconfigure the standby temperature during which we didn't print with this extruder.
* Find the previous extruder plan with the same extruder as layers[layer_plan_idx].extruder_plans[extruder_plan_idx]
+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
+9 -16
Ver Arquivo
@@ -19,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
@@ -62,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.
@@ -73,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
+54 -4
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
{
@@ -44,7 +47,7 @@ MeshGroup::~MeshGroup()
}
}
int MeshGroup::getExtruderCount()
int MeshGroup::getExtruderCount() const
{
if (extruder_count == -1)
{
@@ -54,13 +57,19 @@ int MeshGroup::getExtruderCount()
}
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
if (!extruders[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)
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
logError("Couldn't load extruder.def.json for extruder %i\n", extruder_nr);
std::exit(1);
}
return extruders[extruder_nr];
}
return extruders[extruder_nr];
}
ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr)
{
@@ -118,6 +127,44 @@ void MeshGroup::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 && getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
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)
{
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
getExtruderTrain(mesh.getSettingAsIndex("extruder_nr"))->setIsUsed(true);
}
}
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
@@ -283,6 +330,8 @@ bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
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))
{
@@ -290,6 +339,7 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMa
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;
}
}
+2 -2
Ver Arquivo
@@ -18,9 +18,9 @@ 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();
int getExtruderCount() const;
MeshGroup(SettingsBaseVirtual* settings_base);
+195
Ver Arquivo
@@ -0,0 +1,195 @@
#include "Preheat.h"
namespace cura
{
void Preheat::setConfig(const MeshGroup& meshgroup)
{
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
{
assert(meshgroup.getExtruderTrain(extruder_nr) != nullptr);
const ExtruderTrain& extruder_train = *meshgroup.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
double machine_nozzle_cool_down_speed = extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed");
double machine_nozzle_heat_up_speed = extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed");
double material_extrusion_cool_down_speed = extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed");
assert(material_extrusion_cool_down_speed < machine_nozzle_heat_up_speed && "The extrusion cooldown speed must be smaller than the heat up speed; otherwise the printing temperature cannot be reached!");
config.time_to_cooldown_1_degree[0] = 1.0 / machine_nozzle_cool_down_speed;
config.time_to_heatup_1_degree[0] = 1.0 / machine_nozzle_heat_up_speed;
config.time_to_cooldown_1_degree[1] = 1.0 / (machine_nozzle_cool_down_speed + material_extrusion_cool_down_speed);
config.time_to_heatup_1_degree[1] = 1.0 / (machine_nozzle_heat_up_speed - material_extrusion_cool_down_speed);
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature");
config.min_time_window = extruder_train.getSettingInSeconds("machine_min_cool_heat_time_window");
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature");
config.material_print_temperature_layer_0 = extruder_train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
config.material_initial_print_temperature = extruder_train.getSettingInDegreeCelsius("material_initial_print_temperature");
config.material_final_print_temperature = extruder_train.getSettingInDegreeCelsius("material_final_print_temperature");
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
}
}
double Preheat::getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing)
{
Config& config = config_per_extruder[extruder];
double time;
if (temp_after > temp_before)
{
time = (temp_after - temp_before) * config.time_to_heatup_1_degree[during_printing];
}
else
{
time = (temp_before - temp_after) * config.time_to_cooldown_1_degree[during_printing];
}
return std::max(0.0, time);
}
double Preheat::getTemp(unsigned int extruder, double flow, bool is_initial_layer)
{
if (is_initial_layer && config_per_extruder[extruder].material_print_temperature_layer_0 != 0)
{
return config_per_extruder[extruder].material_print_temperature_layer_0;
}
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
Preheat::WarmUpResult Preheat::getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
WarmUpResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
result.total_time_window = time_window;
// ,temp_end
// / .
// ,temp_start / .
// \ ' ' ' ' '/ ' ' '> outer_temp .
// \________/ .
// "-> temp_mid
// ^^^^^^^^^^
// limited_time_window
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.heating_time = extra_heatup_time;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_start;
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = temp_start;
return result;
}
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.heating_time = 0;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_end;
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = temp_end;
return result;
}
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_standby_temp < limited_time_window)
{
result.heating_time += time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = temp_mid;
}
else
{
result.heating_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.lowest_temperature = std::max(temp_mid, temp_end - result.heating_time / time_to_heatup_1_degree);
}
if (result.heating_time > time_window || result.heating_time < 0.0)
{
logWarning("getWarmUpPointAfterCoolDown returns result outside of the time window!");
}
return result;
}
Preheat::CoolDownResult Preheat::getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
CoolDownResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
assert(temp_start != -1 && temp_mid != -1 && temp_end != -1 && "temperatures must be initialized!");
result.total_time_window = time_window;
// limited_time_window
// :^^^^^^^^^^^^:
// : ________. : . . .> temp_mid
// : / \ : .
// :/ . . . . .\:. . .> outer_temp .
// ^temp_start \ .
// \ .
// ^temp_end
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.cooling_time = 0;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_end;
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = temp_end;
return result;
}
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.cooling_time = extra_cooldown_time;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_start;
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = temp_start;
return result;
}
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double cool_down_time = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_temp1 = cool_down_time * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_temp1 < limited_time_window)
{
result.cooling_time += cool_down_time;
result.highest_temperature = temp_mid;
}
else
{
result.cooling_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.highest_temperature = std::min(temp_mid, temp_end + result.cooling_time / time_to_cooldown_1_degree);
}
if (result.cooling_time > time_window || result.cooling_time < 0.0)
{
logWarning("getCoolDownPointAfterWarmUp returns result outside of the time window!");
}
return result;
}
}//namespace cura
+88 -104
Ver Arquivo
@@ -26,17 +26,21 @@ class Preheat
class Config
{
public:
double time_to_heatup_1_degree; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature)
double time_to_cooldown_1_degree; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature)
double heatup_cooldown_time_mod_while_printing; //!< The time to be added to Preheat::time_to_heatup_1_degree and subtracted from Preheat::time_to_cooldown_1_degree to get the timings while printing
double time_to_heatup_1_degree[2]; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double time_to_cooldown_1_degree[2]; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
double min_time_window; //!< Minimal time (in seconds) to allow an extruder to cool down and then warm up again.
double material_print_temperature; //!< default print temp (backward compatilibily)
double material_print_temperature_layer_0; //!< initial layer print temp
double material_initial_print_temperature; //!< print temp when first starting to extrude after a layer switch
double material_final_print_temperature; //!< print temp at the end of all extrusion moves of an extruder to which it's cooled down just before - during the extrusion
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
@@ -54,6 +58,16 @@ public:
double lowest_temperature; //!< The lower temperature from which heating starts.
};
/*!
* The type of result when computing when to start cooling down a nozzle before it's not going to be used again.
*/
struct CoolDownResult
{
double total_time_window; //!< The total time in which heating and cooling takes place.
double cooling_time; //!< The total time needed to cool down to the required temperature.
double highest_temperature; //!< The upper temperature from which cooling starts.
};
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
@@ -68,68 +82,52 @@ public:
* Get the time it takes to heat up one degree celsius
*
* \param extruder the extruder train for which to get time it takes to heat up one degree celsius
* \param during_printing whether the heating takes time during printing or when idle
* \return the time it takes to heat up one degree celsius
*/
double getTimeToHeatup1Degree(int extruder)
double getTimeToHeatup1Degree(int extruder, bool during_printing)
{
return config_per_extruder[extruder].time_to_heatup_1_degree;
return config_per_extruder[extruder].time_to_heatup_1_degree[during_printing];
}
/*!
* Get the initial print temperature when starting to extrude.
* \param during_printing whether the heating takes time during printing or when idle
*/
double getInitialPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_initial_print_temperature;
}
/*!
* Get the final print temperature at the end of all extrusion moves with the current extruder
*/
double getFinalPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_final_print_temperature;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
* \param meshgroup Where to get settings from
*/
void setConfig(MeshGroup& settings)
{
for (int extruder_nr = 0; extruder_nr < settings.getExtruderCount(); extruder_nr++)
{
assert(settings.getExtruderTrain(extruder_nr) != nullptr);
ExtruderTrain& extruder_train = *settings.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
config.time_to_cooldown_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed"); // 0.5
config.time_to_heatup_1_degree = 1.0 / extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed"); // 0.5
config.heatup_cooldown_time_mod_while_printing = 1.0 / extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed"); // 0.1
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature"); // 150
void setConfig(const MeshGroup& meshgroup);
config.min_time_window = extruder_train.getSettingInSeconds("machine_min_cool_heat_time_window");
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature"); // 220
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
}
}
bool usesFlowDependentTemp(int extruder_nr)
{
return config_per_extruder[extruder_nr].flow_dependent_temperature;
}
private:
/*!
* Calculate time to heat up from standby temperature to a given temperature.
* Assumes @p temp is higher than the standby temperature.
*
* \param extruder The extruder for which to get the time
* \param temp The temperature to be reached
*/
double timeToHeatFromStandbyToPrintTemp(unsigned int extruder, double temp)
{
return (temp - config_per_extruder[extruder].standby_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
public:
/*!
* Get the optimal temperature corresponding to a given average flow.
* Get the optimal temperature corresponding to a given average flow,
* or the initial layer temperature.
*
* \param extruder The extruder train
* \param flow The flow for which to get the optimal temperature
* \param is_initial_layer Whether the initial layer temperature should be returned instead of flow-based temperature
* \return The corresponding optimal temperature
*/
double getTemp(unsigned int extruder, double flow)
{
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
double getTemp(unsigned int extruder, double flow, bool is_initial_layer);
/*!
* Return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
@@ -142,76 +140,62 @@ public:
}
/*!
* Decide when to start warming up again after starting to cool down towards the standby temperature.
* Decide when to start warming up again after starting to cool down towards \p temp_mid.
* Two cases are considered:
* the case where the standby temperature is reached \__/ .
* and the case where it isn't \/ .
*
* IT is assumed that the printer is not printing during this cool down and warm up time.
*
* Assumes from_temp is approximately the same as @p temp
* \warning it is assumed that \p temp_mid is lower than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
// ,temp_end
// / .
// ,temp_start / .
// \ / .
// \________/ .
// "-> temp_mid
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \param temp_start The temperature from which to start cooling down
* \param temp_mid The temeprature to which we try to cool down
* \param temp_end The temperature to which we need to have heated up at the end of the \p time_window
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the @p time_window to insert the preheat command and the temperature from which the heating starts
*/
WarmUpResult timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(double time_window, unsigned int extruder, double temp)
{
WarmUpResult result;
const Config& config = config_per_extruder[extruder];
result.total_time_window = time_window;
double time_ratio_cooldown_heatup = config.time_to_cooldown_1_degree / config.time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = timeToHeatFromStandbyToPrintTemp(extruder, temp);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_standby_temp < time_window)
{
result.heating_time = time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = config.standby_temp;
}
else
{
result.heating_time = time_window * config.time_to_heatup_1_degree / (config.time_to_cooldown_1_degree + config.time_to_heatup_1_degree);
result.lowest_temperature = std::max(config.standby_temp, temp - result.heating_time / config.time_to_heatup_1_degree);
}
return result;
}
WarmUpResult getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* Calculate time needed to warm up the nozzle from a given temp to a given temp.
* If the printer is printing in the mean time the warming up will take longer.
* Decide when to start cooling down again after starting to warm up towards the \p temp_mid
* Two cases are considered:
* the case where the temperature is reached /"""\ .
* and the case where it isn't /\ .
*
* \warning it is assumed that \p temp_mid is higher than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
* \param from_temp The temperature at which the nozzle was before
// _> temp_mid
// /""""""""\ .
// / \ .
// ^temp_start \ .
// \ .
// ^temp_end
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp The temperature to which to heat
* \param printing Whether the printer is printing in the time to heat up the nozzle
* \return The time needed to reach the desired temperature (@p temp)
* \param temp_start The temperature from which to start heating up
* \param temp_mid The temeprature to which we try to heat up
* \param temp_end The temperature to which we need to have cooled down after \p time_window time
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the \p time_window to insert the preheat command and the temperature from which the cooling starts
*/
double timeBeforeEndToInsertPreheatCommand_warmUp(double from_temp, unsigned int extruder, double temp, bool printing)
{
if (temp > from_temp)
{
if (printing)
{
return (temp - from_temp) * (config_per_extruder[extruder].time_to_heatup_1_degree + config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
else
{
return (temp - from_temp) * config_per_extruder[extruder].time_to_heatup_1_degree;
}
}
else
{
if (printing)
{
return (from_temp - temp) * config_per_extruder[extruder].time_to_cooldown_1_degree;
}
else
{
return (from_temp - temp) * std::max(0.0, config_per_extruder[extruder].time_to_cooldown_1_degree - config_per_extruder[extruder].heatup_cooldown_time_mod_while_printing);
}
}
}
CoolDownResult getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* Get the time to go from one temperature to another temperature
* \param extruder The extruder number for which to perform the heatup / cooldown
* \param temp_before The before temperature
* \param temp_after The after temperature
* \param during_printing Whether the planned cooldown / warmup occurs during printing or while in standby mode
* \return The time needed
*/
double getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing);
};
} // namespace cura
+192 -201
Ver Arquivo
@@ -1,5 +1,7 @@
#include "PrimeTower.h"
#include <limits>
#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
@@ -9,34 +11,39 @@
namespace cura
{
PrimeTower::PrimeTower()
: is_hollow(false)
, wipe_from_middle(false)
, current_pre_wipe_location_idx(0)
{
for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
{
last_prime_tower_poly_printed[extruder_nr] = -1;
}
}
void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>& retraction_config_per_extruder)
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++)
{
ExtruderTrain* train = meshgroup->getExtruderTrain(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(MeshGroup* meshgroup, int layer_thickness)
void PrimeTower::setConfigs(const MeshGroup* meshgroup, const int layer_thickness)
{
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
extruder_count = meshgroup->getExtruderCount();
for (int extr = 0; extr < extruder_count; extr++)
{
GCodePathConfig& conf = config_per_extruder[extr];
@@ -44,66 +51,19 @@ 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");
int max_object_height_per_extruder[extruder_count];
std::fill_n(max_object_height_per_extruder, extruder_count, -1); // unitialize all as -1
{ // compute max_object_height_per_extruder
for (SliceMeshStorage& mesh : storage.meshes)
{
unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_object_height_per_extruder[extr_nr] =
std::max( max_object_height_per_extruder[extr_nr]
, mesh.layer_nr_max_filled_layer );
}
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_infill_extruder_nr] =
std::max( max_object_height_per_extruder[support_infill_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
max_object_height_per_extruder[support_roof_extruder_nr] =
std::max( max_object_height_per_extruder[support_roof_extruder_nr]
, storage.support.layer_nr_max_filled_layer );
}
{ // // compute max_object_height_second_to_last_extruder
int extruder_max_object_height = 0;
for (int extruder_nr = 1; extruder_nr < extruder_count; extruder_nr++)
{
if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_max_object_height])
{
extruder_max_object_height = extruder_nr;
}
}
int extruder_second_max_object_height = -1;
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
if (extruder_nr == extruder_max_object_height) { continue; }
if (extruder_second_max_object_height == -1 || max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
{
extruder_second_max_object_height = extruder_nr;
}
}
if (extruder_second_max_object_height < 0)
{
storage.max_object_height_second_to_last_extruder = -1;
}
else
{
storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height];
}
}
}
void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
extruder_count = storage.meshgroup->getExtruderCount();
int64_t prime_tower_wall_thickness = storage.getSettingInMicrons("prime_tower_wall_thickness");
int64_t tower_size = storage.getSettingInMicrons("prime_tower_size");
if (prime_tower_wall_thickness * 2 < tower_size)
{
is_hollow = true;
}
PolygonRef p = ground_poly.newPoly();
int tower_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
@@ -111,93 +71,63 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
middle = Point(x - tower_size / 2, y + tower_size / 2);
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
if (is_hollow)
{
ground_poly = ground_poly.difference(ground_poly.offset(-prime_tower_wall_thickness));
}
post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
enabled = storage.max_print_height_second_to_last_extruder >= 0
&& storage.getSettingBoolean("prime_tower_enable")
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
&& storage.getSettingInMicrons("prime_tower_size") > 10;
if (enabled)
{
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.getSettingBoolean("prime_tower_enable"))
{
PolygonRef p = storage.primeTower.ground_poly.newPoly();
int tower_size = storage.getSettingInMicrons("prime_tower_size");
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
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
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
int extra_infill_shift = 0;
generateGroundpoly(storage);
int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position
for (int extruder = 0; extruder < extruder_count; extruder++)
{
int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
patterns_per_extruder.emplace_back(n_patterns);
std::vector<Polygons>& patterns = patterns_per_extruder.back();
std::vector<ExtrusionMoves>& patterns = patterns_per_extruder.back();
patterns.resize(n_patterns);
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
Polygons result_polygons; // should remain empty, since we generate lines pattern!
int outline_offset = -line_width/2;
patterns[pattern_idx].polygons = ground_poly.offset(-line_width / 2);
Polygons& result_lines = patterns[pattern_idx].lines;
int outline_offset = -line_width;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
infill_comp.generate(result_polygons, result_lines);
}
}
}
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, const int new_extruder)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
if (!enabled)
{
return;
}
@@ -210,99 +140,160 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
{ // don't print the prime tower if it has been printed already
return;
}
if (prev_extruder == gcodeLayer.getExtruder())
{
wipe = false;
}
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
}
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)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
if (layer_nr > storage.max_print_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
Polygons& pattern = patterns_per_extruder[new_extruder][layer_nr % 2];
bool pre_wipe = storage.meshgroup->getExtruderTrain(new_extruder)->getSettingBoolean("dual_pre_wipe");
bool post_wipe = storage.meshgroup->getExtruderTrain(prev_extruder)->getSettingBoolean("prime_tower_wipe_enabled");
GCodePathConfig& config = config_per_extruder[new_extruder];
int start_idx = 0; // TODO: figure out which idx is closest to the far right corner
gcodeLayer.addPolygon(ground_poly.back(), start_idx, &config);
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (CommandSocket::isInstantiated())
if (prev_extruder == new_extruder)
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
pre_wipe = false;
post_wipe = false;
}
// pre-wipe:
if (pre_wipe)
{
preWipe(storage, gcodeLayer, new_extruder);
}
if (wipe)
addToGcode_denseInfill(storage, gcodeLayer, gcode, layer_nr, prev_extruder, new_extruder);
// post-wipe:
if (post_wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(storage.wipePoint - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
gcodeLayer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
}
}
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
return;
}
int new_extruder = gcodeLayer.getExtruder();
ExtrusionMoves& pattern = patterns_per_extruder[new_extruder][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
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)
GCodePathConfig& config = config_per_extruder[new_extruder];
gcodeLayer.addPolygonsByOptimizer(pattern.polygons, &config);
gcodeLayer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
}
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
{
Point ret(0, 0);
int absolute_starting_points = 0;
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
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)
{
wipe_from_middle = is_hollow;
// only wipe from the middle of the prime tower if we have a z hop already on the first move after the layer switch
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
GCodePathConfig& config = config_per_extruder[new_extruder];
gcodeLayer.addPolygonsByOptimizer(insets[(prime_tower_dir_outward)? insets.size() - 1 - n : n], &config);
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
wipe_from_middle &= train.getSettingBoolean("retraction_hop_enabled")
&& (!train.getSettingBoolean("retraction_hop_only_when_collides") || train.getSettingBoolean("retraction_hop_after_extruder_switch"));
}
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));
PolygonsPointIndex segment_start; // from where to start the sequence of wipe points
PolygonsPointIndex segment_end; // where to end the sequence of wipe points
if (wipe_from_middle)
{
// take the same start as end point so that the whole poly os covered.
// find the inner polygon.
segment_start = segment_end = PolygonUtils::findNearestVert(middle, ground_poly);
}
};
}//namespace cura
else
{
// take the closer corner of the wipe tower and generate wipe locations on that side only:
//
// |
// |
// +-----
// .
// ^ nozzle switch location
Point from = getLocationBeforePrimeTower(storage);
// find the single line segment closest to [from] pointing most toward [from]
PolygonsPointIndex closest_vert = PolygonUtils::findNearestVert(from, ground_poly);
PolygonsPointIndex prev = closest_vert.prev();
PolygonsPointIndex next = closest_vert.next();
int64_t prev_dot_score = dot(from - closest_vert.p(), turn90CCW(prev.p() - closest_vert.p()));
int64_t next_dot_score = dot(from - closest_vert.p(), turn90CCW(closest_vert.p() - next.p()));
if (prev_dot_score > next_dot_score)
{
segment_start = prev;
segment_end = closest_vert;
}
else
{
segment_start = closest_vert;
segment_end = next;
}
}
PolygonUtils::spreadDots(segment_start, segment_end, number_of_pre_wipe_locations, pre_wipe_locations);
}
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr)
{
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);
if (wipe_from_middle)
{
// for hollow wipe tower:
// start from above
// go to wipe start
// go to the Z height of the previous/current layer
// wipe
// go to normal layer height (automatically on the next extrusion move)...
GCodePath& toward_middle = gcode_layer.addTravel(middle);
toward_middle.perform_z_hop = true;
gcode_layer.forceNewPathStart();
GCodePath& toward_wipe_start = gcode_layer.addTravel_simple(start);
toward_wipe_start.perform_z_hop = false;
toward_wipe_start.retract = true;
}
else
{
gcode_layer.addTravel(start);
}
float flow = 0.0001; // force this path being interpreted as an extrusion path, so that no Z hop will occur (TODO: really separately handle travel and extrusion moves)
gcode_layer.addExtrusionMove(end, &config_per_extruder[extruder_nr], SpaceFillType::None, flow);
}
}//namespace cura
+126 -31
Ver Arquivo
@@ -1,9 +1,12 @@
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
#include <vector>
#include "GCodePathConfig.h"
#include "MeshGroup.h"
#include "utils/polygon.h" // Polygons
#include "utils/polygonUtils.h"
namespace cura
{
@@ -13,51 +16,143 @@ class SliceDataStorage;
class GCodePlanner;
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;
class WallInfill
struct ExtrusionMoves
{
Polygons polygons;
Polygons lines;
};
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);
bool enabled; //!< Whether the prime tower is enabled
int extruder_count; //!< number of extruders
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
bool is_hollow; //!< Whether the prime tower is hollow
bool wipe_from_middle; //!< Whether to wipe on the inside of the hollow prime tower
Point middle; //!< The middle of the prime tower
Point post_wipe_point; //!< location to post-wipe the unused nozzle off on
std::vector<ClosestPolygonPoint> pre_wipe_locations; //!< The differernt locations where to pre-wipe the active nozzle
const unsigned int pre_wipe_location_skip = 13; //!< How big the steps are when stepping through \ref PrimeTower::wipe_locations
const unsigned int number_of_pre_wipe_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations
// note that the above are two consecutive numbers in the Fibonacci sequence
int current_pre_wipe_location_idx; //!< Index into \ref PrimeTower::wipe_locations of where to pre-wipe the nozzle
public:
Polygons ground_poly; //!< The outline of the prime tower to be used for each layer
std::vector<std::vector<ExtrusionMoves>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
/*!
* 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
*
* Fills \ref PrimeTower::ground_poly and sets \ref PrimeTower::middle
*
* \param storage Where to retrieve prime tower settings from
*/
void generateGroundpoly(const SliceDataStorage& storage);
std::vector<std::vector<Polygons>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
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);
void computePrimeTowerMax(SliceDataStorage& storage);
PrimeTower();
PrimeTower(); //!< basic constructor
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);
/*!
* 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 new_extruder The switched to extruder with which the prime tower paths should be generated.
*/
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder);
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 extrude paths for each extruder on even and odd layers
* Fill the ground poly with dense infill.
*
* \param storage where to get settings from
* \param total_layers The total number of layers
*/
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
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
*/
void addToGcode_denseInfill(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_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
+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);
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), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
if (outside_only)
{ // don't remove support within empty holes where no brim is generated.
model_brim_covered_area.add(first_layer_empty_holes);
}
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);
}
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
}
constexpr int join_distance = 20;
first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
constexpr int smallest_line_length = 200;
constexpr int largest_error_of_removed_point = 50;
first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
}
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
+1
Ver Arquivo
@@ -38,6 +38,7 @@ void WallsComputation::generateInsets(SliceLayerPart* part)
//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)
+10 -25
Ver Arquivo
@@ -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;
@@ -42,7 +42,7 @@ void Weaver::weave(MeshGroup* meshgroup)
}
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);
}
}
@@ -53,8 +53,7 @@ void Weaver::weave(MeshGroup* meshgroup)
for (cura::Slicer* slicer : slicerList)
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.
{
@@ -85,10 +84,8 @@ void Weaver::weave(MeshGroup* meshgroup)
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;
@@ -327,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;
@@ -336,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())
@@ -388,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];
@@ -430,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;
}
@@ -476,16 +473,4 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
}
}//namespace cura
+2 -4
Ver Arquivo
@@ -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"); // ___ ___ .
+65 -45
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/Progress.h"
#include "pathOrderOptimizer.h" // for skirt
#include "pathOrderOptimizer.h" //For skirt/brim.
namespace cura
{
@@ -17,7 +19,7 @@ void Wireframe2gcode::writeGCode()
gcode.preSetup(wireFrame.meshgroup);
gcode.setInitialTemps(wireFrame.meshgroup);
gcode.setInitialTemps(*wireFrame.meshgroup);
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
@@ -33,23 +35,22 @@ void Wireframe2gcode::writeGCode()
{
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment(PrintFeatureType::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);
}
}
@@ -65,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);
}
}
,
@@ -75,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);
@@ -125,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);
}
}
@@ -147,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);
}
});
@@ -179,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;
@@ -191,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);
}
}
@@ -207,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())
{
@@ -257,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);
@@ -266,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);
@@ -304,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)
{
@@ -319,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)
@@ -383,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:
@@ -487,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_/ .
@@ -550,9 +550,15 @@ void Wireframe2gcode::processStartingCode()
{
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"))
{
@@ -561,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");
@@ -585,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);
}
}
@@ -598,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];
@@ -607,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));
}
}
}
+3 -5
Ver Arquivo
@@ -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;
+395 -46
Ver Arquivo
@@ -18,8 +18,6 @@
#include <windows.h>
#endif
#include "settings/SettingRegistry.h" // loadExtruderJSONsettings
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
// std::cerr << x;
@@ -57,45 +55,193 @@ 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;
};
/*!
* 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
@@ -127,12 +273,14 @@ void CommandSocket::connect(const std::string& ip, int port)
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::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::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);
@@ -175,6 +323,7 @@ void CommandSocket::connect(const std::string& ip, int port)
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())
{
@@ -186,19 +335,44 @@ void CommandSocket::connect(const std::string& ip, int port)
{
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())
{
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)
{
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();
@@ -243,14 +417,13 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list, const google
{ // 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
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, train);
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->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
ExtruderTrain* train = meshgroup->getExtruderTrain(extruder_nr);
for (auto setting : extruder.settings().settings())
{
train->setSetting(setting.name(), setting.value());
@@ -320,41 +493,99 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list, const google
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
void CommandSocket::sendOptimizedLayerInfo(int layer_nr, int32_t z, int32_t height)
{
#ifdef ARCUS
std::shared_ptr<cura::proto::Layer> layer = private_data->getLayerById(layer_nr);
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, const 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
@@ -368,6 +599,7 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
void CommandSocket::sendPrintTimeMaterialEstimates()
{
#ifdef ARCUS
logDebug("Sending print time and material estimates.\n");
auto message = std::make_shared<cura::proto::PrintTimeMaterialEstimates>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
@@ -381,6 +613,7 @@ void CommandSocket::sendPrintTimeMaterialEstimates()
}
private_data->socket->sendMessage(message);
logDebug("Done sending print time and material estimates.\n");
#endif
}
@@ -398,20 +631,48 @@ void CommandSocket::sendLayerData()
#ifdef ARCUS
#endif
#ifdef ARCUS
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
log("End sliced object called. Sending %d layers.", private_data->current_layer_count);
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();
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
}
@@ -419,8 +680,10 @@ void CommandSocket::sendLayerData()
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
}
@@ -454,12 +717,12 @@ void CommandSocket::sendGCodePrefix(std::string prefix)
#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;
}
@@ -467,12 +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
+48 -12
Ver Arquivo
@@ -50,19 +50,44 @@ public:
*/
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, const 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 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);
@@ -87,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.
*
@@ -114,6 +148,8 @@ public:
private:
class Private;
const std::unique_ptr<Private> private_data;
class PathCompiler;
const std::unique_ptr<PathCompiler> path_comp;
#endif
};
-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
+132 -69
Ver Arquivo
@@ -7,6 +7,7 @@
#include "utils/logoutput.h"
#include "PrintFeature.h"
#include "utils/Date.h"
#include "utils/string.h" // MMtoStream, PrecisionedDouble
namespace cura {
@@ -26,42 +27,56 @@ GCodeExport::GCodeExport()
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;
total_bounding_box = AABB3D();
}
GCodeExport::~GCodeExport()
{
}
void GCodeExport::preSetup(const MeshGroup* settings)
void GCodeExport::preSetup(const MeshGroup* meshgroup)
{
setFlavor(settings->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = settings->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
setFlavor(meshgroup->getSettingAsGCodeFlavor("machine_gcode_flavor"));
use_extruder_offset_to_offset_coords = meshgroup->getSettingBoolean("machine_use_extruder_offset_to_offset_coords");
extruder_count = settings->getSettingAsCount("machine_extruder_count");
extruder_count = meshgroup->getSettingAsCount("machine_extruder_count");
for (const Mesh& mesh : settings->meshes)
for (const Mesh& mesh : meshgroup->meshes)
{
extruder_attr[mesh.getSettingAsIndex("extruder_nr")].is_used = true;
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
extruder_attr[mesh.getSettingAsIndex("extruder_nr")].is_used = true;
}
}
for (unsigned int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
const ExtruderTrain* train = settings->getExtruderTrain(extruder_nr);
const ExtruderTrain* train = meshgroup->getExtruderTrain(extruder_nr);
if (settings->getSettingAsIndex("adhesion_extruder_nr") == int(extruder_nr)
|| (settings->getSettingBoolean("support_enable") && settings->getSettingAsIndex("support_infill_extruder_nr") == int(extruder_nr))
|| (settings->getSettingBoolean("support_enable") && settings->getSettingAsIndex("support_extruder_nr_layer_0") == int(extruder_nr))
|| (settings->getSettingBoolean("support_enable") && settings->getSettingBoolean("support_roof_enable") && settings->getSettingAsIndex("support_roof_extruder_nr") == int(extruder_nr))
)
if (meshgroup->getSettingAsIndex("adhesion_extruder_nr") == int(extruder_nr) && meshgroup->getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
extruder_attr[extruder_nr].is_used = true;
}
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"));
@@ -76,11 +91,11 @@ void GCodeExport::preSetup(const MeshGroup* settings)
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 = settings->getSettingInMicrons("machine_width");
machine_dimensions.y = settings->getSettingInMicrons("machine_depth");
machine_dimensions.z = settings->getSettingInMicrons("machine_height");
machine_dimensions.x = meshgroup->getSettingInMicrons("machine_width");
machine_dimensions.y = meshgroup->getSettingInMicrons("machine_depth");
machine_dimensions.z = meshgroup->getSettingInMicrons("machine_height");
machine_name = settings->getSettingString("machine_name");
machine_name = meshgroup->getSettingString("machine_name");
if (flavor == EGCodeFlavor::BFB)
{
@@ -91,20 +106,27 @@ void GCodeExport::preSetup(const MeshGroup* settings)
new_line = "\n";
}
estimateCalculator.setFirmwareDefaults(settings);
estimateCalculator.setFirmwareDefaults(meshgroup);
}
void GCodeExport::setInitialTemps(const MeshGroup& settings)
{
int start_extruder_nr = 0;
if (settings.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
start_extruder_nr = settings.getSettingAsIndex("adhesion_extruder_nr");
}
for (unsigned int extr_nr = 0; extr_nr < extruder_count; extr_nr++)
{
const ExtruderTrain* extr_train = settings.getExtruderTrain(extr_nr);
assert(extr_train);
double temp = extr_train->getSettingInDegreeCelsius((extr_nr == 0)? "material_print_temperature" : "material_standby_temperature");
const ExtruderTrain& train = *settings.getExtruderTrain(extr_nr);
double print_temp_0 = train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
double print_temp_here = (print_temp_0 != 0)? print_temp_0 : train.getSettingInDegreeCelsius("material_print_temperature");
double temp = ((int)extr_nr == start_extruder_nr)? print_temp_here : train.getSettingInDegreeCelsius("material_standby_temperature");
setInitialTemp(extr_nr, temp);
}
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature");
initial_bed_temp = settings.getSettingInDegreeCelsius("material_bed_temperature_layer_0");
}
void GCodeExport::setInitialTemp(int extruder_nr, double temp)
@@ -155,12 +177,12 @@ std::string GCodeExport::getFileHeader(const double* print_time, const std::vect
prefix << ";PRINT.TIME:" << static_cast<int>(*print_time) << new_line;
}
prefix << ";PRINT.SIZE.MIN.X:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Y:0" << new_line;
prefix << ";PRINT.SIZE.MIN.Z:0" << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(machine_dimensions.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(machine_dimensions.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(machine_dimensions.z) << new_line;
prefix << ";PRINT.SIZE.MIN.X:" << INT2MM(total_bounding_box.min.x) << new_line;
prefix << ";PRINT.SIZE.MIN.Y:" << INT2MM(total_bounding_box.min.y) << new_line;
prefix << ";PRINT.SIZE.MIN.Z:" << INT2MM(total_bounding_box.min.z) << new_line;
prefix << ";PRINT.SIZE.MAX.X:" << INT2MM(total_bounding_box.max.x) << new_line;
prefix << ";PRINT.SIZE.MAX.Y:" << INT2MM(total_bounding_box.max.y) << new_line;
prefix << ";PRINT.SIZE.MAX.Z:" << INT2MM(total_bounding_box.max.z) << new_line;
prefix << ";END_OF_HEADER" << new_line;
return prefix.str();
default:
@@ -189,27 +211,27 @@ void GCodeExport::setOutputStream(std::ostream* stream)
*output_stream << std::fixed;
}
bool GCodeExport::getExtruderIsUsed(int extruder_nr)
bool GCodeExport::getExtruderIsUsed(const int extruder_nr) const
{
return extruder_attr[extruder_nr].is_used;
}
int GCodeExport::getNozzleSize(int extruder_nr)
int GCodeExport::getNozzleSize(const int extruder_nr) const
{
return extruder_attr[extruder_nr].nozzle_size;
}
Point GCodeExport::getExtruderOffset(int id)
Point GCodeExport::getExtruderOffset(const int id) const
{
return extruder_attr[id].nozzle_offset;
}
std::string GCodeExport::getMaterialGUID(int extruder_nr)
std::string GCodeExport::getMaterialGUID(const int extruder_nr) const
{
return extruder_attr[extruder_nr].material_guid;
}
Point GCodeExport::getGcodePos(int64_t x, int64_t y, int extruder_train)
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); }
@@ -251,7 +273,7 @@ EGCodeFlavor GCodeExport::getFlavor()
void GCodeExport::setZ(int z)
{
this->zPos = z;
this->current_layer_z = z;
}
Point3 GCodeExport::getPosition()
@@ -402,7 +424,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:
@@ -459,7 +481,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)
@@ -486,7 +508,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.
@@ -511,10 +533,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);
@@ -526,11 +546,14 @@ 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
total_bounding_box.include(Point3(x, y, z));
if (extrusion_mm3_per_mm < 0)
logWarning("Warning! Negative extrusion move!");
@@ -549,7 +572,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;
@@ -562,7 +585,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);
@@ -570,7 +593,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);
}
@@ -582,7 +605,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);
}
@@ -594,30 +617,22 @@ 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);
@@ -692,8 +707,8 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force, bool ext
{
double speed = ((retraction_diff_e_amount < 0.0)? config->speed : extr_attr.last_retraction_prime_speed) * 60;
current_e_value += retraction_diff_e_amount;
*output_stream << "G1 F" << speed << " "
<< extr_attr.extruderCharacter << std::setprecision(5) << current_e_value << new_line;
*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;
@@ -709,7 +724,17 @@ void GCodeExport::writeZhopStart(int hop_height)
if (hop_height > 0)
{
isZHopped = hop_height;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << new_line;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z + isZHopped} << new_line;
total_bounding_box.include(currentPosition + Point3(0, 0, isZHopped));
}
}
void GCodeExport::writeZhopEnd()
{
if (isZHopped)
{
isZHopped = 0;
*output_stream << "G1 Z" << MMtoStream{currentPosition.z} << new_line;
}
}
@@ -733,6 +758,8 @@ void GCodeExport::startExtruder(int new_extruder)
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;
@@ -796,7 +823,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
{
@@ -812,31 +839,44 @@ void GCodeExport::writeTemperatureCommand(int extruder, double temperature, bool
{
if (!wait && extruder_attr[extruder].currentTemperature == temperature)
return;
if (flavor == EGCodeFlavor::ULTIGCODE)
{ // The UM2 family doesn't support temperature commands (they are fixed in the firmware)
return;
}
if (wait)
*output_stream << "M109";
else
*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;
}
void GCodeExport::writeBedTemperatureCommand(double temperature, bool wait)
{
if (flavor == EGCodeFlavor::ULTIGCODE)
{ // The UM2 family doesn't support temperature commands (they are fixed in the firmware)
return;
}
if (wait)
*output_stream << "M190 S";
else
*output_stream << "M140 S";
*output_stream << temperature << new_line;
*output_stream << PrecisionedDouble{1, temperature} << new_line;
}
void GCodeExport::writeAcceleration(double acceleration)
{
if (current_acceleration != acceleration)
{
*output_stream << "M204 S" << acceleration << new_line; // Print and Travel acceleration
*output_stream << "M204 S" << PrecisionedDouble{0, acceleration} << new_line; // Print and Travel acceleration
current_acceleration = acceleration;
estimateCalculator.setAcceleration(acceleration);
}
@@ -846,12 +886,35 @@ void GCodeExport::writeJerk(double jerk)
{
if (current_jerk != jerk)
{
*output_stream << "M205 X" << jerk << new_line;
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);
+39 -11
Ver Arquivo
@@ -42,7 +42,7 @@ private:
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 the current meshgroup
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;
@@ -97,8 +97,18 @@ private:
double currentSpeed; //!< The current speed (F values / 60) in mm/s
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
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!
AABB3D total_bounding_box; //!< The bounding box of all g-code.
/*!
* The z position to be used on the next xy move, if the head wasn't in the correct z position yet.
*
* \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;
@@ -167,15 +177,15 @@ public:
void setOutputStream(std::ostream* stream);
bool getExtruderIsUsed(int extruder_nr); //!< Returns whether the extruder with the given index is used up until the current meshgroup
bool getExtruderIsUsed(const int extruder_nr) const; //!< Returns whether the extruder with the given index is used up until the current meshgroup
int getNozzleSize(int extruder_nr);
int getNozzleSize(const int extruder_nr) const;
Point getExtruderOffset(int id);
Point getExtruderOffset(const int id) const;
std::string getMaterialGUID(int extruder_nr); //!< returns the GUID of the material used for the nozzle with id \p extruder_nr
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(int64_t x, int64_t y, int extruder_train);
Point getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const;
void setFlavor(EGCodeFlavor flavor);
EGCodeFlavor getFlavor();
@@ -241,15 +251,15 @@ 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, bool extruder_switch = false);
@@ -260,6 +270,12 @@ public:
*/
void writeZhopStart(int hop_height);
/*!
* End a z hop: go back to the layer height
*
*/
void writeZhopEnd();
/*!
* Start the new_extruder:
* - set new extruder
@@ -307,6 +323,18 @@ public:
*/
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
*
+185 -78
Ver Arquivo
@@ -1,10 +1,11 @@
/** 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"
#include "raft.h" // getTotalExtraLayers
namespace cura {
@@ -22,11 +23,14 @@ 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)
ExtruderPlan::ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config)
: extruder(extruder)
, required_temp(-1)
, heated_pre_travel_time(0)
, initial_printing_temperature(-1)
, printing_temperature(-1)
, start_position(start_position)
, layer_nr(layer_nr)
, is_initial_layer(is_initial_layer)
, layer_thickness(layer_thickness)
, fan_speed_layer_time_settings(fan_speed_layer_time_settings)
, retraction_config(retraction_config)
@@ -89,9 +93,10 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, 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)
, is_initial_layer(layer_nr == 0 - Raft::getTotalExtraLayers(storage))
, z(z)
, layer_thickness(layer_thickness)
, start_position(last_position)
@@ -102,10 +107,10 @@ GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int
, 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, start_position, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
extruder_plans.emplace_back(current_extruder, start_position, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[current_extruder], storage.retraction_config_per_extruder[current_extruder]);
comb = nullptr;
was_inside = is_inside_mesh;
is_inside = false; // assumes the next move will not be to inside a layer part (overwritten just before going into a layer part)
was_inside = storage.getPartInside(layer_nr, start_position);
is_inside = nullptr; // 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);
@@ -134,14 +139,7 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
}
if (layer_nr < 0)
{ // when a raft is present
if (combing_mode == CombingMode::NO_SKIN)
{
return Polygons();
}
else
{
return storage.raftOutline.offset(MM2INT(0.1));
}
return Polygons();
}
else
{
@@ -169,7 +167,7 @@ Polygons GCodePlanner::computeCombBoundaryInside(CombingMode combing_mode)
}
}
void GCodePlanner::setIsInside(bool _is_inside)
void GCodePlanner::setIsInside(SliceLayerPart* _is_inside)
{
is_inside = _is_inside;
}
@@ -180,7 +178,7 @@ bool GCodePlanner::setExtruder(int extruder)
{
return false;
}
setIsInside(false);
setIsInside(nullptr);
{ // handle end position of the prev extruder
SettingsBaseVirtual* train = getLastPlannedExtruderTrainSettings();
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
@@ -202,7 +200,8 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
extruder_plans.emplace_back(extruder, lastPosition, layer_nr, is_initial_layer, layer_thickness, fan_speed_layer_time_settings_per_extruder[extruder], storage.retraction_config_per_extruder[extruder]);
assert((int)extruder_plans.size() <= storage.meshgroup->getExtruderCount() && "Never use the same extruder twice on one layer!");
}
last_planned_extruder_setting_base = storage.meshgroup->getExtruderTrain(extruder);
@@ -226,11 +225,27 @@ bool GCodePlanner::setExtruder(int extruder)
return true;
}
void GCodePlanner::moveInsideCombBoundary(int distance)
void GCodePlanner::moveInsideCombBoundary(int distance, const SliceLayerPart& part)
{
int max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
// this function is to be used to move from the boudary of a part to inside the part
int max_dist2 = MM2INT(2.0) * MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
Point p = lastPosition; // copy, since we are going to move p
{ // first move inside the last part, so that the chance is higher that we move inside the same part
const Polygons* comb_boundary_here;
if (part.insets.size() > 1)
{
comb_boundary_here = &part.insets[1];
}
else if (part.insets.size() == 1)
{
comb_boundary_here = &part.insets[0];
}
else
{
comb_boundary_here = &part.print_outline;
}
PolygonUtils::moveInside(*comb_boundary_here, p, distance);
}
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
{
//Move inside again, so we move out of tight 90deg corners
@@ -244,7 +259,7 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
}
}
void GCodePlanner::addTravel(Point p)
GCodePath& GCodePlanner::addTravel(Point p)
{
GCodePath* path = nullptr;
GCodePathConfig& travel_config = storage.travel_config_per_extruder[getExtruder()];
@@ -322,7 +337,7 @@ void GCodePlanner::addTravel(Point p)
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.
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1);
moveInsideCombBoundary(extr->getSettingInMicrons((extr->getSettingAsCount("wall_line_count") > 1) ? "wall_line_width_x" : "wall_line_width_0") * 1, *was_inside);
}
path = getLatestPathWithConfig(&travel_config, SpaceFillType::None);
path->retract = true;
@@ -330,11 +345,12 @@ void GCodePlanner::addTravel(Point p)
}
}
addTravel_simple(p, path);
GCodePath& ret = addTravel_simple(p, path);
was_inside = is_inside;
return ret;
}
void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
GCodePath& GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
@@ -342,6 +358,7 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
}
path->points.push_back(p);
lastPosition = p;
return *path;
}
@@ -351,22 +368,47 @@ void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillT
lastPosition = p;
}
void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, bool spiralize)
void GCodePlanner::addPolygon(PolygonRef polygon, int start_idx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, coord_t wall_0_wipe_dist, bool spiralize)
{
Point p0 = polygon[startIdx];
Point p0 = polygon[start_idx];
addTravel(p0);
for(unsigned int i=1; i<polygon.size(); i++)
for (unsigned int point_idx = 1; point_idx < polygon.size(); point_idx++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[startIdx];
Point& p1 = polygon[start_idx];
float flow = (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0;
addExtrusionMove(p1, config, SpaceFillType::Polygons, flow, spiralize);
if (wall_0_wipe_dist > 0)
{ // apply outer wall wipe
p0 = polygon[start_idx];
int distance_traversed = 0;
for (unsigned int point_idx = 1; ; point_idx++)
{
Point p1 = polygon[(start_idx + point_idx) % polygon.size()];
int p0p1_dist = vSize(p1 - p0);
if (distance_traversed + p0p1_dist >= wall_0_wipe_dist)
{
Point vector = p1 - p0;
Point half_way = p0 + normal(vector, wall_0_wipe_dist - distance_traversed);
addTravel_simple(half_way);
break;
}
else
{
addTravel_simple(p1);
distance_traversed += p0p1_dist;
}
p0 = p1;
}
forceNewPathStart();
}
}
else
{
@@ -374,7 +416,7 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
}
}
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, bool spiralize)
void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation, EZSeamType z_seam_type, coord_t wall_0_wipe_dist, bool spiralize)
{
if (polygons.size() == 0)
{
@@ -388,7 +430,7 @@ void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* c
orderOptimizer.optimize();
for (unsigned int poly_idx : orderOptimizer.polyOrder)
{
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, spiralize);
addPolygon(polygons[poly_idx], orderOptimizer.polyStart[poly_idx], config, wall_overlap_computation, wall_0_wipe_dist, spiralize);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
@@ -563,22 +605,23 @@ void ExtruderPlan::processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_t
}
/*
Supposing no influence of minimal layer time; i.e. layer time > min layer time fan speed min:
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
zero..|/__________________
layer nr >
max.. fan 'full' on layer
| :
| :
^ min..|..:________________
fan | /
speed | /
speed_0..|/
|
|__________________
layer nr >
*/
if (layer_nr < fsml.cool_fan_full_layer)
{
//Slow down the fan on the layers below the [cool_fan_full_layer], where layer 0 is speed 0.
fan_speed = fan_speed * layer_nr / fsml.cool_fan_full_layer;
fan_speed = fsml.cool_fan_speed_0 + (fan_speed - fsml.cool_fan_speed_0) * std::max(0, layer_nr) / fsml.cool_fan_full_layer;
}
}
@@ -608,31 +651,47 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
{
completeConfigs();
CommandSocket::setLayerForSend(layer_nr);
CommandSocket::setSendCurrentPosition( gcode.getPositionXY() );
gcode.setLayerNr(layer_nr);
gcode.writeLayerComment(layer_nr);
if (layer_nr == 1 - Raft::getTotalExtraLayers(storage))
{
bool wait = false;
gcode.writeBedTemperatureCommand(storage.getSettingInDegreeCelsius("material_bed_temperature"), wait);
}
gcode.setZ(z);
GCodePathConfig* last_extrusion_config = nullptr; // used to check whether we need to insert a TYPE comment in the gcode.
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++)
{
RetractionConfig& retraction_config = storage.retraction_config_per_extruder[gcode.getExtruderNr()];
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, 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);
gcode.writeTemperatureCommand(extruder, extruder_plan.initial_printing_temperature, wait);
}
// prime extruder if it hadn't been used yet
@@ -642,27 +701,46 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
if (extruder_plan.prev_extruder_standby_temp)
{ // turn off previous extruder
constexpr bool wait = false;
gcode.writeTemperatureCommand(prev_extruder, *extruder_plan.prev_extruder_standby_temp, wait);
double prev_extruder_temp = *extruder_plan.prev_extruder_standby_temp;
int prev_layer_nr = (extruder_plan_idx == 0)? layer_nr - 1 : layer_nr;
if (prev_layer_nr == storage.max_print_height_per_extruder[prev_extruder])
{
prev_extruder_temp = 0; // TODO ? should there be a setting for extruder_off_temperature ?
}
gcode.writeTemperatureCommand(prev_extruder, prev_extruder_temp, wait);
}
}
else if (extruder_plan_idx == 0 && layer_nr != 0 && storage.meshgroup->getExtruderTrain(extruder)->getSettingBoolean("retract_at_layer_change"))
{
gcode.writeRetraction(&retraction_config);
}
gcode.writeFanCommand(extruder_plan.getFanSpeed());
std::vector<GCodePath>& paths = extruder_plan.paths;
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 (storage.getSettingBoolean("acceleration_enabled"))
if (acceleration_enabled)
{
gcode.writeAcceleration(path.config->getAcceleration());
}
if (storage.getSettingBoolean("jerk_enabled"))
if (jerk_enabled)
{
gcode.writeJerk(path.config->getJerk());
}
@@ -674,27 +752,31 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
{
gcode.writeZhopStart(retraction_config.zHop);
}
else
{
gcode.writeZhopEnd();
}
}
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
// Apply the relevant factor
if (path.config->isTravelPath())
speed *= extruder_plan.getTravelSpeedFactor();
else
speed *= extruder_plan.getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config_per_extruder[extruder], nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
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->isTravelPath())
{ // early comp for travel paths, which are handled more simply
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
@@ -731,7 +813,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
&& 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;
}
@@ -739,7 +821,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
{
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());
}
}
@@ -772,7 +854,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layer_thickness * length / totalLength);
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());
}
}
@@ -780,7 +862,6 @@ void GCodePlanner::writeGCode(GCodeExport& gcode)
}
} // paths for this extruder /\ .
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
if (train->getSettingBoolean("cool_lift_head") && extruder_plan.extraTime > 0.0)
{
gcode.writeComment("Small layer, adding delay");
@@ -815,7 +896,7 @@ void GCodePlanner::overrideFanSpeeds(double 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)
{
@@ -838,39 +919,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)
{
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_layer_0");
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("acceleration_layer_0");
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_infill)->getSettingInMillimetersPerSecond("jerk_layer_0");
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);
int extruder_nr_support_roof = storage.getSettingAsIndex("support_roof_extruder_nr");
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_roof)->getSettingInMillimetersPerSecond("speed_layer_0");
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_roof)->getSettingInMillimetersPerSecond("acceleration_layer_0");
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_roof)->getSettingInMillimetersPerSecond("jerk_layer_0");
storage.support_roof_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Support roof (global).
int extruder_nr_support_skin = storage.getSettingAsIndex("support_interface_extruder_nr");
initial_layer_speed_config.speed = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("speed_print_layer_0");
initial_layer_speed_config.acceleration = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("acceleration_print_layer_0");
initial_layer_speed_config.jerk = storage.meshgroup->getExtruderTrain(extruder_nr_support_skin)->getSettingInMillimetersPerSecond("jerk_print_layer_0");
storage.support_skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); ++extruder_nr)
{
const ExtruderTrain* extruder_train = storage.meshgroup->getExtruderTrain(extruder_nr);
initial_layer_speed_config.speed = extruder_train->getSettingInMillimetersPerSecond("speed_travel_layer_0");
initial_layer_speed_config.acceleration = extruder_train->getSettingInMillimetersPerSecond("acceleration_travel_layer_0");
initial_layer_speed_config.jerk = extruder_train->getSettingInMillimetersPerSecond("jerk_travel_layer_0");
//Travel speed (per extruder).
storage.travel_config_per_extruder[extruder_nr].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
}
for (SliceMeshStorage& mesh : storage.meshes)
{
initial_layer_speed_config.speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
initial_layer_speed_config.acceleration = mesh.getSettingInMillimetersPerSecond("acceleration_layer_0");
initial_layer_speed_config.jerk = mesh.getSettingInMillimetersPerSecond("jerk_layer_0");
initial_layer_speed_config.speed = mesh.getSettingInMillimetersPerSecond("speed_print_layer_0");
initial_layer_speed_config.acceleration = mesh.getSettingInMillimetersPerSecond("acceleration_print_layer_0");
initial_layer_speed_config.jerk = mesh.getSettingInMillimetersPerSecond("jerk_print_layer_0");
//Outer wall speed (per mesh).
mesh.inset0_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Inner wall speed (per mesh).
mesh.insetX_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
//Skin speed (per mesh).
mesh.skin_config.smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
for (unsigned int idx = 0; idx < MAX_INFILL_COMBINE; idx++)
{
//Infill speed (per combine part per mesh).
mesh.infill_config[idx].smoothSpeed(initial_layer_speed_config, layer_nr, initial_speedup_layers);
}
}
}
else if (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();
@@ -1007,10 +1114,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());
}
+35 -27
Ver Arquivo
@@ -1,3 +1,4 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef GCODE_PLANNER_H
#define GCODE_PLANNER_H
@@ -19,6 +20,7 @@ namespace cura
{
class SliceDataStorage;
class SliceLayerPart;
/*!
* A gcode command to insert before a specific path.
@@ -38,7 +40,9 @@ struct NozzleTempInsert
, extruder(extruder)
, temperature(temperature)
, wait(wait)
{}
{
assert(temperature != 0 && temperature != -1 && "Temperature command must be set!");
}
/*!
* Write the temperature command at the current position in the gcode.
@@ -280,7 +284,10 @@ protected:
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
int extruder; //!< The extruder used for this paths in the current plan.
double required_temp; //!< The required temperature at the start of this extruder plan.
double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature
double initial_printing_temperature; //!< The required temperature at the start of this extruder plan.
double printing_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the initial and final print temp
std::optional<std::list<NozzleTempInsert>::iterator> printing_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing temperature of the next extruder plan (if it has the same extruder).
std::optional<double> prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder.
TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan.
@@ -293,7 +300,7 @@ public:
* \param extruder The extruder number for which this object is a plan.
* \param start_position The position the head is when this extruder plan starts
*/
ExtruderPlan(int extruder, Point start_position, int layer_nr, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
/*!
* Add a new Insert, constructed with the given arguments
@@ -397,6 +404,8 @@ protected:
Point start_position; //!< The position the print head was at at the start of this extruder plan
int layer_nr; //!< The layer number at which we are currently printing.
bool is_initial_layer; //!< Whether this extruder plan is printed on the very first layer (which might be raft)
int layer_thickness; //!< The thickness of this layer in Z-direction
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
@@ -453,6 +462,7 @@ private:
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
int layer_nr; //!< The layer number of this layer plan
int is_initial_layer; //!< Whether this is the first layer (which might be raft)
int z;
@@ -465,8 +475,8 @@ private:
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
SliceLayerPart* was_inside; //!< The layer part the last planned (extrusion) move was inside (if any)
SliceLayerPart* is_inside; //!< The layer part the destination of the next planned travel move is inside (if any)
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
Comb* comb;
@@ -485,7 +495,8 @@ private:
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
public:
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
*
@@ -497,7 +508,7 @@ private:
* - when changing extruder, the same travel config is used, but its extruder field is changed.
*/
void forceNewPathStart();
public:
/*!
*
* \param fan_speed_layer_time_settings_per_extruder The fan speed and layer time settings for each extruder.
@@ -506,7 +517,7 @@ public:
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_height, Point last_position, int current_extruder, 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);
@@ -542,19 +553,11 @@ public:
return was_inside;
}
/*!
* 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);
}
/*!
@@ -562,8 +565,9 @@ public:
*
* Features like infill, walls, skin etc. are considered inside.
* Features like prime tower and support are considered outside.
* \param inside_part The part in which the newly planned position is inside, or nullptr if not inside anything
*/
void setIsInside(bool going_to_comb);
void setIsInside(SliceLayerPart* inside_part);
bool setExtruder(int extruder);
@@ -583,7 +587,7 @@ public:
*
* \param p The point to travel to
*/
void addTravel(Point p);
GCodePath& addTravel(Point p);
/*!
* Add a travel path to a certain point and retract if needed.
@@ -593,7 +597,7 @@ public:
* \param p The point to travel to
* \param path (optional) The travel path to which to add the point \p p
*/
void addTravel_simple(Point p, GCodePath* path = nullptr);
GCodePath& addTravel_simple(Point p, GCodePath* path = nullptr);
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
@@ -612,9 +616,10 @@ public:
* \param startIdx The index of the starting vertex of the \p polygon
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param wall_0_wipe_dist The distance to travel along the polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
*/
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, bool spiralize = false);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add polygons to the gcode with optimized order.
@@ -629,9 +634,10 @@ public:
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param z_seam_type The seam type / poly start optimizer
* \param wall_0_wipe_dist The distance to travel along each polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
*/
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, bool spiralize = false);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add lines to the gcode with optimized order.
@@ -660,8 +666,9 @@ public:
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();
@@ -708,8 +715,9 @@ public:
* This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air.
*
* \param distance The distance to the comb boundary after we moved inside it.
* \param part_outline The part in which we last resided
*/
void moveInsideCombBoundary(int distance);
void moveInsideCombBoundary(int distance, const SliceLayerPart& part);
};
}//namespace cura
+62 -19
Ver Arquivo
@@ -21,7 +21,6 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
const Polygons* outline = &in_outline;
Polygons outline_offsetted;
switch(pattern)
{
@@ -41,9 +40,10 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
outline_offsetted = in_outline.offset(outline_offset - infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
outline = &outline_offsetted;
generateConcentricInfill(*outline, result_polygons, line_distance);
generateConcentricInfill(result_polygons, line_distance);
break;
case EFillMethod::CONCENTRIC_3D:
generateConcentric3DInfill(result_polygons);
break;
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
@@ -54,15 +54,53 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines)
}
}
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
void Infill::generateConcentricInfill(Polygons& result, int inset_value)
{
while(outline.size() > 0)
Polygons first_concentric_wall = in_outline.offset(outline_offset - line_distance + infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
result.add(first_concentric_wall);
if (perimeter_gaps)
{
result.add(outline);
outline = outline.offset(-inset_value);
}
const Polygons inner = first_concentric_wall.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
const Polygons gaps_here = in_outline.difference(inner);
perimeter_gaps->add(gaps_here);
}
generateConcentricInfill(first_concentric_wall, result, inset_value);
}
void Infill::generateConcentricInfill(Polygons& first_concentric_wall, Polygons& result, int inset_value)
{
Polygons* prev_inset = &first_concentric_wall;
Polygons next_inset;
while (prev_inset->size() > 0)
{
next_inset = prev_inset->offset(-inset_value);
result.add(next_inset);
if (perimeter_gaps)
{
const Polygons outer = prev_inset->offset(-infill_line_width / 2 - perimeter_gaps_extra_offset);
const Polygons inner = next_inset.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
const Polygons gaps_here = outer.difference(inner);
perimeter_gaps->add(gaps_here);
}
prev_inset = &next_inset;
}
}
void Infill::generateConcentric3DInfill(Polygons& result)
{
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
Polygons first_wall;
// in contrast to concentric infill we dont do "- infill_line_width / 2" cause this is already handled by the max two lines above
first_wall = in_outline.offset(outline_offset - shift);
generateConcentricInfill(first_wall, result, period);
first_wall = in_outline.offset(outline_offset - period + shift);
generateConcentricInfill(first_wall, result, period);
}
void Infill::generateGridInfill(Polygons& result)
{
@@ -80,14 +118,15 @@ void Infill::generateCubicInfill(Polygons& result)
void Infill::generateTetrahedralInfill(Polygons& result)
{
int shift = int64_t(one_over_sqrt_2 * z) % line_distance;
shift = std::min(shift, line_distance - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, line_distance / 2 - infill_line_width / 2); // don't put lines too close to each other
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle, -shift);
generateLineInfill(result, line_distance, fill_angle + 90, shift);
generateLineInfill(result, line_distance, fill_angle + 90, -shift);
generateLineInfill(result, period, fill_angle, shift);
generateLineInfill(result, period, fill_angle, -shift);
generateLineInfill(result, period, fill_angle + 90, shift);
generateLineInfill(result, period, fill_angle + 90, -shift);
}
void Infill::generateTriangleInfill(Polygons& result)
@@ -210,14 +249,18 @@ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& resul
if (outline_offset != 0)
{
outline = in_outline.offset(outline_offset);
if (perimeter_gaps)
{
perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset)));
}
}
else
{
outline = in_outline;
}
outline = outline.offset(infill_overlap);
outline = outline.offset(infill_overlap);
if (outline.size() == 0)
{
return;
+45 -4
Ver Arquivo
@@ -20,6 +20,8 @@ namespace cura
class Infill
{
static constexpr int perimeter_gaps_extra_offset = 15; // extra offset so that the perimeter gaps aren't created everywhere due to rounding errors
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
@@ -29,12 +31,33 @@ class Infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
int64_t z; //!< height of the layer for which we generate infill
int64_t shift; //!< shift of the scanlines in the direction perpendicular to the fill_angle
Polygons* perimeter_gaps; //!< (optional output) The areas in between consecutive insets when Concentric infill is used.
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0)
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, int infill_line_width, int line_distance, int infill_overlap, double fill_angle, int64_t z, int64_t shift, bool connected_zigzags = false, bool use_endpieces = false)
/*!
* \warning If \p perimeter_gaps is given, then the difference between the \p in_outline
* and the polygons which result from offsetting it by the \p outline_offset
* and then expanding it again by half the \p infill_line_width
* is added to the \p perimeter_gaps
*
* \param[out] perimeter_gaps (optional output) The areas in between consecutive insets when Concentric infill is used.
*/
Infill(EFillMethod pattern
, const Polygons& in_outline
, int outline_offset
, int infill_line_width
, int line_distance
, int infill_overlap
, double fill_angle
, int64_t z
, int64_t shift
, Polygons* perimeter_gaps = nullptr
, bool connected_zigzags = false
, bool use_endpieces = false
)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
@@ -44,6 +67,7 @@ public:
, fill_angle(fill_angle)
, z(z)
, shift(shift)
, perimeter_gaps(perimeter_gaps)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
@@ -68,13 +92,30 @@ private:
* \param line_distance the width of the scan segments
*/
static inline int computeScanSegmentIdx(int x, int line_distance);
/*!
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
* Generate sparse concentric infill
*
* Also adds \ref Inifll::perimeter_gaps between \ref Infill::in_outline and the first wall
*
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateConcentricInfill(Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill starting from a specific outer wall
* \param first_wall The outer wall from which to start
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons& first_wall, Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill
* \param result (output) The resulting polygons
*/
void generateConcentric3DInfill(Polygons& result);
/*!
* Generate a rectangular grid of infill lines
-1
Ver Arquivo
@@ -46,7 +46,6 @@ void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers,
{
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
mesh.layers.push_back(SliceLayer());
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
+80 -69
Ver Arquivo
@@ -25,31 +25,32 @@ 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.def.json>]\n");
cura::logError(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
cura::logError(" -j<settings.def.json>\n\tLoad settings.json file to register all settings and their defaults\n");
cura::logError("\n");
cura::logError("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e<extruder_nr>] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
cura::logError(" -v\n\tIncrease the verbose level (show log messages).\n");
cura::logError(" -p\n\tLog progress information.\n");
cura::logError(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n");
cura::logError(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
cura::logError(" -l <model_file>\n\tLoad an STL model. \n");
cura::logError(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
cura::logError(" -e<extruder_nr>\n\tSwitch setting focus to the extruder train with the given number.\n");
cura::logError(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
cura::logError(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
cura::logError("\n");
cura::logError("The settings are appended to the last supplied object:\n");
cura::logError("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
cura::logError("\n");
cura::logError("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search paths delimited by a (semi-)colon.\n");
cura::logError("\n");
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.
@@ -97,7 +98,8 @@ void connect(int argc, char **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:
@@ -124,7 +126,7 @@ 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++)
@@ -139,14 +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
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, train);
meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
}
meshgroup->finalize();
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
@@ -156,7 +159,6 @@ void slice(int argc, char **argv)
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_extruder_train = meshgroup->createExtruderTrain(0);
last_settings_object = meshgroup;
SettingRegistry::getInstance()->loadExtruderJSONsettings(0, last_extruder_train);
}catch(...){
cura::logError("Unknown exception\n");
@@ -180,7 +182,8 @@ void slice(int argc, char **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':
@@ -188,17 +191,22 @@ void slice(int argc, char **argv)
extruder_train_nr = int(*str - '0'); // TODO: parse int instead (now "-e10"="-e:" , "-e11"="-e;" , "-e12"="-e<" .. etc)
last_settings_object = meshgroup->createExtruderTrain(extruder_train_nr);
last_extruder_train = last_settings_object;
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_train_nr, last_extruder_train);
break;
case 'l':
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
{
@@ -251,8 +259,7 @@ void slice(int argc, char **argv)
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
ExtruderTrain* train = meshgroup->createExtruderTrain(extruder_train_nr); // create new extruder train objects or use already existing ones
SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_train_nr, train);
meshgroup->createExtruderTrain(extruder_train_nr); // create new extruder train objects or use already existing ones
}
@@ -297,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)
@@ -348,6 +355,7 @@ int main(int argc, char **argv)
bool inherit_viz = false;
bool warning_viz = false;
bool error_viz = false;
bool global_only_viz = false;
if (argc >= 6)
{
char* str = argv[5];
@@ -369,6 +377,9 @@ int main(int argc, char **argv)
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);
@@ -380,26 +391,26 @@ int main(int argc, char **argv)
}
else
{
cura::logError("\n");
cura::logError("usage:\n");
cura::logError("CuraEngine analyse <fdmPrinter.def.json> <output.gv> <engine_settings_list> -[p|i|e|w]\n");
cura::logError("\tGenerate a grpah to visualize the setting inheritance structure.\n");
cura::logError("\t<fdmPrinter.def.json>\n\tThe base seting definitions file.\n");
cura::logError("\t<output.gv>\n\tThe output file.\n");
cura::logError("\t<engine_settings_list>\n\tA text file with all setting keys used in the engine, separated by newlines.\n");
cura::logError("\t-[p|i|e|w]\n\tOptions for what to include in the visualization\n");
cura::logError("\t\tp\tVisualize the parent-child relationship.\n");
cura::logError("\t\ti\tVisualize inheritance function relationships.\n");
cura::logError("\t\te\tVisualize (max/min) error function relationships.\n");
cura::logError("\t\tw\tVisualize (max/min) warning function relationships.\n");
cura::logError("\n");
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);
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("ERROR: Failed to analyse json file: %s\n", argv[2]);
cura::logError("Failed to analyse json file: %s\n", argv[2]);
}
exit(0);
}
+9 -3
Ver Arquivo
@@ -72,6 +72,13 @@ AABB3D Mesh::getAABB() const
{
return aabb;
}
void Mesh::expandXY(int64_t offset)
{
if (offset)
{
aabb.expandXY(offset);
}
}
int Mesh::findIndexOfVertex(const Point3& v)
@@ -133,7 +140,7 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
}
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]; }
@@ -171,7 +178,6 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
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)
{
@@ -179,7 +185,7 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
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;
}
+1
Ver Arquivo
@@ -72,6 +72,7 @@ public:
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).
+24 -7
Ver Arquivo
@@ -3,20 +3,26 @@
namespace cura
{
void carveMultipleVolumes(std::vector<Slicer*> &volumes)
void carveMultipleVolumes(std::vector<Slicer*> &volumes, bool alternate_carve_order)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for (unsigned int volume_1_idx = 0; volume_1_idx < volumes.size(); volume_1_idx++)
for (unsigned int volume_1_idx = 1; volume_1_idx < volumes.size(); volume_1_idx++)
{
Slicer& volume_1 = *volumes[volume_1_idx];
if (volume_1.mesh->getSettingBoolean("infill_mesh"))
if (volume_1.mesh->getSettingBoolean("infill_mesh")
|| volume_1.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_1.mesh->getSettingBoolean("support_mesh")
)
{
continue;
}
for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++)
{
Slicer& volume_2 = *volumes[volume_2_idx];
if (volume_2.mesh->getSettingBoolean("infill_mesh"))
if (volume_2.mesh->getSettingBoolean("infill_mesh")
|| volume_2.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_2.mesh->getSettingBoolean("support_mesh")
)
{
continue;
}
@@ -28,7 +34,14 @@ void carveMultipleVolumes(std::vector<Slicer*> &volumes)
{
SlicerLayer& layer1 = volume_1.layers[layerNr];
SlicerLayer& layer2 = volume_2.layers[layerNr];
layer1.polygons = layer1.polygons.difference(layer2.polygons);
if (alternate_carve_order && layerNr % 2 == 0)
{
layer2.polygons = layer2.polygons.difference(layer1.polygons);
}
else
{
layer1.polygons = layer1.polygons.difference(layer2.polygons);
}
}
}
}
@@ -48,6 +61,8 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
{
int overlap = volume->mesh->getSettingInMicrons("multiple_mesh_overlap");
if (volume->mesh->getSettingBoolean("infill_mesh")
|| volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| volume->mesh->getSettingBoolean("support_mesh")
|| overlap == 0)
{
continue;
@@ -60,7 +75,10 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
for (Slicer* other_volume : volumes)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| other_volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| other_volume->mesh->getSettingBoolean("support_mesh")
|| !other_volume->mesh->getAABB().hit(aabb)
|| other_volume == volume
)
{
continue;
@@ -68,10 +86,9 @@ void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
SlicerLayer& other_volume_layer = other_volume->layers[layer_nr];
all_other_volumes = all_other_volumes.unionPolygons(other_volume_layer.polygons.offset(offset_to_merge_other_merged_volumes));
}
all_other_volumes = all_other_volumes.offset(-offset_to_merge_other_merged_volumes);
SlicerLayer& volume_layer = volume->layers[layer_nr];
volume_layer.polygons.unionPolygons(all_other_volumes.intersection(volume_layer.polygons.offset(overlap / 2)));
volume_layer.polygons = volume_layer.polygons.unionPolygons(all_other_volumes.intersection(volume_layer.polygons.offset(overlap / 2)));
}
}
}
+5 -1
Ver Arquivo
@@ -7,7 +7,11 @@
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
namespace cura {
void carveMultipleVolumes(std::vector<Slicer*> &meshes);
/*!
*
* \param alternate_carve_order Whether to switch which model carves out of which with every layer
*/
void carveMultipleVolumes(std::vector<Slicer*> &meshes, bool alternate_carve_order);
/*!
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
+7 -5
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
@@ -153,7 +153,7 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
void LineOrderOptimizer::optimize()
{
int gridSize = 5000; // the size of the cells in the hash grid. TODO
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
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
@@ -18,7 +18,7 @@ class PathOrderOptimizer
{
public:
EZSeamType type;
Point startPoint; //!< The location of the nozzle before starting to print the current layer
Point startPoint; //!< A location near the prefered start location
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
std::vector<int> polyStart; //!< polygons[i][polyStart[i]] = point of polygon i which is to be the starting point in printing the polygon
std::vector<int> polyOrder; //!< the optimized order as indices in #polygons
+133 -50
Ver Arquivo
@@ -2,62 +2,74 @@
#include "Comb.h"
#include <algorithm>
#include <functional> // function
#include <unordered_set>
#include "../utils/polygonUtils.h"
#include "../utils/PolygonsPointIndex.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"
namespace cura {
// boundary_outside is only computed when it's needed!
Polygons& Comb::getBoundaryOutside()
Polygons Comb::getCombOutlines()
{
if (!boundary_outside)
if (layer_nr >= 0)
{
boundary_outside = new Polygons();
*boundary_outside = storage.getLayerOutlines(layer_nr, false).offset(offset_from_outlines_outside);
bool include_helper_parts = false;
return storage.getLayerOutlines(layer_nr, include_helper_parts);
}
else
{
return storage.raftOutline;
}
return *boundary_outside;
}
BucketGrid2D<PolygonsPointIndex>& Comb::getOutsideLocToLine()
LocToLineGrid& Comb::getOutsideLocToLine()
{
Polygons& outside = getBoundaryOutside();
if (!outside_loc_to_line)
{
outside_loc_to_line = PolygonUtils::createLocToLineGrid(outside, offset_from_inside_to_outside * 3 / 2);
}
return *outside_loc_to_line;
}
Polygons& Comb::getBoundaryOutside()
{
return *boundary_outside;
}
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: 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)
, max_move_inside_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 !!
, partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !!
, outlines(getCombOutlines())
, inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset))
, boundary_outside(
[&storage, layer_nr, travel_avoid_distance]()
{
return storage.getLayerOutlines(layer_nr, false).offset(travel_avoid_distance);
}
)
, outside_loc_to_line(
[](Comb* comber, const int64_t offset_from_inside_to_outside)
{
return PolygonUtils::createLocToLineGrid(comber->getBoundaryOutside(), offset_from_inside_to_outside * 3 / 2);
}
, this
, offset_from_inside_to_outside
)
{
}
Comb::~Comb()
{
if (boundary_outside)
if (inside_loc_to_line)
{
delete boundary_outside;
}
if (outside_loc_to_line)
{
delete outside_loc_to_line;
delete inside_loc_to_line;
}
}
@@ -84,7 +96,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
return LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
return LinePolygonsCrossings::comb(part, *inside_loc_to_line, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
@@ -99,31 +111,31 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
return false;
}
Crossing start_crossing(startPoint, startInside, start_part_idx, start_part_boundary_poly_idx, boundary_inside);
Crossing end_crossing(endPoint, endInside, end_part_idx, end_part_boundary_poly_idx, boundary_inside);
Crossing start_crossing(startPoint, startInside, start_part_idx, start_part_boundary_poly_idx, boundary_inside, inside_loc_to_line);
Crossing end_crossing(endPoint, endInside, end_part_idx, end_part_boundary_poly_idx, boundary_inside, inside_loc_to_line);
{ // find crossing over the in-between area between inside and outside
start_crossing.findCrossingInOrMid(partsView_inside, endPoint);
end_crossing.findCrossingInOrMid(partsView_inside, start_crossing.in_or_mid);
}
bool avoid_other_parts_now = avoid_other_parts;
if (avoid_other_parts_now && vSize2(start_crossing.in_or_mid - end_crossing.in_or_mid) < offset_from_inside_to_outside * offset_from_inside_to_outside * 4)
bool skip_avoid_other_parts_path = false;
if (skip_avoid_other_parts_path && vSize2(start_crossing.in_or_mid - end_crossing.in_or_mid) < offset_from_inside_to_outside * offset_from_inside_to_outside * 4)
{ // parts are next to eachother, i.e. the direct crossing will always be smaller than two crossings via outside
avoid_other_parts_now = false;
skip_avoid_other_parts_path = true;
}
if (avoid_other_parts_now)
if (avoid_other_parts && !skip_avoid_other_parts_path)
{ // compute the crossing points when moving through air
Polygons& outside = getBoundaryOutside(); // comb through all air, since generally the outside consists of a single part
// comb through all air, since generally the outside consists of a single part
bool success = start_crossing.findOutside(outside, end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this);
bool success = start_crossing.findOutside(*boundary_outside, end_crossing.in_or_mid, fail_on_unavoidable_obstacles, *this);
if (!success)
{
return false;
}
success = end_crossing.findOutside(outside, start_crossing.out, fail_on_unavoidable_obstacles, *this);
success = end_crossing.findOutside(*boundary_outside, start_crossing.out, fail_on_unavoidable_obstacles, *this);
if (!success)
{
return false;
@@ -136,7 +148,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
// start to boundary
assert(start_crossing.dest_part.size() > 0 && "The part we start inside when combing should have been computed already!");
combPaths.emplace_back();
bool combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, startPoint, start_crossing.in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(start_crossing.dest_part, *inside_loc_to_line, startPoint, start_crossing.in_or_mid, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{ // Couldn't comb between start point and computed crossing from the start part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon
return false;
@@ -144,7 +156,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
// throught air from boundary to boundary
if (avoid_other_parts_now)
if (avoid_other_parts && !skip_avoid_other_parts_path)
{
combPaths.emplace_back();
combPaths.throughAir = true;
@@ -155,7 +167,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
else
{
bool combing_succeeded = LinePolygonsCrossings::comb(getBoundaryOutside(), start_crossing.out, end_crossing.out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(*boundary_outside, *outside_loc_to_line, start_crossing.out, end_crossing.out, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{
return false;
@@ -166,10 +178,28 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
{ // directly through air (not avoiding other parts)
combPaths.emplace_back();
combPaths.throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().cross_boundary = true; // note: we don't actually know whether this is cross boundary, but it might very well be
combPaths.back().push_back(start_crossing.in_or_mid);
combPaths.back().push_back(end_crossing.in_or_mid);
}
if (skip_avoid_other_parts_path)
{
if (startInside == endInside && start_part_idx == end_part_idx)
{
if (startInside)
{ // both start and end are inside
combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *inside_loc_to_line);
}
else
{ // both start and end are outside
combPaths.back().cross_boundary = PolygonUtils::polygonCollidesWithLineSegment(startPoint, endPoint, *outside_loc_to_line);
}
}
else
{
combPaths.back().cross_boundary = true;
}
}
if (endInside)
{
@@ -177,7 +207,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
assert(end_crossing.dest_part.size() > 0 && "The part we end up inside when combing should have been computed already!");
combPaths.emplace_back();
bool combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, end_crossing.in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
bool combing_succeeded = LinePolygonsCrossings::comb(end_crossing.dest_part, *inside_loc_to_line, end_crossing.in_or_mid, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
if (!combing_succeeded)
{ // Couldn't comb between end point and computed crossing to the end part! Happens for very thin parts when the offset_to_get_off_boundary moves points to outside the polygon
return false;
@@ -188,28 +218,47 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool _st
}
}
Comb::Crossing::Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside)
Comb::Crossing::Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside, const LocToLineGrid* inside_loc_to_line)
: dest_is_inside(dest_is_inside)
, dest_crossing_poly(boundary_inside[dest_part_boundary_crossing_poly_idx]) // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
, boundary_inside(boundary_inside)
, inside_loc_to_line(inside_loc_to_line)
, dest_point(dest_point)
, dest_part_idx(dest_part_idx)
{
if (dest_is_inside)
{
dest_crossing_poly = boundary_inside[dest_part_boundary_crossing_poly_idx]; // initialize with most obvious poly, cause mostly a combing move will move outside the part, rather than inside a hole in the part
}
}
bool Comb::moveInside(bool is_inside, Point& dest_point, unsigned int& inside_poly)
{
if (is_inside)
{
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_moveInside_distance2);
if (cpp.point_idx == NO_INDEX)
coord_t max_move_inside_distance2_here = std::numeric_limits<coord_t>::max(); // the distance which would make the moveInside fail
if (storage.getSettingAsCombingMode("retraction_combing") == cura::CombingMode::NO_SKIN)
{ // if we perform no_skin combing, then a far move inside is likely a consequence of there meing skin in between the destination point and the inside comb boundary
// if we perform normal combing, then a far move inside is likely to be a consequence of sharp pointy segments in the layer part
max_move_inside_distance2_here = max_move_inside_distance2;
}
Point original_dest_point = dest_point;
ClosestPolygonPoint cpp = PolygonUtils::ensureInsideOrOutside(boundary_inside, dest_point, offset_extra_start_end, max_move_inside_distance2_here, &boundary_inside, inside_loc_to_line);
if (!cpp.isValid())
{
return false;
}
else
{
inside_poly = cpp.poly_idx;
return true;
if (vSize2(dest_point - original_dest_point) > max_move_inside_distance2 // only check for collision with outlines for long moves
&& PolygonUtils::polygonCollidesWithLineSegment(outlines, dest_point, original_dest_point))
{
return false;
}
else
{
inside_poly = cpp.poly_idx;
return true;
}
}
}
return false;
@@ -223,10 +272,43 @@ void Comb::Crossing::findCrossingInOrMid(const PartsView& partsView_inside, cons
Point _dest_point(dest_point); // copy to local variable for lambda capture
std::function<int(Point)> close_towards_start_penalty_function([_dest_point](Point candidate){ return vSize2((candidate - _dest_point) / 10); });
dest_part = partsView_inside.assemblePart(dest_part_idx);
Point result(close_to);
ClosestPolygonPoint boundary_crossing_point;
{ // set [result] to a point on the destination part closest to close_to (but also a bit close to fest_point)
std::unordered_set<unsigned int> dest_part_poly_indices;
for (unsigned int poly_idx : partsView_inside[dest_part_idx])
{
dest_part_poly_indices.emplace(poly_idx);
}
coord_t dist2_score = std::numeric_limits<coord_t>::max();
std::function<bool (const PolygonsPointIndex&)> line_processor
= [close_to, _dest_point, &boundary_crossing_point, &dist2_score, &dest_part_poly_indices](const PolygonsPointIndex& boundary_segment)
{
if (dest_part_poly_indices.find(boundary_segment.poly_idx) == dest_part_poly_indices.end())
{ // we're not looking at a polygon from the dest_part
return true; // a.k.a. continue;
}
Point closest_here = LinearAlg2D::getClosestOnLineSegment(close_to, boundary_segment.p(), boundary_segment.next().p());
coord_t dist2_score_here = vSize2(close_to - closest_here) + vSize2(_dest_point - closest_here) / 10;
if (dist2_score_here < dist2_score)
{
dist2_score = dist2_score_here;
boundary_crossing_point = ClosestPolygonPoint(closest_here, boundary_segment.point_idx, boundary_segment.getPolygon(), boundary_segment.poly_idx);
}
return true;
};
inside_loc_to_line->processLine(std::make_pair(dest_point, close_to), line_processor);
}
Point result(boundary_crossing_point.p()); // the inside point of the crossing
if (!boundary_crossing_point.isValid())
{ // no point has been found in the sparse grid
result = dest_point;
}
int64_t max_dist2 = std::numeric_limits<int64_t>::max();
ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, close_towards_start_penalty_function);
if (crossing_1_in_cp.point_idx != NO_INDEX)
ClosestPolygonPoint crossing_1_in_cp = PolygonUtils::ensureInsideOrOutside(dest_part, result, boundary_crossing_point, offset_dist_to_get_from_on_the_polygon_to_outside, max_dist2, &boundary_inside, inside_loc_to_line, close_towards_start_penalty_function);
if (crossing_1_in_cp.isValid())
{
dest_crossing_poly = crossing_1_in_cp.poly;
in_or_mid = result;
@@ -263,7 +345,8 @@ bool Comb::Crossing::findOutside(const Polygons& outside, const Point close_to,
if (dest_is_inside && in_out_dist2_1 > comber.max_crossing_dist2) // moveInside moved too far
{ // if move is too far over in_between
// find crossing closer by
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(outside, dest_crossing_poly, dest_point, close_to, comber);
assert(dest_crossing_poly && "destination crossing poly should have been instantiated!");
std::shared_ptr<std::pair<ClosestPolygonPoint, ClosestPolygonPoint>> best = findBestCrossing(outside, *dest_crossing_poly, dest_point, close_to, comber);
if (best)
{
in_or_mid = PolygonUtils::moveInside(best->first, comber.offset_dist_to_get_from_on_the_polygon_to_outside);
+31 -15
Ver Arquivo
@@ -4,9 +4,11 @@
#include <memory> // shared_ptr
#include "../utils/optional.h"
#include "../utils/polygon.h"
#include "../utils/BucketGrid2D.h"
#include "../utils/SparsePointGridInclusive.h"
#include "../utils/polygonUtils.h"
#include "../utils/LazyInitialization.h"
#include "LinePolygonsCrossings.h"
#include "CombPath.h"
@@ -30,7 +32,7 @@ class SliceDataStorage;
* As an optimization, the combing paths inside are calculated on specifically those PolygonsParts within which to comb, while the coundary_outside isn't split into outside parts,
* because generally there is only one outside part; encapsulated holes occur less often.
*/
class Comb
class Comb
{
friend class LinePolygonsCrossings;
private:
@@ -46,7 +48,9 @@ private:
Point in_or_mid; //!< The point on the inside boundary, or in between the inside and outside boundary if the start/end point isn't inside the inside boudary
Point out; //!< The point on the outside boundary
PolygonsPart dest_part; //!< The assembled inside-boundary PolygonsPart in which the dest_point lies. (will only be initialized when Crossing::dest_is_inside holds)
PolygonRef dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
std::optional<PolygonRef> dest_crossing_poly; //!< The polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon)
const Polygons& boundary_inside; //!< The inside boundary as in \ref Comb::boundary_inside
const LocToLineGrid* inside_loc_to_line; //!< The loc to line grid \ref Comb::inside_loc_to_line
/*!
* Simple constructor
@@ -57,7 +61,7 @@ private:
* \param dest_part_boundary_crossing_poly_idx The index in \p boundary_inside of the polygon of the part in which dest_point lies, which will be crossed (often will be the outside polygon).
* \param boundary_inside The boundary within which to comb.
*/
Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside);
Crossing(const Point& dest_point, const bool dest_is_inside, const unsigned int dest_part_idx, const unsigned int dest_part_boundary_crossing_poly_idx, const Polygons& boundary_inside, const LocToLineGrid* inside_loc_to_line);
/*!
* Find the not-outside location (Combing::in_or_mid) of the crossing between to the outside boundary
@@ -101,7 +105,7 @@ private:
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 max_move_inside_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
@@ -110,21 +114,28 @@ private:
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)
BucketGrid2D<PolygonsPointIndex>* outside_loc_to_line; //!< The BucketGrid mapping locations to line segments of the outside boundary.
PartsView partsView_inside; //!< Structured indices onto boundary_inside which shows which polygons belong to which part.
Polygons outlines; //!< The actual boundary between the model and air
LocToLineGrid* inside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the inner boundary.
LazyInitialization<Polygons> boundary_outside; //!< The boundary outside of which to stay to avoid collision with other layer parts. This is a pointer cause we only compute it when we move outside the boundary (so not when there is only a single part in the layer)
LazyInitialization<LocToLineGrid, Comb*, const int64_t> outside_loc_to_line; //!< The SparsePointGridInclusive mapping locations to line segments of the outside boundary.
/*!
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
* Get the outlines of the meshes or raft for this layer
*/
Polygons& getBoundaryOutside();
Polygons getCombOutlines();
/*!
* Get the BucketGrid mapping locations to line segments of the outside boundary. Calculate it when it hasn't been calculated yet.
* Get the SparsePointGridInclusive mapping locations to line segments of the outside boundary. Calculate it when it hasn't been calculated yet.
*/
BucketGrid2D<PolygonsPointIndex>& getOutsideLocToLine();
LocToLineGrid& getOutsideLocToLine();
/*!
* Get the boundary_outside, which is an offset from the outlines of all meshes in the layer. Calculate it when it hasn't been calculated yet.
*/
Polygons& getBoundaryOutside();
/*!
* Move the startPoint or endPoint inside when it should be inside
@@ -138,6 +149,9 @@ private:
public:
/*!
* Initializes the combing areas for every mesh in the layer (not support)
*
* \warning \ref Comb::calc changes the order of polygons in \p Comb::comb_boundary_inside
*
* \param storage Where the layer polygon data is stored
* \param layer_nr The number of the layer for which to generate the combing areas.
* \param comb_boundary_inside The comb boundary within which to comb within layer parts.
@@ -146,12 +160,14 @@ public:
* \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
*
* \warning Changes the order of polygons in \ref Comb::comb_boundary_inside
*
* \param startPoint Where to start moving from
* \param endPoint Where to move to
* \param combPoints Output parameter: The points along the combing path, excluding the \p startPoint (?) and \p endPoint
@@ -160,7 +176,7 @@ public:
* \param via_outside_makes_combing_fail When going through air is inavoidable, stop calculation early and return false.
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \return Whether combing has succeeded; otherwise a retraction is needed.
*/
*/
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored, bool via_outside_makes_combing_fail, bool fail_on_unavoidable_obstacles);
};
+14 -7
Ver Arquivo
@@ -85,12 +85,19 @@ bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
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))
// 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;
}
@@ -138,7 +145,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y)));
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
< poly.size() / 2 )
{ // follow the path in the same direction as the winding order of the boundary polygon
@@ -159,7 +166,7 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x + std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y)));
}
@@ -187,9 +194,9 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx], loc_to_line_grid))
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx - 1]))
if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx - 1], loc_to_line_grid))
{
comb_path.cross_boundary = true;
}
@@ -202,7 +209,7 @@ bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimize
// TODO: add the below extra optimization? (+/- 7% extra computation time, +/- 2% faster print for Dual_extrusion_support_generation.stl)
while (optimized_comb_path.size() > 1)
{
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx]))
if (PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx], loc_to_line_grid))
{
break;
}
+12 -4
Ver Arquivo
@@ -3,6 +3,8 @@
#define PATH_PLANNING_LINE_POLYGONS_CROSSINGS_H
#include "../utils/polygon.h"
#include "../utils/polygonUtils.h"
#include "../utils/SparseLineGrid.h"
#include "CombPath.h"
@@ -80,6 +82,7 @@ private:
unsigned int max_crossing_idx; //!< The index into LinePolygonsCrossings::crossings to the crossing with the maximal PolyCrossings::max crossing of all PolyCrossings's.
Polygons& boundary; //!< The boundary not to cross during combing.
LocToLineGrid& loc_to_line_grid; //!< Mapping from locations to line segments of \ref LinePolygonsCrossings::boundary
Point startPoint; //!< The start point of the scanline.
Point endPoint; //!< The end point of the scanline.
@@ -163,8 +166,12 @@ private:
* \param end the end point
* \param dist_to_move_boundary_point_outside Distance used to move a point from a boundary so that it doesn't intersect with it anymore. (Precision issue)
*/
LinePolygonsCrossings(Polygons& boundary, Point& start, Point& end, int64_t dist_to_move_boundary_point_outside)
: boundary(boundary), startPoint(start), endPoint(end), dist_to_move_boundary_point_outside(dist_to_move_boundary_point_outside)
LinePolygonsCrossings(Polygons& boundary, LocToLineGrid& loc_to_line_grid, Point& start, Point& end, int64_t dist_to_move_boundary_point_outside)
: boundary(boundary)
, loc_to_line_grid(loc_to_line_grid)
, startPoint(start)
, endPoint(end)
, dist_to_move_boundary_point_outside(dist_to_move_boundary_point_outside)
{
}
@@ -173,15 +180,16 @@ public:
/*!
* The main function of this class: calculate one combing path within the boundary.
* \param boundary The polygons to follow when calculating the basic combing path
* \param loc_to_line_grid A sparse grid mapping cells to all line segments of (at least) \p boundary in those cells
* \param startPoint From where to start the combing move.
* \param endPoint Where to end the combing move.
* \param combPath Output parameter: the combing path generated.
* \param fail_on_unavoidable_obstacles When moving over other parts is inavoidable, stop calculation early and return false.
* \return Whether combing succeeded, i.e. we didn't cross any gaps/other parts
*/
static bool comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
static bool comb(Polygons& boundary, LocToLineGrid& loc_to_line_grid, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
LinePolygonsCrossings linePolygonsCrossings(boundary, loc_to_line_grid, startPoint, endPoint, dist_to_move_boundary_point_outside);
return linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored, fail_on_unavoidable_obstacles);
};
};
+69 -8
Ver Arquivo
@@ -1,25 +1,86 @@
/** 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, ClipperLib::jtRound));
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, ClipperLib::jtRound));
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
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"));
if (train.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
{
return 0;
}
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
+7 -22
Ver Arquivo
@@ -127,13 +127,14 @@ int SettingRegistry::loadExtruderJSONsettings(unsigned int extruder_nr, Settings
{
if (extruder_nr >= extruder_train_ids.size())
{
return -1;
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;
@@ -217,22 +218,6 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
return 3;
}
{ // handle machine name
std::string machine_name = "Unknown";
if (json_document.HasMember("name"))
{
const rapidjson::Value& machine_name_field = json_document["name"];
if (machine_name_field.IsString())
{
machine_name = machine_name_field.GetString();
}
}
SettingConfig& machine_name_setting = addSetting("machine_name", "Machine Name");
machine_name_setting.setDefault(machine_name);
machine_name_setting.setType("string");
settings_base->_setSetting(machine_name_setting.getKey(), machine_name_setting.getDefaultValue());
}
if (json_document.HasMember("settings"))
{
std::list<std::string> path;
@@ -262,7 +247,7 @@ void SettingRegistry::handleChildren(const rapidjson::Value& settings_list, std:
{
if (!settings_list.IsObject())
{
logError("ERROR: json settings list is not an object!\n");
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)
@@ -295,7 +280,7 @@ void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator&
const rapidjson::Value& json_setting = json_setting_it->value;
if (!json_setting.IsObject())
{
logError("ERROR: json setting is not an object!\n");
logError("json setting is not an object!\n");
return;
}
std::string name = json_setting_it->name.GetString();
@@ -308,7 +293,7 @@ void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator&
{
if (!json_setting.HasMember("label") || !json_setting["label"].IsString())
{
logError("ERROR: json setting \"%s\" has no label!\n", name.c_str());
logError("json setting \"%s\" has no label!\n", name.c_str());
return;
}
std::string label = json_setting["label"].GetString();
@@ -316,7 +301,7 @@ void SettingRegistry::handleSetting(const rapidjson::Value::ConstMemberIterator&
SettingConfig* setting = getSettingConfig(name);
if (warn_duplicates && setting)
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", name.c_str(), label.c_str(), getSettingConfig(name)->getLabel().c_str());
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)
{
+64 -11
Ver Arquivo
@@ -13,6 +13,7 @@
#include <cassert>
#include <fstream>
#include <set>
#include <unordered_map>
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
@@ -36,13 +37,16 @@ class SettingsToGv
FILE* out;
std::set<std::string> engine_settings;
bool parent_child_viz, inherit_viz, error_viz, warning_viz;
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)
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");
@@ -60,14 +64,31 @@ public:
private:
void generateEdge(const std::string& parent, const std::string& child, RelationType relation_type)
{
if (engine_settings.find(parent) != engine_settings.end())
if (global_only_viz)
{
fprintf(out, "%s [color=green];\n", parent.c_str());
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());
}
}
if (engine_settings.find(child) != engine_settings.end())
else
{
fprintf(out, "%s [color=green];\n", child.c_str());
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)
{
@@ -126,7 +147,10 @@ private:
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)
@@ -157,12 +181,41 @@ private:
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, "inherit_function", parent, name, RelationType::INHERIT_FUNCTION);
bool generated_edge_max = createFunctionEdges(data, "max_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_min = createFunctionEdges(data, "min_value", parent, name, RelationType::ERROR_FUNCTION);
bool generated_edge_max_warn = createFunctionEdges(data, "max_value_warning", parent, name, RelationType::WARNING_FUNCTION);
bool generated_edge_min_warn = createFunctionEdges(data, "min_value_warning", parent, name, RelationType::WARNING_FUNCTION);
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;
+115 -5
Ver Arquivo
@@ -33,6 +33,8 @@ std::string toString(EGCodeFlavor flavor)
return "RepRap(Volumetric)";
case EGCodeFlavor::GRIFFIN:
return "Griffin";
case EGCodeFlavor::REPETIER:
return "Repetier";
case EGCodeFlavor::REPRAP:
default:
return "RepRap";
@@ -40,7 +42,7 @@ std::string toString(EGCodeFlavor flavor)
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
: parent(nullptr)
{
}
@@ -50,7 +52,7 @@ SettingsBaseVirtual::SettingsBaseVirtual(SettingsBaseVirtual* parent)
}
SettingsBase::SettingsBase()
: SettingsBaseVirtual(NULL)
: SettingsBaseVirtual(nullptr)
{
}
@@ -78,24 +80,34 @@ void SettingsBase::setSetting(std::string key, std::string value)
}
else
{
cura::logError("Warning: setting an unregistered setting %s to %s\n", key.c_str(), value.c_str());
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::logError("Unregistered setting %s\n", key.c_str());
cura::logWarning("Unregistered setting %s\n", key.c_str());
return "";
}
@@ -104,6 +116,12 @@ 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);
@@ -121,6 +139,17 @@ int SettingsBaseVirtual::getSettingAsCount(std::string key) const
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);
@@ -132,6 +161,12 @@ 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);
@@ -160,7 +195,7 @@ double SettingsBaseVirtual::getSettingInDegreeCelsius(std::string key) const
double SettingsBaseVirtual::getSettingInMillimetersPerSecond(std::string key) const
{
std::string value = getSettingString(key);
return std::max(1.0, atof(value.c_str()));
return std::max(0.0, atof(value.c_str()));
}
double SettingsBaseVirtual::getSettingInCubicMillimeters(std::string key) const
@@ -181,6 +216,20 @@ double SettingsBaseVirtual::getSettingInSeconds(std::string key) const
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;
@@ -225,6 +274,47 @@ FlowTempGraph SettingsBaseVirtual::getSettingAsFlowTempGraph(std::string key) co
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
{
@@ -241,6 +331,8 @@ EGCodeFlavor SettingsBaseVirtual::getSettingAsGCodeFlavor(std::string key) const
return EGCodeFlavor::MACH3;
else if (value == "RepRap (Volumatric)")
return EGCodeFlavor::REPRAP_VOLUMATRIC;
else if (value == "Repetier")
return EGCodeFlavor::REPETIER;
return EGCodeFlavor::REPRAP;
}
@@ -259,6 +351,8 @@ EFillMethod SettingsBaseVirtual::getSettingAsFillMethod(std::string key) const
return EFillMethod::TRIANGLES;
if (value == "concentric")
return EFillMethod::CONCENTRIC;
if (value == "concentric_3d")
return EFillMethod::CONCENTRIC_3D;
if (value == "zigzag")
return EFillMethod::ZIG_ZAG;
return EFillMethod::NONE;
@@ -271,6 +365,8 @@ EPlatformAdhesion SettingsBaseVirtual::getSettingAsPlatformAdhesion(std::string
return EPlatformAdhesion::BRIM;
if (value == "raft")
return EPlatformAdhesion::RAFT;
if (value == "none")
return EPlatformAdhesion::NONE;
return EPlatformAdhesion::SKIRT;
}
@@ -308,6 +404,20 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key) const
return ESurfaceMode::NORMAL;
}
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key) const
{
std::string value = getSettingString(key);
if (value == "nowhere")
{
return FillPerimeterGapMode::NOWHERE;
}
if (value == "everywhere")
{
return FillPerimeterGapMode::EVERYWHERE;
}
return FillPerimeterGapMode::NOWHERE;
}
CombingMode SettingsBaseVirtual::getSettingAsCombingMode(std::string key)
{
std::string value = getSettingString(key);
+55 -6
Ver Arquivo
@@ -86,6 +86,8 @@ enum class EGCodeFlavor
* M227 is used to initialize a single extrusion train.
**/
GRIFFIN = 6,
REPETIER = 7,
};
/*!
@@ -106,18 +108,20 @@ enum class EFillMethod
TETRAHEDRAL,
TRIANGLES,
CONCENTRIC,
CONCENTRIC_3D,
ZIG_ZAG,
NONE
};
/*!
* Type of platform adheasion
* Type of platform adhesion.
*/
enum class EPlatformAdhesion
{
SKIRT,
BRIM,
RAFT
RAFT,
NONE
};
/*!
@@ -144,6 +148,12 @@ enum class ESurfaceMode
BOTH
};
enum class FillPerimeterGapMode
{
NOWHERE,
EVERYWHERE
};
enum class CombingMode
{
OFF,
@@ -151,6 +161,15 @@ enum class CombingMode
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,
@@ -177,7 +196,16 @@ public:
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
@@ -188,7 +216,18 @@ public:
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;
@@ -198,15 +237,18 @@ public:
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;
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key) const;
CombingMode getSettingAsCombingMode(std::string key);
SupportDistPriority getSettingAsSupportDistPriority(std::string key);
};
@@ -224,6 +266,11 @@ 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
@@ -234,6 +281,7 @@ public:
* \param value the value
*/
void setSetting(std::string key, std::string value);
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() const
@@ -274,6 +322,7 @@ 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
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)
};
+37 -16
Ver Arquivo
@@ -1,6 +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)
@@ -9,19 +11,19 @@ namespace cura
{
void generateSkins(int layerNr, SliceMeshStorage& mesh, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic)
void generateSkins(int layerNr, SliceMeshStorage& mesh, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_line_width, int insetCount, bool no_small_gaps_heuristic)
{
generateSkinAreas(layerNr, mesh, innermost_wall_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 = &mesh.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
SliceLayerPart* part = &layer->parts[partNr];
generateSkinInsets(part, extrusionWidth, insetCount);
generateSkinInsets(part, innermost_wall_line_width, insetCount);
}
}
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
SliceLayer& layer = mesh.layers[layer_nr];
@@ -39,8 +41,8 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_
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)
@@ -81,7 +83,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(mesh.layers.size()) - 1 && upSkinCount > 0)
if (layer_nr < static_cast<int>(mesh.layers.size()) - 1 - upSkinCount && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
@@ -105,7 +107,7 @@ void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, int innermost_wall_
}
void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount)
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount)
{
if (insetCount == 0)
{
@@ -119,10 +121,11 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
skin_part.insets.push_back(Polygons());
if (i == 0)
{
skin_part.insets[0] = skin_part.outline.offset(- extrusionWidth/2);
} else
skin_part.insets[0] = skin_part.outline.offset(-wall_line_width / 2);
}
else
{
skin_part.insets[i] = skin_part.insets[i - 1].offset(-extrusionWidth);
skin_part.insets[i] = skin_part.insets[i - 1].offset(-wall_line_width);
}
// optimize polygons: remove unnecessary verts
@@ -136,17 +139,26 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateInfill(int layerNr, SliceMeshStorage& mesh, 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 = mesh.layers[layerNr];
int extra_offset = 0;
EFillMethod fill_pattern = mesh.getSettingAsFillMethod("infill_pattern");
if ((fill_pattern == EFillMethod::CONCENTRIC || fill_pattern == EFillMethod::CONCENTRIC_3D)
&& layerNr % 2 == 0
&& mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") * 2)
{
extra_offset = -innermost_wall_line_width;
}
for(SliceLayerPart& part : layer.parts)
{
if (int(part.insets.size()) < wall_line_count)
{
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
Polygons infill = part.insets.back().offset(extra_offset - innermost_wall_line_width / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
@@ -159,8 +171,17 @@ void generateInfill(int layerNr, SliceMeshStorage& mesh, int innermost_wall_extr
}
}
infill.removeSmallAreas(MIN_AREA_SIZE);
part.infill_area = infill.offset(infill_skin_overlap);
Polygons final_infill = infill.offset(infill_skin_overlap);
if (mesh.getSettingBoolean("infill_hollow"))
{
part.print_outline = part.print_outline.difference(final_infill);
}
else
{
part.infill_area = final_infill;
}
}
}
@@ -172,7 +193,7 @@ void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh, un
{
layer_skip_count = 1;
}
unsigned int gradual_infill_step_layer_count = gradual_infill_step_height / mesh.getSettingInMicrons("layer_height"); // The difference in layer count between consecutive density infill areas
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
+21 -16
Ver Arquivo
@@ -11,38 +11,43 @@ namespace cura
*
* \param layerNr The index of the layer for which to generate the skins.
* \param mesh The storage where the layer outline information (input) is stored and where the skin insets and fill areas (output) are stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param innermost_wall_extrusion_width The line width of the inner most wall
* \param 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
*/
void generateSkins(int layerNr, SliceMeshStorage& mesh, int extrusionWidth, int downSkinCount, int upSkinCount, int wall_line_count, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic);
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 mesh The storage where the layer outline information (input) is stored and where the skin outline (output) is stored.
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
* \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& mesh, 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 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);
void generateSkinInsets(SliceLayerPart* part, const int wall_line_width, int insetCount);
/*!
* Generate Infill by offsetting from the last wall.
@@ -54,11 +59,11 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
* \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& mesh, 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.
-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 = storage.getSettingAsIndex("adhesion_extruder_nr");
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.approxConvexHull();
}
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
+172 -39
Ver Arquivo
@@ -67,13 +67,13 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
}
}
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;
@@ -83,33 +83,63 @@ std::vector<GCodePathConfig> SliceDataStorage::initializeTravelConfigs()
}
return ret;
}
std::vector<GCodePathConfig> SliceDataStorage::initializeSkirtConfigs()
std::vector<GCodePathConfig> SliceDataStorage::initializeSkirtBrimConfigs()
{
std::vector<GCodePathConfig> ret;
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
skirt_config.emplace_back(PrintFeatureType::Skirt);
skirt_brim_config.emplace_back(PrintFeatureType::SkirtBrim);
}
return ret;
}
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
print_layer_count(0),
retraction_config_per_extruder(initializeRetractionConfigs()),
extruder_switch_retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config_per_extruder(initializeTravelConfigs()),
skirt_config(initializeSkirtConfigs()),
raft_base_config(PrintFeatureType::Support),
skirt_brim_config(initializeSkirtBrimConfigs()),
raft_base_config(PrintFeatureType::SupportInterface),
raft_interface_config(PrintFeatureType::Support),
raft_surface_config(PrintFeatureType::Support),
raft_surface_config(PrintFeatureType::SupportInterface),
support_config(PrintFeatureType::Support),
support_roof_config(PrintFeatureType::Skin),
max_object_height_second_to_last_extruder(-1)
support_skin_config(PrintFeatureType::SupportInterface),
max_print_height_second_to_last_extruder(-1)
{
}
SliceLayerPart* SliceDataStorage::getPartInside(int layer_nr, Point location)
{
if (layer_nr >= 0)
{
for (SliceMeshStorage& mesh : meshes)
{
if ((unsigned int)layer_nr >= mesh.layers.size())
{
continue;
}
SliceLayer& layer = mesh.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{
if (part.outline.inside(location))
{
return &part;
}
}
}
return nullptr;
}
else
{
return nullptr;
}
}
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)
{
@@ -136,25 +166,28 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
else
{
Polygons total;
for (const SliceMeshStorage& mesh : meshes)
if (layer_nr >= 0)
{
if (mesh.getSettingBoolean("infill_mesh"))
for (const SliceMeshStorage& mesh : meshes)
{
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 (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
{
continue;
}
const SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (const_cast<SliceMeshStorage&>(mesh).getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) // TODO: make all getSetting functions const??
{
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);
}
@@ -164,7 +197,7 @@ Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_pa
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)
{
@@ -178,21 +211,24 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
else
{
Polygons total;
for (const SliceMeshStorage& mesh : meshes)
if (layer_nr >= 0)
{
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??
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);
}
@@ -201,20 +237,23 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
std::vector<bool> SliceDataStorage::getExtrudersUsed() const
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
if (getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
ret[extr_nr] = true;
continue;
if (skirt_brim[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
}
@@ -225,12 +264,106 @@ std::vector< bool > SliceDataStorage::getExtrudersUsed()
// support is presupposed to be present...
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
ret[getSettingAsIndex("support_interface_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
for (const SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
}
return ret;
}
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr) const
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
bool include_adhesion = true;
bool include_helper_parts = true;
bool include_models = true;
if (layer_nr < 0)
{
include_models = false;
if (layer_nr < -Raft::getFillerLayerCount(*this))
{
include_helper_parts = false;
}
else
{
layer_nr = 0; // because the helper parts are copied from the initial layer in the filler layer
include_adhesion = false;
}
}
else if (layer_nr > 0 || getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{ // only include adhesion only for layers where platform adhesion actually occurs
// i.e. layers < 0 are for raft, layer 0 is for brim/skirt
include_adhesion = false;
}
if (include_adhesion)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt_brim[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
}
// TODO: ooze shield, draft shield ..?
if (include_helper_parts)
{
// support
if (layer_nr < int(support.supportLayers.size()))
{
const SupportLayer& support_layer = support.supportLayers[layer_nr];
if (layer_nr == 0)
{
if (support_layer.supportAreas.size() > 0)
{
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
}
}
else
{
if (support_layer.supportAreas.size() > 0)
{
ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
}
}
if (support_layer.skin.size() > 0)
{
ret[getSettingAsIndex("support_interface_extruder_nr")] = true;
}
}
}
if (include_models)
{
for (const SliceMeshStorage& mesh : meshes)
{
if (layer_nr >= int(mesh.layers.size()))
{
continue;
}
const SliceLayer& layer = mesh.layers[layer_nr];
if (layer.parts.size() > 0)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
}
}
return ret;
}
+35 -10
Ver Arquivo
@@ -124,7 +124,9 @@ 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.
Polygons support_mesh; //!< Areas from support meshes
Polygons anti_overhang; //!< Areas where no overhang should be detected.
};
class SupportStorage
@@ -160,7 +162,7 @@ public:
, insetX_config(PrintFeatureType::InnerWall)
, skin_config(PrintFeatureType::Skin)
{
layers.reserve(slice_layer_count);
layers.resize(slice_layer_count);
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(PrintFeatureType::Infill);
@@ -172,6 +174,8 @@ class SliceDataStorage : public SettingsMessenger, NoCopy
public:
MeshGroup* meshgroup; // needed to pass on the per extruder settings.. (TODO: put this somewhere else? Put the per object settings here directly, or a pointer only to the per object settings.)
unsigned int print_layer_count; //!< The total number of layers (except the raft and filler layers)
Point3 model_size, model_min, model_max;
std::vector<SliceMeshStorage> meshes;
@@ -180,7 +184,7 @@ public:
std::vector<GCodePathConfig> travel_config_per_extruder; //!< The config used for travel moves (only speed is set!)
std::vector<GCodePathConfig> skirt_config; //!< config for skirt per extruder
std::vector<GCodePathConfig> skirt_brim_config; //!< Configuration for skirt and brim per extruder.
std::vector<CoastingConfig> coasting_config; //!< coasting config per extruder
GCodePathConfig raft_base_config;
@@ -188,32 +192,36 @@ public:
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
int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder
std::vector<int> max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used.
std::vector<size_t> max_print_height_order; //!< Ordered indices into max_print_height_per_extruder: back() will return the extruder number with the highest print height.
PrimeTower primeTower;
std::vector<Polygons> oozeShield; //oozeShield per layer
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
Point wipePoint;
/*!
* Construct the initial retraction_config_per_extruder
*/
std::vector<RetractionConfig> initializeRetractionConfigs();
/*!
* Construct the initial travel_config_per_extruder
*/
std::vector<GCodePathConfig> initializeTravelConfigs();
/*!
* Construct the initial skirt_config s for each extruder
* Construct the initial skirt & brim configurations for each extruder.
*/
std::vector<GCodePathConfig> initializeSkirtConfigs();
std::vector<GCodePathConfig> initializeSkirtBrimConfigs();
/*!
* \brief Creates a new slice data storage that stores the slice data of the
@@ -231,6 +239,15 @@ public:
{
}
/*!
* Check in which part \p location lies, if in any.
*
* \param layer_nr The layer for which to check
* \param location The location to check
* \return The part in which \p location lie, or nullptr, if it's outside all parts.
*/
SliceLayerPart* getPartInside(int layer_nr, Point location);
/*!
* Get all outlines within a given layer.
*
@@ -255,7 +272,15 @@ public:
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
std::vector<bool> getExtrudersUsed() const;
/*!
* Get the extruders used on a particular layer.
*
* \param layer_nr the layer for which to check
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed(int layer_nr) const;
};
}//namespace cura
+519 -140
Ver Arquivo
@@ -5,13 +5,13 @@
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/SparsePointGridInclusive.h"
#include "slicer.h"
#include "debug.h" // TODO remove
namespace cura {
int largest_neglected_gap_first_phase = MM2INT(0.01); //!< distance between two line segments regarded as connected
int largest_neglected_gap_second_phase = MM2INT(0.02); //!< distance between two line segments regarded as connected
int max_stitch1 = MM2INT(10.0); //!< maximal distance stitched between open polylines to form polygons
@@ -31,17 +31,17 @@ void SlicerLayer::makeBasicPolygonLoops(const Mesh* mesh, Polygons& open_polylin
void SlicerLayer::makeBasicPolygonLoop(const Mesh* mesh, Polygons& open_polylines, unsigned int start_segment_idx)
{
Polygon poly;
poly.add(segments[start_segment_idx].start);
for (int segment_idx = start_segment_idx; segment_idx != -1; )
{
SlicerSegment& segment = segments[segment_idx];
poly.add(segment.end);
segment.addedToPolygon = true;
segment_idx = getNextSegmentIdx(mesh, segment, start_segment_idx);
if (segment_idx == static_cast<int>(start_segment_idx))
if (segment_idx == static_cast<int>(start_segment_idx))
{ // polyon is closed
polygons.add(poly);
return;
@@ -51,163 +51,498 @@ void SlicerLayer::makeBasicPolygonLoop(const Mesh* mesh, Polygons& open_polyline
open_polylines.add(poly);
}
int SlicerLayer::tryFaceNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment, int face_idx, unsigned int start_segment_idx) const
{
decltype(face_idx_to_segment_idx.begin()) it;
auto it_end = face_idx_to_segment_idx.end();
it = face_idx_to_segment_idx.find(face_idx);
if (it != it_end)
{
int segment_idx = (*it).second;
Point p1 = segments[segment_idx].start;
Point diff = segment.end - p1;
if (shorterThen(diff, largest_neglected_gap_first_phase))
{
if (segment_idx == static_cast<int>(start_segment_idx))
{
return start_segment_idx;
}
if (segments[segment_idx].addedToPolygon)
{
return -1;
}
return segment_idx;
}
}
return -1;
}
int SlicerLayer::getNextSegmentIdx(const Mesh* mesh, const SlicerSegment& segment, unsigned int start_segment_idx)
{
int next_segment_idx = -1;
const MeshFace& face = mesh->faces[segment.faceIndex];
for (unsigned int face_edge_idx = 0; face_edge_idx < 3; face_edge_idx++)
{ // check segments in connected faces
decltype(face_idx_to_segment_idx.begin()) it;
if (face.connected_face_index[face_edge_idx] > -1 && (it = face_idx_to_segment_idx.find(face.connected_face_index[face_edge_idx])) != face_idx_to_segment_idx.end())
bool segment_ended_at_edge = segment.endVertex == nullptr;
if (segment_ended_at_edge)
{
int face_to_try = segment.endOtherFaceIdx;
if (face_to_try == -1)
{
int segment_idx = (*it).second;
Point p1 = segments[segment_idx].start;
Point diff = segment.end - p1;
if (shorterThen(diff, largest_neglected_gap_first_phase))
return -1;
}
return tryFaceNextSegmentIdx(mesh,segment,face_to_try,start_segment_idx);
}
else
{
// segment ended at vertex
const std::vector<uint32_t> &faces_to_try = segment.endVertex->connected_faces;
for (int face_to_try : faces_to_try)
{
int result_segment_idx =
tryFaceNextSegmentIdx(mesh,segment,face_to_try,start_segment_idx);
if (result_segment_idx == static_cast<int>(start_segment_idx))
{
if (segment_idx == static_cast<int>(start_segment_idx))
{
return start_segment_idx;
}
if (segments[segment_idx].addedToPolygon)
{
continue;
}
next_segment_idx = segment_idx; // not immediately returned since we might still encounter the start_segment_idx
return start_segment_idx;
}
else if (result_segment_idx != -1)
{
// not immediately returned since we might still encounter the start_segment_idx
next_segment_idx = result_segment_idx;
}
}
}
return next_segment_idx;
}
void SlicerLayer::connectOpenPolylines(Polygons& open_polylines)
{
// TODO use some space partitioning data structure to make this run faster than O(n^2)
for(unsigned int open_polyline_idx = 0; open_polyline_idx < open_polylines.size(); open_polyline_idx++)
{
PolygonRef open_polyline = open_polylines[open_polyline_idx];
if (open_polyline.size() < 1) continue;
for(unsigned int open_polyline_other_idx = 0; open_polyline_other_idx < open_polylines.size(); open_polyline_other_idx++)
{
PolygonRef open_polyline_other = open_polylines[open_polyline_other_idx];
if (open_polyline_other.size() < 1) continue;
Point diff = open_polyline.back() - open_polyline_other[0];
bool allow_reverse = false;
// Search a bit fewer cells but at cost of covering more area.
// Since acceptance area is small to start with, the extra is unlikely to hurt much.
coord_t cell_size = largest_neglected_gap_first_phase * 2;
connectOpenPolylinesImpl(open_polylines, largest_neglected_gap_second_phase, cell_size, allow_reverse);
}
if (shorterThen(diff, largest_neglected_gap_second_phase))
void SlicerLayer::stitch(Polygons& open_polylines)
{
bool allow_reverse = true;
connectOpenPolylinesImpl(open_polylines, max_stitch1, max_stitch1, allow_reverse);
}
const SlicerLayer::Terminus SlicerLayer::Terminus::INVALID_TERMINUS{~static_cast<Index>(0U)};
bool SlicerLayer::PossibleStitch::operator<(const PossibleStitch& other) const
{
// better if lower distance
if (dist2 > other.dist2)
{
return true;
}
else if (dist2 < other.dist2)
{
return false;
}
// better if in order instead of reversed
if (!in_order() && other.in_order())
{
return true;
}
// better if lower Terminus::Index for terminus_0
// This just defines a more total order and isn't strictly necessary.
if (terminus_0.asIndex() > other.terminus_0.asIndex())
{
return true;
}
else if (terminus_0.asIndex() < other.terminus_0.asIndex())
{
return false;
}
// better if lower Terminus::Index for terminus_1
// This just defines a more total order and isn't strictly necessary.
if (terminus_1.asIndex() > other.terminus_1.asIndex())
{
return true;
}
else if (terminus_1.asIndex() < other.terminus_1.asIndex())
{
return false;
}
// The stitches have equal goodness
return false;
}
std::priority_queue<SlicerLayer::PossibleStitch>
SlicerLayer::findPossibleStitches(
const Polygons& open_polylines,
coord_t max_dist, coord_t cell_size,
bool allow_reverse) const
{
std::priority_queue<PossibleStitch> stitch_queue;
// maximum distance squared
int64_t max_dist2 = max_dist * max_dist;
// Represents a terminal point of a polyline in open_polylines.
struct StitchGridVal
{
unsigned int polyline_idx;
// Depending on the SparsePointGridInclusive, either the start point or the
// end point of the polyline
Point polyline_term_pt;
};
struct StitchGridValLocator
{
Point operator()(const StitchGridVal& val) const
{
return val.polyline_term_pt;
}
};
// Used to find nearby end points within a fixed maximum radius
SparsePointGrid<StitchGridVal,StitchGridValLocator> grid_ends(cell_size);
// Used to find nearby start points within a fixed maximum radius
SparsePointGrid<StitchGridVal,StitchGridValLocator> grid_starts(cell_size);
// populate grids
// Inserts the ends of all polylines into the grid (does not
// insert the starts of the polylines).
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
{
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
if (polyline_0.size() < 1) continue;
StitchGridVal grid_val;
grid_val.polyline_idx = polyline_0_idx;
grid_val.polyline_term_pt = polyline_0.back();
grid_ends.insert(grid_val);
}
// Inserts the start of all polylines into the grid.
if (allow_reverse)
{
for(unsigned int polyline_0_idx = 0; polyline_0_idx < open_polylines.size(); polyline_0_idx++)
{
const PolygonRef polyline_0 = open_polylines[polyline_0_idx];
if (polyline_0.size() < 1) continue;
StitchGridVal grid_val;
grid_val.polyline_idx = polyline_0_idx;
grid_val.polyline_term_pt = polyline_0[0];
grid_starts.insert(grid_val);
}
}
// search for nearby end points
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
{
const PolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
std::vector<StitchGridVal> nearby_ends;
// Check for stitches that append polyline_1 onto polyline_0
// in natural order. These are stitches that use the end of
// polyline_0 and the start of polyline_1.
nearby_ends = grid_ends.getNearby(polyline_1[0], max_dist);
for (const auto& nearby_end : nearby_ends)
{
Point diff = nearby_end.polyline_term_pt - polyline_1[0];
int64_t dist2 = vSize2(diff);
if (dist2 < max_dist2)
{
if (open_polyline_idx == open_polyline_other_idx)
PossibleStitch poss_stitch;
poss_stitch.dist2 = dist2;
poss_stitch.terminus_0 = Terminus{nearby_end.polyline_idx, true};
poss_stitch.terminus_1 = Terminus{polyline_1_idx, false};
stitch_queue.push(poss_stitch);
}
}
if (allow_reverse)
{
// Check for stitches that append polyline_1 onto polyline_0
// by reversing order of polyline_1. These are stitches that
// use the end of polyline_0 and the end of polyline_1.
nearby_ends = grid_ends.getNearby(polyline_1.back(), max_dist);
for (const auto& nearby_end : nearby_ends)
{
// Disallow stitching with self with same end point
if (nearby_end.polyline_idx == polyline_1_idx)
{
polygons.add(open_polyline);
open_polyline.clear();
break;
continue;
}
else
Point diff = nearby_end.polyline_term_pt - polyline_1.back();
int64_t dist2 = vSize2(diff);
if (dist2 < max_dist2)
{
for (unsigned int line_idx = 0; line_idx < open_polyline_other.size(); line_idx++)
{
open_polyline.add(open_polyline_other[line_idx]);
}
open_polyline_other.clear();
PossibleStitch poss_stitch;
poss_stitch.dist2 = dist2;
poss_stitch.terminus_0 = Terminus{nearby_end.polyline_idx, true};
poss_stitch.terminus_1 = Terminus{polyline_1_idx, true};
stitch_queue.push(poss_stitch);
}
}
// Check for stitches that append polyline_1 onto polyline_0
// by reversing order of polyline_0. These are stitches that
// use the start of polyline_0 and the start of polyline_1.
std::vector<StitchGridVal> nearby_starts =
grid_starts.getNearby(polyline_1[0], max_dist);
for (const auto& nearby_start : nearby_starts)
{
// Disallow stitching with self with same end point
if (nearby_start.polyline_idx == polyline_1_idx)
{
continue;
}
Point diff = nearby_start.polyline_term_pt - polyline_1[0];
int64_t dist2 = vSize2(diff);
if (dist2 < max_dist2)
{
PossibleStitch poss_stitch;
poss_stitch.dist2 = dist2;
poss_stitch.terminus_0 = Terminus{nearby_start.polyline_idx, false};
poss_stitch.terminus_1 = Terminus{polyline_1_idx, false};
stitch_queue.push(poss_stitch);
}
}
}
}
return stitch_queue;
}
void SlicerLayer::stitch(Polygons& open_polylines)
{ // TODO This is an inefficient implementation which can run in O(n^3) time.
// below code closes smallest gaps first
while(1)
void SlicerLayer::planPolylineStitch(
const Polygons& open_polylines,
Terminus& terminus_0, Terminus& terminus_1, bool reverse[2]) const
{
size_t polyline_0_idx = terminus_0.getPolylineIdx();
size_t polyline_1_idx = terminus_1.getPolylineIdx();
bool back_0 = terminus_0.isEnd();
bool back_1 = terminus_1.isEnd();
reverse[0] = false;
reverse[1] = false;
if (back_0)
{
int64_t best_dist2 = max_stitch1 * max_stitch1;
unsigned int best_polyline_1_idx = -1;
unsigned int best_polyline_2_idx = -1;
bool reversed = false;
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
if (back_1)
{
PolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
for(unsigned int polyline_2_idx = 0; polyline_2_idx < open_polylines.size(); polyline_2_idx++)
// back of both polylines
// we can reverse either one and then append onto the other
// reverse the smaller polyline
if (open_polylines[polyline_0_idx].size() <
open_polylines[polyline_1_idx].size())
{
PolygonRef polyline_2 = open_polylines[polyline_2_idx];
if (polyline_2.size() < 1) continue;
Point diff = polyline_1.back() - polyline_2[0];
int64_t dist2 = vSize2(diff);
if (dist2 < best_dist2)
{
best_dist2 = dist2;
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_2_idx;
reversed = false;
}
if (polyline_1_idx != polyline_2_idx)
{
Point diff = polyline_1.back() - polyline_2.back();
int64_t dist2 = vSize2(diff);
if (dist2 < best_dist2)
{
best_dist2 = dist2;
best_polyline_1_idx = polyline_1_idx;
best_polyline_2_idx = polyline_2_idx;
reversed = true;
}
}
std::swap(terminus_0,terminus_1);
}
reverse[1] = true;
} else {
// back of 0, front of 1
// already in order, nothing to do
}
if (best_dist2 >= max_stitch1 * max_stitch1)
break; // this code is reached if there was nothing to stitch within the distance limits
PolygonRef polyline_1 = open_polylines[best_polyline_1_idx];
PolygonRef polyline_2 = open_polylines[best_polyline_2_idx];
if (best_polyline_1_idx == best_polyline_2_idx)
{ // connect last piece of 'circle'
polygons.add(polyline_1);
polyline_1.clear();
}
else
{
if (back_1)
{
// front of 0, back of 1
// in order if we swap 0 and 1
std::swap(terminus_0,terminus_1);
}
else
{ // connect two polylines
if (reversed)
{
// front of both polylines
// we can reverse either one and then prepend to the other
// reverse the smaller polyline
if (open_polylines[polyline_0_idx].size() >
open_polylines[polyline_1_idx].size())
{
if (polyline_1.size() > polyline_2.size()) // decide which polygon to copy into the other
{
for(int poly_idx = polyline_2.size()-1; poly_idx >= 0; poly_idx--)
polyline_1.add(polyline_2[poly_idx]);
polyline_2.clear();
}
else
{
for(int poly_idx = polyline_1.size()-1; poly_idx >= 0; poly_idx--)
polyline_2.add(polyline_1[poly_idx]);
polyline_1.clear();
}
// note that either way we end up with the end of former polyline_1 next to the start of former polyline_2
}
else
{
for(Point& p : polyline_2)
polyline_1.add(p);
polyline_2.clear();
std::swap(terminus_0,terminus_1);
}
reverse[0] = true;
}
}
}
void SlicerLayer::joinPolylines(PolygonRef& polyline_0, PolygonRef& polyline_1,
const bool reverse[2]) const
{
if (reverse[0])
{
// reverse polyline_0
size_t size_0 = polyline_0.size();
for (size_t idx = 0U; idx != size_0/2; ++idx)
{
std::swap(polyline_0[idx], polyline_0[size_0-1-idx]);
}
}
if (reverse[1])
{
// reverse polyline_1 by adding in reverse order
for(int poly_idx = polyline_1.size() - 1; poly_idx >= 0; poly_idx--)
polyline_0.add(polyline_1[poly_idx]);
}
else
{
// append polyline_1 onto polyline_0
for(Point& p : polyline_1)
polyline_0.add(p);
}
polyline_1.clear();
}
SlicerLayer::TerminusTrackingMap::TerminusTrackingMap(Terminus::Index end_idx) :
m_terminus_old_to_cur_map(end_idx)
{
// Initialize map to everything points to itself since nothing has moved yet.
for (size_t idx = 0U; idx != end_idx; ++idx)
{
m_terminus_old_to_cur_map[idx] = Terminus{idx};
}
m_terminus_cur_to_old_map = m_terminus_old_to_cur_map;
}
void SlicerLayer::TerminusTrackingMap::updateMap(
size_t num_terms,
const Terminus *cur_terms, const Terminus *next_terms,
size_t num_removed_terms,
const Terminus *removed_cur_terms)
{
// save old locations
std::vector<Terminus> old_terms(num_terms);
for (size_t idx = 0U; idx != num_terms; ++idx)
{
old_terms[idx] = getOldFromCur(cur_terms[idx]);
}
// update using maps old <-> cur and cur <-> next
for (size_t idx = 0U; idx != num_terms; ++idx)
{
m_terminus_old_to_cur_map[old_terms[idx].asIndex()] = next_terms[idx];
Terminus next_term = next_terms[idx];
if (next_term != Terminus::INVALID_TERMINUS)
{
m_terminus_cur_to_old_map[next_term.asIndex()] = old_terms[idx];
}
}
// remove next locations that no longer exist
for (size_t rem_idx = 0U; rem_idx != num_removed_terms; ++rem_idx)
{
m_terminus_cur_to_old_map[removed_cur_terms[rem_idx].asIndex()] =
Terminus::INVALID_TERMINUS;
}
}
void SlicerLayer::connectOpenPolylinesImpl(Polygons& open_polylines, coord_t max_dist, coord_t cell_size, bool allow_reverse)
{
// below code closes smallest gaps first
std::priority_queue<PossibleStitch> stitch_queue =
findPossibleStitches(open_polylines, max_dist, cell_size, allow_reverse);
static const Terminus INVALID_TERMINUS = Terminus::INVALID_TERMINUS;
Terminus::Index terminus_end_idx = Terminus::endIndexFromPolylineEndIndex(open_polylines.size());
// Keeps track of how polyline end point locations move around
TerminusTrackingMap terminus_tracking_map(terminus_end_idx);
while (!stitch_queue.empty())
{
// Get the next best stitch
PossibleStitch next_stitch;
next_stitch = stitch_queue.top();
stitch_queue.pop();
Terminus old_terminus_0 = next_stitch.terminus_0;
Terminus terminus_0 = terminus_tracking_map.getCurFromOld(old_terminus_0);
if (terminus_0 == INVALID_TERMINUS)
{
// if we already used this terminus, then this stitch is no longer usable
continue;
}
Terminus old_terminus_1 = next_stitch.terminus_1;
Terminus terminus_1 = terminus_tracking_map.getCurFromOld(old_terminus_1);
if (terminus_1 == INVALID_TERMINUS)
{
// if we already used this terminus, then this stitch is no longer usable
continue;
}
size_t best_polyline_0_idx = terminus_0.getPolylineIdx();
size_t best_polyline_1_idx = terminus_1.getPolylineIdx();
// check to see if this completes a polygon
bool completed_poly = best_polyline_0_idx == best_polyline_1_idx;
if (completed_poly)
{
// finished polygon
PolygonRef polyline_0 = open_polylines[best_polyline_0_idx];
polygons.add(polyline_0);
polyline_0.clear();
Terminus cur_terms[2] = {{best_polyline_0_idx, false},
{best_polyline_0_idx, true}};
for (size_t idx = 0U; idx != 2U; ++idx)
{
terminus_tracking_map.markRemoved(cur_terms[idx]);
}
continue;
}
// we need to join these polylines
// plan how to join polylines
bool reverse[2];
planPolylineStitch(open_polylines, terminus_0, terminus_1, reverse);
// need to reread since planPolylineStitch can swap terminus_0/1
best_polyline_0_idx = terminus_0.getPolylineIdx();
best_polyline_1_idx = terminus_1.getPolylineIdx();
PolygonRef polyline_0 = open_polylines[best_polyline_0_idx];
PolygonRef polyline_1 = open_polylines[best_polyline_1_idx];
// join polylines according to plan
joinPolylines(polyline_0, polyline_1, reverse);
// update terminus_tracking_map
Terminus cur_terms[4] = {{best_polyline_0_idx, false},
{best_polyline_0_idx, true},
{best_polyline_1_idx, false},
{best_polyline_1_idx, true}};
Terminus next_terms[4] = {{best_polyline_0_idx, false},
INVALID_TERMINUS,
INVALID_TERMINUS,
{best_polyline_0_idx, true}};
if (reverse[0])
{
std::swap(next_terms[0],next_terms[1]);
}
if (reverse[1])
{
std::swap(next_terms[2],next_terms[3]);
}
// cur_terms -> next_terms has movement map
// best_polyline_1 is always removed
terminus_tracking_map.updateMap(4U, cur_terms, next_terms,
2U, &cur_terms[2]);
}
}
void SlicerLayer::stitch_extensive(Polygons& open_polylines)
{
//For extensive stitching find 2 open polygons that are touching 2 closed polygons.
// Then find the shortest path over this polygon that can be used to connect the open polygons,
// And generate a path over this shortest bit to link up the 2 open polygons.
// (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
while(1)
{
unsigned int best_polyline_1_idx = -1;
@@ -217,12 +552,12 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
best_result.polygonIdx = -1;
best_result.pointIdxA = -1;
best_result.pointIdxB = -1;
for(unsigned int polyline_1_idx = 0; polyline_1_idx < open_polylines.size(); polyline_1_idx++)
{
PolygonRef polyline_1 = open_polylines[polyline_1_idx];
if (polyline_1.size() < 1) continue;
{
GapCloserResult res = findPolygonGapCloser(polyline_1[0], polyline_1.back());
if (res.len > 0 && res.len < best_result.len)
@@ -237,7 +572,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
{
PolygonRef polyline_2 = open_polylines[polyline_2_idx];
if (polyline_2.size() < 1 || polyline_1_idx == polyline_2_idx) continue;
GapCloserResult res = findPolygonGapCloser(polyline_1[0], polyline_2.back());
if (res.len > 0 && res.len < best_result.len)
{
@@ -247,7 +582,7 @@ void SlicerLayer::stitch_extensive(Polygons& open_polylines)
}
}
}
if (best_result.len < POINT_MAX)
{
if (best_polyline_1_idx == best_polyline_2_idx)
@@ -325,7 +660,7 @@ GapCloserResult SlicerLayer::findPolygonGapCloser(Point ip0, Point ip1)
ret.pointIdxA = c1.pointIdx;
ret.pointIdxB = c2.pointIdx;
ret.AtoB = true;
if (ret.pointIdxA == ret.pointIdxB)
{
//Connection points are on the same line segment.
@@ -351,7 +686,7 @@ GapCloserResult SlicerLayer::findPolygonGapCloser(Point ip0, Point ip1)
p0 = p1;
}
lenB += vSize(p0 - ip0);
if (lenA < lenB)
{
ret.AtoB = true;
@@ -373,7 +708,7 @@ ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input)
for(unsigned int i=0; i<polygons[n].size(); i++)
{
Point p1 = polygons[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
@@ -402,18 +737,18 @@ ClosePolygonResult SlicerLayer::findPolygonPointClosestTo(Point input)
void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
{
Polygons open_polylines;
makeBasicPolygonLoops(mesh, open_polylines);
connectOpenPolylines(open_polylines);
// TODO: (?) for mesh surface mode: connect open polygons. Maybe the above algorithm can create two open polygons which are actually connected when the starting segment is in the middle between the two open polygons.
if (mesh->getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::NORMAL)
{ // don't stitch when using (any) mesh surface mode, i.e. also don't stitch when using mixed mesh surface and closed polygons, because then polylines which are supposed to be open will be closed
stitch(open_polylines);
}
if (extensive_stitching)
{
stitch_extensive(open_polylines);
@@ -427,7 +762,7 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
openPolylines.add(polyline);
}
}
for (PolygonRef polyline : open_polylines)
{
if (polyline.size() > 0)
@@ -441,9 +776,9 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
//Finally optimize all the polygons. Every point removed saves time in the long run.
polygons.simplify();
polygons.removeDegenerateVerts(); // remove verts connected to overlapping line segments
int xy_offset = mesh->getSettingInMicrons("xy_offset");
if (xy_offset != 0)
{
@@ -452,24 +787,30 @@ void SlicerLayer::makePolygons(const Mesh* mesh, bool keep_none_closed, bool ext
}
Slicer::Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keep_none_closed, bool extensive_stitching)
Slicer::Slicer(Mesh* mesh, int initial, int thickness, int slice_layer_count, bool keep_none_closed, bool extensive_stitching)
: mesh(mesh)
{
assert(slice_layer_count > 0);
TimeKeeper slice_timer;
layers.resize(slice_layer_count);
for(int32_t layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
{
layers[layer_nr].z = initial + thickness * layer_nr;
}
for(unsigned int mesh_idx = 0; mesh_idx < mesh->faces.size(); mesh_idx++)
{
const MeshFace& face = mesh->faces[mesh_idx];
Point3 p0 = mesh->vertices[face.vertex_index[0]].p;
Point3 p1 = mesh->vertices[face.vertex_index[1]].p;
Point3 p2 = mesh->vertices[face.vertex_index[2]].p;
const MeshVertex& v0 = mesh->vertices[face.vertex_index[0]];
const MeshVertex& v1 = mesh->vertices[face.vertex_index[1]];
const MeshVertex& v2 = mesh->vertices[face.vertex_index[2]];
Point3 p0 = v0.p;
Point3 p1 = v1.p;
Point3 p2 = v2.p;
int32_t minZ = p0.z;
int32_t maxZ = p0.z;
if (p1.z < minZ) minZ = p1.z;
@@ -482,22 +823,56 @@ Slicer::Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_cou
int32_t z = layer_nr * thickness + initial;
if (z < minZ) continue;
if (layer_nr < 0) continue;
SlicerSegment s;
s.endVertex = nullptr;
int end_edge_idx = -1;
if (p0.z < z && p1.z >= z && p2.z >= z)
{
s = project2D(p0, p2, p1, z);
end_edge_idx = 0;
if (p1.z == z)
{
s.endVertex = &v1;
}
}
else if (p0.z > z && p1.z < z && p2.z < z)
{
s = project2D(p0, p1, p2, z);
end_edge_idx = 2;
}
else if (p1.z < z && p0.z >= z && p2.z >= z)
{
s = project2D(p1, p0, p2, z);
end_edge_idx = 1;
if (p2.z == z)
{
s.endVertex = &v2;
}
}
else if (p1.z > z && p0.z < z && p2.z < z)
{
s = project2D(p1, p2, p0, z);
end_edge_idx = 0;
}
else if (p2.z < z && p1.z >= z && p0.z >= z)
{
s = project2D(p2, p1, p0, z);
end_edge_idx = 2;
if (p0.z == z)
{
s.endVertex = &v0;
}
}
else if (p2.z > z && p1.z < z && p0.z < z)
{
s = project2D(p2, p0, p1, z);
end_edge_idx = 1;
}
else
{
//Not all cases create a segment, because a point of a face could create just a dot, and two touching faces
@@ -506,14 +881,18 @@ Slicer::Slicer(const Mesh* mesh, int initial, int thickness, int slice_layer_cou
}
layers[layer_nr].face_idx_to_segment_idx.insert(std::make_pair(mesh_idx, layers[layer_nr].segments.size()));
s.faceIndex = mesh_idx;
s.endOtherFaceIdx = face.connected_face_index[end_edge_idx];
s.addedToPolygon = false;
layers[layer_nr].segments.push_back(s);
}
}
log("slice of mesh took %.3f seconds\n",slice_timer.restart());
for(unsigned int layer_nr=0; layer_nr<layers.size(); layer_nr++)
{
layers[layer_nr].makePolygons(mesh, keep_none_closed, extensive_stitching);
}
mesh->expandXY(mesh->getSettingInMicrons("xy_offset"));
log("slice make polygons took %.3f seconds\n",slice_timer.restart());
}
}//namespace cura
+393 -27
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,17 +30,17 @@ 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
@@ -41,14 +48,14 @@ class SlicerLayer
public:
std::vector<SlicerSegment> segments;
std::unordered_map<int, int> face_idx_to_segment_idx; // topology
int z;
int z = -1;
Polygons polygons;
Polygons openPolylines;
/*!
* 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
@@ -58,7 +65,7 @@ public:
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
*/
@@ -66,7 +73,7 @@ protected:
/*!
* 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
@@ -77,7 +84,7 @@ protected:
* 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.
@@ -87,18 +94,18 @@ protected:
/*!
* 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);
@@ -109,12 +116,371 @@ protected:
/*!
* 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:
/*!
* \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
{
public:
/*! A representation of Terminus that can be used as an array index.
*
* See \ref asIndex() for more information.
*/
using Index = size_t;
/*! A Terminus value representing an invalid value.
*
* This is used to record when Terminus are removed.
*/
static const Terminus INVALID_TERMINUS;
/*! 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
{
/*! 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
{
// in order if using back of line 0 and front of line 1
return terminus_0.isEnd() &&
!terminus_1.isEnd();
}
/*! 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
@@ -122,13 +488,13 @@ class Slicer
public:
std::vector<SlicerLayer> layers;
const Mesh* mesh; //!< The sliced mesh
Slicer(const Mesh* mesh, int initial, int thickness, int slice_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
@@ -139,7 +505,7 @@ public:
int64_t y = y0 + num / dx_01;
return y;
}
SlicerSegment project2D(Point3& p0, Point3& p1, Point3& p2, int32_t z) const
{
SlicerSegment seg;
@@ -151,7 +517,7 @@ public:
return seg;
}
void dumpSegmentsToHTML(const char* filename);
};
+204 -92
Ver Arquivo
@@ -1,16 +1,19 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "support.h"
#include <cmath> // sqrt
#include <utility> // pair
#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)
@@ -30,28 +33,74 @@ 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;
}
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
{
int max_layer_nr_support_mesh_filled;
for (max_layer_nr_support_mesh_filled = storage.support.supportLayers.size() - 1; max_layer_nr_support_mesh_filled >= 0; max_layer_nr_support_mesh_filled--)
{
const SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
if (support_layer.supportAreas.size() > 0)
{
break;
}
}
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, max_layer_nr_support_mesh_filled);
for (int layer_nr = 0; layer_nr < max_layer_nr_support_mesh_filled; layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
support_layer.support_mesh = support_layer.support_mesh.unionPolygons();
}
// initialization of supportAreasPerLayer
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
storage.support.supportLayers.emplace_back();
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
if (layer_count > storage.support.supportLayers.size())
{ // there might alsready be anti_overhang_area data in the supportLayers
storage.support.supportLayers.resize(layer_count);
}
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
{
continue;
}
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, storage.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
generateSupportInterface(storage, mesh, supportAreas, layer_count);
}
else
{
@@ -64,10 +113,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;
}
/*
@@ -93,48 +141,55 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
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 = storage.getSettingInMicrons("layer_height");
int extrusionWidth = storage.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
int support_xy_distance_overhang = mesh.getSettingInMicrons("support_xy_distance_overhang");
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");
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 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");
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;
}
// 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 max_dist_from_lower_layer = tanAngle * supportLayerThickness; // max dist which can be bridged
@@ -157,9 +212,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;
}
@@ -168,7 +222,7 @@ 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--)
@@ -176,7 +230,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
}
bool still_in_upper_empty_layers = true;
int overhang_points_pos = overhang_points.size() - 1;
Polygons supportLayer_last;
std::vector<Polygons> towerRoofs;
@@ -193,14 +246,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
}
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
@@ -211,10 +262,11 @@ 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);
}
supportLayer_this = supportLayer_this.unionPolygons(storage.support.supportLayers[layer_idx].support_mesh);
// move up from model
if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom)
{
@@ -249,12 +301,6 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
supportAreas[layer_idx] = supportLayer_this;
if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
{
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, (int)layer_idx);
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
}
@@ -291,6 +337,19 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
supportAreas[layer_idx] = touching_buildplate;
}
}
for (unsigned int layer_idx = supportAreas.size() - 1; layer_idx != (unsigned int) std::max(-1, storage.support.layer_nr_max_filled_layer) ; layer_idx--)
{
const Polygons& support_here = supportAreas[layer_idx];
if (support_here.size() > 0)
{
storage.support.layer_nr_max_filled_layer = layer_idx;
break;
}
}
storage.support.generated = true;
}
@@ -313,6 +372,9 @@ std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const Sli
Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer);
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
const SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
basic_overhang = basic_overhang.difference(support_layer.anti_overhang);
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
// support_extension = support_extension.intersection(supportLayer_supported);
// support_extension = support_extension.intersection(supportLayer_supportee);
@@ -331,11 +393,12 @@ void AreaSupport::detectOverhangPoints(
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)
@@ -343,17 +406,25 @@ 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)
{
Polygons part_poly_recomputed = part_poly.difference(storage.support.supportLayers[layer_idx].anti_overhang);
if (part_poly_recomputed.size() == 0)
{
continue;
}
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
overhang_points.back().second.push_back(part_poly);
overhang_points.back().second.push_back(part_poly_recomputed);
else
{
std::vector<Polygons> small_part_polys;
small_part_polys.push_back(part_poly);
small_part_polys.push_back(part_poly_recomputed);
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
}
}
@@ -404,15 +475,21 @@ void AreaSupport::handleTowers(
}
// make tower roofs
//for (Polygons& tower_roof : towerRoofs)
for (unsigned int r = 0; r < towerRoofs.size(); r++)
for (unsigned int roof_idx = 0; roof_idx < towerRoofs.size(); roof_idx++)
{
supportLayer_this = supportLayer_this.unionPolygons(towerRoofs[r]);
Polygons& tower_roof = towerRoofs[r];
if (tower_roof.size() > 0 && tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
Polygons& tower_roof = towerRoofs[roof_idx];
if (tower_roof.size() > 0)
{
towerRoofs[r] = tower_roof.offset(towerRoofExpansionDistance);
supportLayer_this = supportLayer_this.unionPolygons(tower_roof);
if (tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
{
tower_roof = tower_roof.offset(towerRoofExpansionDistance);
}
else
{
tower_roof.clear();
}
}
}
}
@@ -465,25 +542,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]);
}
}
}
+8 -11
Ver Arquivo
@@ -12,12 +12,11 @@ class AreaSupport {
public:
/*!
* Generate the support areas and support roof areas for all models.
* Generate the support areas and support skin areas for all models.
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
* \param layer_count total number of layers
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
private:
/*!
* Generate support polygons over all layers for one object.
@@ -33,14 +32,14 @@ private:
/*!
* Generate support roof areas and non-roof areas for a given mesh.
* Generate support skin areas and non-skin areas for a given mesh.
*
* \param storage Output storage: support area + support roof area output
* \param supportAreas The basic support areas for the current mesh
* \param layerThickness The layer height
* \param support_roof_height The thickness of the hammock in z directiontt
* \param storage Output storage: support area + support skin area output
* \param mesh The mesh to generate support skins for.
* \param support_areas The basic support areas for the current mesh
* \param layer_count The number of layers in this mesh group.
*/
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
static void generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
@@ -64,15 +63,13 @@ private:
* \param overhang_points stores overhang_points along with the layer index at which the overhang point occurs
* \param layer_count total number of layers
* \param supportMinAreaSqrt diameter of the minimal area which can be supported without a specialized strut
* \param extrusionWidth extrusionWidth
*/
static void detectOverhangPoints(
SliceDataStorage& storage,
SliceMeshStorage& mesh,
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
int layer_count,
int supportMinAreaSqrt,
int extrusionWidth
int supportMinAreaSqrt
);
/*!
+8 -2
Ver Arquivo
@@ -2,8 +2,10 @@
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "utils/math.h"
#include "timeEstimate.h"
#include "settings/settings.h"
#include "settings/settings.h"
namespace cura
{
@@ -27,7 +29,6 @@ void TimeEstimateCalculator::setFirmwareDefaults(const SettingsBaseVirtual* sett
acceleration = settings_base->getSettingInMillimetersPerSecond("machine_acceleration");
}
template<typename T> const T square(const T& a) { return a * a; }
void TimeEstimateCalculator::setPosition(Position newPos)
{
@@ -49,6 +50,11 @@ void TimeEstimateCalculator::setMaxXyJerk(double jerk)
max_xy_jerk = jerk;
}
void TimeEstimateCalculator::setMaxZFeedrate(double max_z_feedrate)
{
max_feedrate[Z_AXIS] = max_z_feedrate;
}
void TimeEstimateCalculator::reset()
{
extra_time = 0.0;
+2
Ver Arquivo
@@ -84,6 +84,8 @@ public:
void addTime(double time);
void setAcceleration(double acc); //!< Set the default acceleration to \p acc
void setMaxXyJerk(double jerk); //!< Set the max xy jerk to \p jerk
void setMaxZFeedrate(double max_z_feedrate); //!< Set the maximal feedrate in the z direction to \p max_z_feedrate
void reset();
double calculate();
+16
Ver Arquivo
@@ -24,6 +24,12 @@ AABB::AABB(const Polygons& polys)
calculate(polys);
}
AABB::AABB(const PolygonRef poly)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
calculate(poly);
}
void AABB::calculate(const Polygons& polys)
{
min = Point(POINT_MAX, POINT_MAX);
@@ -37,6 +43,16 @@ void AABB::calculate(const Polygons& polys)
}
}
void AABB::calculate(const PolygonRef poly)
{
min = Point(POINT_MAX, POINT_MAX);
max = Point(POINT_MIN, POINT_MIN);
for (const Point& p : poly)
{
include(p);
}
}
bool AABB::hit(const AABB& other) const
{
if (max.X < other.min.X) return false;
+2
Ver Arquivo
@@ -20,8 +20,10 @@ public:
AABB(); //!< initializes with invalid min and max
AABB(Point& min, Point& max); //!< initializes with given min and max
AABB(const Polygons& polys); //!< Computes the boundary box for the given polygons
AABB(const PolygonRef poly); //!< Computes the boundary box for the given polygons
void calculate(const Polygons& polys); //!< Calculates the aabb for the given polygons (throws away old min and max data of this aabb)
void calculate(const PolygonRef poly); //!< Calculates the aabb for the given polygon (throws away old min and max data of this aabb)
/*!
* Check whether this aabb overlaps with another.
+2 -2
Ver Arquivo
@@ -15,8 +15,8 @@ AABB3D::AABB3D()
bool AABB3D::hit(const AABB3D& other) const
{
if ( max.x < other.min.y
|| min.x > other.max.y
if ( max.x < other.min.x
|| min.x > other.max.x
|| max.y < other.min.y
|| min.y > other.max.y
|| max.z < other.min.z
-259
Ver Arquivo
@@ -1,259 +0,0 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_BUCKET_GRID_2D_H
#define UTILS_BUCKET_GRID_2D_H
#include <unordered_map>
#include <functional> // std::function
#include "logoutput.h"
#include "intpoint.h"
namespace cura
{
/*!
* Container for items with location for which the lookup for nearby items is optimized.
*
* It functions by hashing the items location and lookuping up based on the hash of that location and the hashes of nearby locations.
*
* We're mapping a cell location multiple times to an object within the cell,
* instead of mapping each cell location only once to a vector of objects within the cell.
*
* The first (current) implementation has the overhead of 'bucket-collisions' where all mappings of two different cells get placed in the same bucket,
* which causes findNearby to loop over unneeded elements.
* The second (alternative) implementation has the overhead and indirection of creating vectors and all that comes with it."
*
*/
template<typename T>
class BucketGrid2D
{
private:
typedef Point Cellidx;
/*!
* Returns a point for which the hash is at a grid position of \p relative_hash relative to \p p.
*
* \param p The point for which to get the relative point to hash
* \param relative_hash The relative position - in grid terms - of the relative point.
* \return A point for which the hash is at a grid position of \p relative_hash relative to \p p.
*/
inline Point getRelativeForHash(const Point& p, const Cellidx& relative_hash) const
{
return p + relative_hash * squareSize;
}
/*!
* A hash class representing the hash function object.
*/
struct PointHasher
{
/*!
* The basic hash function for a 2D grid position.
* \param p The grid location to hash
* \return the hash
*/
inline uint32_t pointHash_simple(const Cellidx& p) const
{
return p.X ^ (p.Y << 8);
}
/*!
* The hash function for a 2D position.
* \param point The location to hash
* \return the hash
*/
inline uint32_t pointHash(const Point& point) const
{
Cellidx p = point / squareSize;
return pointHash_simple(p);
}
/*
inline uint32_t pointHash(const Point& point, const Point& relativeHash) const
{
Point p = p / squareSize + relativeHash;
return pointHash_simple(p);
}*/
/*!
* The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
*/
int squareSize;
/*!
* Basic constructor.
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
*/
PointHasher(int squareSize) : squareSize(squareSize) {};
/*!
* See PointHasher::pointHash
*/
uint32_t operator()(const Point& p) const { return pointHash(p); };
};
/*!
* A helper predicate object which allways returns false when comparing two objects.
*
* This is used for mapping each point to a unique object, even when two objects have the same point associated with it.
*/
struct NeverEqual
{
template<typename S>
bool operator()(const S& p1, const S& p2) const { return false; };
};
private:
/*!
* Basic constructor.
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
*/
int squareSize;
PointHasher point_hasher; //!< The hasher used by the unordered_map
int max_load_factor; //!< The average number of elements per cell/bucket
/*!
* The map type used to associate points with their objects.
*/
typedef typename std::unordered_map<Point, T, PointHasher, NeverEqual> Map;
/*!
* The map used to associate points with their objects.
*/
Map point2object;
public:
/*!
* The constructor for a bucket grid.
*
* \param squareSize The horizontal and vertical size of a cell in the grid; the width and height of a bucket.
* \param initial_map_size The number of elements to be inserted
*/
BucketGrid2D(int squareSize, unsigned int initial_map_size = 4)
: squareSize(squareSize)
, point_hasher(squareSize)
, max_load_factor(2)
, point2object(initial_map_size / max_load_factor, point_hasher)
{
point2object.max_load_factor(max_load_factor); // we expect each cell to contain at least two points on average
point2object.reserve(initial_map_size);
}
/*!
* Get the size (height, width) of the cells.
*/
int getCellSize() const
{
return squareSize;
}
/*!
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
*
* \warning Objects may occur multiple times in the output!
*
* \param p The point for which to find close points.
* \param ret Ouput parameter: all objects close to \p p.
*/
void findNearbyObjects(Point& p, std::vector<T>& ret) const
{
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
Point relative_point = getRelativeForHash(p, Point(x,y));
int bucket_idx = point2object.bucket(relative_point); // when the hash is not a hash of a present item, the bucket_idx returned may be one already encountered
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
{
if (point_hasher(relative_point) == point_hasher(local_it->first))
{
ret.push_back(local_it->second);
}
}
}
}
};
/*!
* Find all objects with a point in a grid cell at a distance of one cell from the cell of \p p.
*
* \warning Objects may occur multiple times in the output!
*
* \param p The point for which to find close points.
* \return All objects close to \p p.
*/
std::vector<T> findNearbyObjects(Point& p) const
{
std::vector<T> ret;
findNearbyObjects(p, ret);
return ret;
}
static const std::function<bool(Point, const T&)> no_precondition;
/*!
* Find the nearest object to a given lcoation \p p, if there is any in a neighboring cell in the grid.
*
* \param p The point for which to find the nearest object.
* \param nearby Output parameter: the nearest object, if any
* \param precondition A precondition which must be satisfied before considering a \p object at a specific \p location as output
* \return Whether an object has been found.
*/
bool findNearestObject(Point& p, T& nearby, std::function<bool(Point location, const T& object)> precondition = no_precondition) const
{
bool found = false;
int64_t bestDist2 = squareSize * 9; // 9 > sqrt(2*2 + 2*2)^2 which is the square of the largest distance of a point to a point in a neighboring cell
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
int bucket_idx = point2object.bucket(getRelativeForHash(p, Point(x,y)));
for ( auto local_it = point2object.begin(bucket_idx); local_it!= point2object.end(bucket_idx); ++local_it )
{
if (!precondition(local_it->first, local_it->second))
{
continue;
}
int32_t dist2 = vSize2(local_it->first - p);
if (dist2 < bestDist2)
{
found = true;
nearby = local_it->second;
bestDist2 = dist2;
}
}
}
}
return found;
};
/*!
* Insert a new point into the bucket grid.
*
* \param p The location associated with \p t.
* \param t The object to insert in the grid cell for position \p p.
*/
void insert(Point& p, T t)
{
// typedef typename Map::iterator iter;
// std::pair<iter, bool> emplaced =
point2object.emplace(p, t);
// if (! emplaced.second)
// logError("Error! BucketGrid2D couldn't insert object!");
};
};
template<typename T>
const std::function<bool(Point, const T&)> BucketGrid2D<T>::no_precondition = [](Point loc, const T&) { return true; };
}//namespace cura
#endif//BUCKET_GRID_2D_H
+129
Ver Arquivo
@@ -0,0 +1,129 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_LAZY_INITIALIZATION_H
#define UTILS_LAZY_INITIALIZATION_H
#include <functional> // bind, function
#include "optional.h"
namespace cura
{
/*!
* Class for initializing an object only when it's requested
*
* Credits to Johannes Goller
*
* \tparam T The type of the object to instantiate lazily
* \tparam Args The types of the arguments to the constructor or constructor function object
*/
template <typename T, typename... Args>
class LazyInitialization : public std::optional<T>
{
public:
/*!
* Delayed constructor call of T class
*
* \warning passing references or pointers as parameters means these objects will be given to the constructor at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(Args... args)
: std::optional<T>()
, constructor(
[args...]()
{
return new T(args...);
}
)
{ }
/*!
* Delayed function call for creating a T object
*
* Performs a copy from the return value of the function on the stack to the heap.
*
* \warning passing references or pointers as parameters means these objects will be given to the function object at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(const std::function<T (Args...)>& f, Args... args)
: std::optional<T>()
, constructor(
[f, args...]()
{
return new T(f(args...));
}
)
{ }
/*!
* Delayed function call for creating a T object
*
* \warning passing references or pointers as parameters means these objects will be given to the function object at evaluation time.
* Make sure these references/pointers are not invalidated between construction of the lazy object and the evaluation.
*/
LazyInitialization(const std::function<T* (Args...)>& f, Args... args)
: std::optional<T>()
, constructor(
[f, args...]()
{
return f(args...);
}
)
{
}
LazyInitialization(LazyInitialization<T, Args...>& other) //!< copy constructor
: std::optional<T>(other)
, constructor(other.constructor)
{
}
LazyInitialization(LazyInitialization<T, Args...>&& other) //!< move constructor
: std::optional<T>(other)
{
constructor = std::move(other.constructor);
}
/*!
* Dereference this lazy object
*
* Calls constructor if object isn't constructed yet.
*/
T& operator*()
{
if (!std::optional<T>::instance)
{
std::optional<T>::instance = constructor();
}
return std::optional<T>::operator*();
}
T* operator->() const
{
if (!std::optional<T>::instance)
{
std::optional<T>::instance = constructor();
}
return std::optional<T>::operator->();
}
LazyInitialization<T, Args...>& operator=(LazyInitialization<T, Args...>&& other)
{
std::optional<T>::operator=(other);
constructor = other.constructor;
return *this;
}
void swap(LazyInitialization<T, Args...>& other)
{
std::optional<T>::swap(other);
std::swap(constructor, other.constructor);
}
private:
std::function<T* ()> constructor;
};
}//namespace cura
#endif // UTILS_LAZY_INITIALIZATION_H
+120 -1
Ver Arquivo
@@ -2,6 +2,8 @@
#include "linearAlg2D.h"
#include <cmath> // atan2
#include <cassert>
#include <algorithm> // swap
#include "intpoint.h" // dot
@@ -25,4 +27,121 @@ float LinearAlg2D::getAngleLeft(const Point& a, const Point& b, const Point& c)
}
}
}
bool LinearAlg2D::getPointOnLineWithDist(const Point p, const Point a, const Point b, int64_t dist, Point& result)
{
// result
// v
// b<----r---a.......x
// '-. :
// '-. :
// '-.p
const Point ab = b - a;
const int64_t ab_size = vSize(ab);
const Point ap = p - a;
const int64_t ax_size = (ab_size < 50)? dot(normal(ab, 1000), ap) / 1000 : dot(ab, ap) / ab_size;
const int64_t ap_size2 = vSize2(ap);
const int64_t px_size = sqrt(std::max(int64_t(0), ap_size2 - ax_size * ax_size));
if (px_size > dist)
{
return false;
}
const int64_t xr_size = sqrt(dist * dist - px_size * px_size);
if (ax_size <= 0)
{ // x lies before ab
const int64_t ar_size = xr_size + ax_size;
if (ar_size < 0 || ar_size > ab_size)
{ // r lies outisde of ab
return false;
}
else
{
result = a + normal(ab, ar_size);
return true;
}
}
else if (ax_size >= ab_size)
{ // x lies after ab
// result
// v
// a-----r-->b.......x
// '-. :
// '-. :
// '-.p
const int64_t ar_size = ax_size - xr_size;
if (ar_size < 0 || ar_size > ab_size)
{ // r lies outisde of ab
return false;
}
else
{
result = a + normal(ab, ar_size);
return true;
}
}
else // ax_size > 0 && ax_size < ab_size
{ // x lies on ab
// result is either or
// v v
// a-----r-----------x-----------r----->b
// '-. : .-'
// '-. : .-'
// '-.p.-'
// or there is not result:
// v v
// r a-------x---->b r
// '-. : .-'
// '-. : .-'
// '-.p.-'
// try r in both directions
const int64_t ar1_size = ax_size - xr_size;
if (ar1_size >= 0)
{
result = a + normal(ab, ar1_size);
return true;
}
const int64_t ar2_size = ax_size + xr_size;
if (ar2_size < ab_size)
{
result = a + normal(ab, ar2_size);
return true;
}
return false;
}
}
bool LinearAlg2D::lineSegmentsCollide(Point a_from_transformed, Point a_to_transformed, Point b_from_transformed, Point b_to_transformed)
{
assert(std::abs(a_from_transformed.Y - a_to_transformed.Y) < 2 && "line a is supposed to be transformed to be aligned with the X axis!");
assert(a_from_transformed.X - 2 <= a_to_transformed.X && "line a is supposed to be aligned with X axis in positive direction!");
if ((b_from_transformed.Y >= a_from_transformed.Y && b_to_transformed.Y <= a_from_transformed.Y) || (b_to_transformed.Y >= a_from_transformed.Y && b_from_transformed.Y <= a_from_transformed.Y))
{
if(b_to_transformed.Y == b_from_transformed.Y)
{
if (b_to_transformed.X < b_from_transformed.X)
{
std::swap(b_to_transformed.X, b_from_transformed.X);
}
if (b_from_transformed.X > a_to_transformed.X)
{
return false;
}
if (b_to_transformed.X < a_from_transformed.X)
{
return false;
}
return true;
}
else
{
int64_t x = b_from_transformed.X + (b_to_transformed.X - b_from_transformed.X) * (a_from_transformed.Y - b_from_transformed.Y) / (b_to_transformed.Y - b_from_transformed.Y);
if (x >= a_from_transformed.X && x <= a_to_transformed.X)
{
return true;
}
}
}
return false;
}
} // namespace cura
+78
Ver Arquivo
@@ -0,0 +1,78 @@
#include "ListPolyIt.h"
#include <cmath> // isfinite
#include <sstream> // ostream
#include "AABB.h" // for debug output svg html
#include "SVG.h"
namespace cura
{
void ListPolyIt::convertPolygonsToLists(Polygons& polys, ListPolygons& result)
{
for (PolygonRef poly : polys)
{
result.emplace_back();
convertPolygonToList(poly, result.back());
}
}
void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result)
{
#ifdef DEBUG
Point last = poly.back();
#endif // DEBUG
for (Point& p : poly)
{
result.push_back(p);
#ifdef DEBUG
// usually polygons shouldn't have such degenerate verts
// in PolygonProximityLinker (where this function is (also) used) it is
// required to not have degenerate verts, because verts are mapped
// to links, but if two different verts are at the same place the mapping fails.
assert(p != last);
last = p;
#endif // DEBUG
}
}
void ListPolyIt::convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons)
{
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
polygons[poly_idx].clear();
convertListPolygonToPolygon(list_polygons[poly_idx], polygons[poly_idx]);
}
}
void ListPolyIt::convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon)
{
for (Point& p : list_polygon)
{
polygon.add(p);
}
}
ListPolyIt ListPolyIt::insertPointNonDuplicate(const ListPolyIt before, const ListPolyIt after, const Point to_insert)
{
if (to_insert == before.p())
{
return before;
}
else if (to_insert == after.p())
{
return after;
}
else
{
ListPolygon& poly = *after.poly;
return ListPolyIt(poly, poly.insert(after.it, to_insert));
}
}
}//namespace cura
+151
Ver Arquivo
@@ -0,0 +1,151 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_LIST_POLY_IT_H
#define UTILS_LIST_POLY_IT_H
#include <vector>
#include <list>
#include "intpoint.h"
#include "polygon.h"
namespace cura
{
/*!
* A wrapper class for a ListPolygon::iterator and a reference to the containing ListPolygon
*/
class ListPolyIt
{
public:
ListPolygon* poly; //!< The polygon
ListPolygon::iterator it; //!< The iterator into ListPolyIt::poly
ListPolyIt(const ListPolyIt& other)
: poly(other.poly)
, it(other.it)
{
}
ListPolyIt(ListPolygon& poly, ListPolygon::iterator it)
: poly(&poly)
, it(it)
{
}
Point& p() const
{
return *it;
}
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
* \param other The ListPolyIt to test for equality
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
*/
bool operator==(const ListPolyIt& other) const
{
return poly == other.poly && it == other.it;
}
bool operator!=(const ListPolyIt& other) const
{
return !(*this == other);
}
ListPolyIt& operator=(const ListPolyIt& other)
{
poly = other.poly;
it = other.it;
return *this;
}
//! move the iterator forward (and wrap around at the end)
ListPolyIt& operator++()
{
++it;
if (it == poly->end()) { it = poly->begin(); }
return *this;
}
//! move the iterator backward (and wrap around at the beginning)
ListPolyIt& operator--()
{
if (it == poly->begin()) { it = poly->end(); }
--it;
return *this;
}
//! move the iterator forward (and wrap around at the end)
ListPolyIt next() const
{
ListPolyIt ret(*this);
++ret;
return ret;
}
//! move the iterator backward (and wrap around at the beginning)
ListPolyIt prev() const
{
ListPolyIt ret(*this);
--ret;
return ret;
}
//! Remove this point from the list polygon
void remove() const
{
poly->erase(it);
}
/*!
* Convert Polygons to ListPolygons
*
* \param polys The polygons to convert
* \param result The converted polygons
*/
static void convertPolygonsToLists(Polygons& polys, ListPolygons& result);
/*!
* Convert Polygons to ListPolygons
*
* \param polys The polygons to convert
* \param result The converted polygons
*/
static void convertPolygonToList(PolygonRef poly, ListPolygon& result);
/*!
* Convert ListPolygons to Polygons
*
* \param list_polygons The polygons to convert
* \param polygons The converted polygons
*/
static void convertListPolygonsToPolygons(ListPolygons& list_polygons, Polygons& polygons);
/*!
* Convert ListPolygons to Polygons
*
* \param list_polygons The polygons to convert
* \param polygons The converted polygons
*/
static void convertListPolygonToPolygon(ListPolygon& list_polygon, PolygonRef polygon);
/*!
* Insert a point into a ListPolygon if it's not a duplicate of the point before or the point after.
*
* \param before Iterator to the point before the point to insert
* \param after Iterator to the point after the point to insert
* \param to_insert The point to insert into the ListPolygon in between \p before and \p after
* \return Iterator to the newly inserted point, or \p before or \p after in case to_insert was already in the polygon
*/
static ListPolyIt insertPointNonDuplicate(const ListPolyIt before, const ListPolyIt after, const Point to_insert);
};
}//namespace cura
namespace std
{
/*!
* Hash function for \ref ListPolyIt
*/
template <>
struct hash<cura::ListPolyIt>
{
size_t operator()(const cura::ListPolyIt& lpi) const
{
return std::hash<cura::Point>()(lpi.p());
}
};
}//namespace std
#endif//UTILS_LIST_POLY_IT_H
+501
Ver Arquivo
@@ -0,0 +1,501 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include <cmath> // isfinite
#include <sstream> // ostream
#include "PolygonProximityLinker.h"
#include "linearAlg2D.h"
#include "AABB.h" // for debug output svg html
#include "SVG.h"
namespace cura
{
PolygonProximityLinker::PolygonProximityLinker(Polygons& polygons, int proximity_distance)
: polygons(polygons)
, proximity_distance(proximity_distance)
, proximity_distance_2(proximity_distance * proximity_distance)
, line_grid(proximity_distance, polygons.pointCount(), 3.0f)
{
// heuristic reserve a good amount of elements
proximity_point_links.reserve(polygons.pointCount()); // When the whole model consists of thin walls, there will generally be a link for every point, plus some endings minus some points which map to eachother
// convert to list polygons for insertion of points
ListPolyIt::convertPolygonsToLists(polygons, list_polygons);
// link each corner to itself
addSharpCorners();
// map each vertex onto nearby line segments
findProximatePoints();
// add links where line segments diverge from below the proximity distance to over the proximity distance
addProximityEndings();
// convert list polygons back
ListPolyIt::convertListPolygonsToPolygons(list_polygons, polygons);
// proximity2HTML("linker.html");
}
bool PolygonProximityLinker::isLinked(Point from)
{
return point_to_link.find(from) != point_to_link.end();
}
std::pair<PolygonProximityLinker::Point2Link::iterator, PolygonProximityLinker::Point2Link::iterator> PolygonProximityLinker::getLinks(Point from)
{
std::pair<Point2Link::iterator, Point2Link::iterator> from_link_pair = point_to_link.equal_range(from);
#ifdef DEBUG
for (Point2Link::iterator it = from_link_pair.first; it != from_link_pair.second; ++it)
{
if (!(it->second.a.p() == from || it->second.b.p() == from))
{
std::cerr << " ERROR!\n" << it->first << " == " << from << "\n";
std::cerr << "should be either " << it->second.a.p() << " or " << it->second.b.p() << "\n";
std::cerr << (from_link_pair.first == from_link_pair.second) << " ; " << (from_link_pair.first == point_to_link.end()) << " ; " << (from_link_pair.second == point_to_link.end()) << "\n";
std::cerr << std::hash<Point>()(from) << " hashes " << std::hash<Point>()(it->second.a.p()) << " or " << std::hash<Point>()(it->second.b.p()) << "\n";
// std::cerr << "ERROR! some point got mapped to a link which doesn't have the point as one of the end points!\n";
std::cerr << "\n all links:\n";
for (std::pair<const Point, const ProximityPointLink> pair : point_to_link)
{
std::cerr << pair.first << " : " << pair.second.a.p() << "-" <<pair.second.b.p() << "\n";
}
std::cerr << "\n link set \n";
for (const ProximityPointLink link : proximity_point_links)
{
std::cerr << link.a.p() << "-" << link.b.p() << " hashes as " << std::hash<ProximityPointLink>()(link) << "\n";
}
assert(false && "some point got mapped to a link which doesn't have the point as one of the end points!");
}
}
#endif
return from_link_pair;
}
void PolygonProximityLinker::createLineGrid()
{
for (unsigned int poly_idx = 0; poly_idx < list_polygons.size(); poly_idx++)
{
ListPolygon& poly = list_polygons[poly_idx];
for (ListPolygon::iterator it = poly.begin(); it != poly.end(); ++it)
{
ListPolyIt point_it(poly, it);
line_grid.insert(point_it);
}
}
}
void PolygonProximityLinker::findProximatePoints()
{
createLineGrid();
for (unsigned int poly_idx = 0; poly_idx < list_polygons.size(); poly_idx++)
{
ListPolygon& poly = list_polygons[poly_idx];
for (ListPolygon::iterator it = poly.begin(); it != poly.end(); ++it)
{
ListPolyIt point_it(poly, it);
if (new_points.find(point_it) == new_points.end())
{
// handle new_points separately
// to prevent this:
// 1 3 5 7
// o<-.
// : 'o<-.
// : /: o<.
// : / : /: 'o<.
// : / : / : / :
// :/ :/ :/ : etc.
// o--->o-->o-->o->
// 2 4 6 8
std::unordered_set<ListPolyIt> nearby_lines;
std::function<bool (const ListPolyIt&)> process_func = [&nearby_lines](const ListPolyIt& elem)
{
nearby_lines.emplace(elem);
return true;
};
line_grid.processNearby(point_it.p(), proximity_distance, process_func);
for (const ListPolyIt& nearby_line : nearby_lines)
{
findProximatePoints(point_it, *nearby_line.poly, nearby_line, nearby_line.next());
}
}
}
}
for (const ListPolyIt& new_point_it : new_points)
{
// link with existing points, but don't introduce new points for line segments
// to prevent this:
// 1 3 5 7
// o<-.
// : 'o<-.
// : /: o<.
// : / : /: 'o<.
// : / : / : / :
// :/ :/ :/ : etc.
// o--->o-->o-->o->
// 2 4 6 8
std::unordered_set<ListPolyIt> nearby_vert_its;
std::function<bool (const ListPolyIt&)> process_func = [&nearby_vert_its](const ListPolyIt& elem)
{
nearby_vert_its.emplace(elem);
return true;
};
line_grid.processNearby(new_point_it.p(), proximity_distance, process_func);
// because we use the same line_grid as before the resulting nearby_points
// will also have points which are not nearby, (But when the line segment *is* nearby.)
// but at least we don't have to create a whole new SparsePointGrid
for (const ListPolyIt& nearby_vert_it : nearby_vert_its)
{
Point new_point = new_point_it.p();
Point nearby_vert = nearby_vert_it.p();
int64_t dist2 = vSize2(new_point - nearby_vert);
if (dist2 < proximity_distance_2
&& new_point != nearby_vert // not the same point
)
{
addProximityLink(new_point_it, nearby_vert_it, sqrt(dist2), ProximityPointLinkType::NORMAL);
}
}
}
}
void PolygonProximityLinker::findProximatePoints(const ListPolyIt a_point_it, ListPolygon& to_list_poly, const ListPolyIt b_from_it, const ListPolyIt b_to_it)
{
const Point& a_point = a_point_it.p();
const Point& b_from = b_from_it.p();
const Point& b_to = b_to_it.p();
if (a_point_it == b_from_it || a_point_it == b_to_it) // we currently consider a linesegment directly connected to [from]
{
return;
}
Point closest = LinearAlg2D::getClosestOnLineSegment(a_point, b_from, b_to);
int64_t dist2 = vSize2(closest - a_point);
if (dist2 > proximity_distance_2
|| (a_point_it.poly == &to_list_poly
&& dot(a_point_it.next().p() - a_point, b_to - b_from) > 0
&& dot(a_point - a_point_it.prev().p(), b_to - b_from) > 0 ) // line segments are likely connected, because the winding order is in the same general direction
)
{ // line segment too far away to be proximate
return;
}
int64_t dist = sqrt(dist2);
if (shorterThen(closest - b_from, 10))
{
addProximityLink(a_point_it, b_from_it, dist, ProximityPointLinkType::NORMAL);
}
else if (shorterThen(closest - b_to, 10))
{
addProximityLink(a_point_it, b_to_it, dist, ProximityPointLinkType::NORMAL);
}
else
{
if (new_points.find(a_point_it) == new_points.end())
{
// don't introduce new points for newly introduced points
// to prevent this:
// 1 3 5 7
// o<-.
// : 'o<-.
// : /: o<.
// : / : /: 'o<.
// : / : / : / :
// :/ :/ :/ : etc.
// o--->o-->o-->o->
// 2 4 6 8
ListPolyIt new_it = addNewPolyPoint(closest, b_from_it, b_to_it, b_to_it);
addProximityLink(a_point_it, new_it, dist, ProximityPointLinkType::NORMAL);
}
}
}
bool PolygonProximityLinker::addProximityLink(ListPolyIt from, ListPolyIt to, int64_t dist, const ProximityPointLinkType type)
{
std::pair<ProximityPointLinks::iterator, bool> result =
proximity_point_links.emplace(from, to, dist, type);
if (!result.second)
{ // links was already made!
return false;
}
ProximityPointLinks::iterator it = result.first;
assert(it->a == from);
assert(it->b == to);
assert(it->a.p() == from.p());
assert(it->b.p() == to.p());
addToPoint2LinkMap(from.p(), it);
addToPoint2LinkMap(to.p(), it);
return result.second;
}
bool PolygonProximityLinker::addCornerLink(ListPolyIt corner_point, const ProximityPointLinkType type)
{
constexpr int dist = 0;
std::pair<ProximityPointLinks::iterator, bool> result =
proximity_point_links.emplace(corner_point, corner_point, dist, type);
if (!result.second)
{ // links was already made!
return false;
}
ProximityPointLinks::iterator it = result.first;
assert(it->a == corner_point);
assert(it->b == corner_point);
addToPoint2LinkMap(corner_point.p(), it);
return result.second;
}
void PolygonProximityLinker::addProximityEndings()
{
ProximityPointLinks new_links; // Where to store the new links temporarily (Don't add them to the map we are iterating over!)
for (const ProximityPointLink& link : proximity_point_links)
{
if (link.dist == proximity_distance)
{ // its ending itself
continue;
}
const ListPolyIt& a_1 = link.a;
const ListPolyIt& b_1 = link.b;
// an overlap segment can be an ending in two directions
{
ListPolyIt a_2 = a_1.next();
ListPolyIt b_2 = b_1.prev();
addProximityEnding(link, a_2, b_2, a_2, b_1, new_links);
}
{
ListPolyIt a_2 = a_1.prev();
ListPolyIt b_2 = b_1.next();
addProximityEnding(link, a_2, b_2, a_1, b_2, new_links);
}
}
for (const ProximityPointLink& link : new_links)
{
addProximityLink(link.a, link.b, link.dist, link.type);
}
}
void PolygonProximityLinker::addProximityEnding(const ProximityPointLink& link, const ListPolyIt& a2_it, const ListPolyIt& b2_it, const ListPolyIt& a_after_middle, const ListPolyIt& b_after_middle, ProximityPointLinks& result)
{
Point& a1 = link.a.p();
Point& a2 = a2_it.p();
Point& b1 = link.b.p();
Point& b2 = b2_it.p();
Point a = a2 - a1;
Point b = b2 - b1;
if (isLinked(a2_it.p()) && isLinked(b2_it.p())) // overlap area stops at one side
{
// TODO: add proximity endings between point and line ?
// would be good for:
// ----+
// |
// +-----
// would be bad for
// ----+-+-----
return;
}
if (isLinked(a2_it, link.b) || isLinked(b2_it, link.a))
{ // other side of ending continues to overlap with the same ending
// link considered
// *
// o<--o<--o<--
// : : .` one more link from the upper side
// o-->o last overlap point on this side
// |
// v
// 0
return;
}
if (a2_it == b2_it)
{ // overlap ends in pointy end
// o-->o-->o
// : : : \,
// : : : o wasn't linked yet because it's connected to the upper and lower part
// : : :,/
// o<--o<--o
result.emplace(ProximityPointLink(a2_it, a2_it, 0, ProximityPointLinkType::ENDING_CORNER));
return;
}
int64_t dist = proximityEndingDistance(a1, a2, b1, b2, link.dist);
if (dist < 0) { return; }
int64_t a_length2 = vSize2(a);
int64_t b_length2 = vSize2(b);
if (dist*dist > std::min(a_length2, b_length2) )
{ // TODO remove this /\ case if error below is never shown
// DEBUG_PRINTLN("Next point should have been linked already!!");
dist = std::sqrt(std::min(a_length2, b_length2));
if (a_length2 < b_length2)
{
Point b_p = b1 + normal(b, dist);
ListPolyIt new_b = addNewPolyPoint(b_p, link.b, b2_it, b_after_middle);
result.emplace(ProximityPointLink(a2_it, new_b, proximity_distance, ProximityPointLinkType::ENDING));
}
else if (b_length2 < a_length2)
{
Point a_p = a1 + normal(a, dist);
ListPolyIt new_a = addNewPolyPoint(a_p, link.a, a2_it, a_after_middle);
result.emplace(ProximityPointLink(new_a, b2_it, proximity_distance, ProximityPointLinkType::ENDING));
}
else // equal
{
result.emplace(ProximityPointLink(a2_it, b2_it, proximity_distance, ProximityPointLinkType::ENDING));
}
}
else if (dist > 0)
{
Point a_p = a1 + normal(a, dist);
ListPolyIt new_a = addNewPolyPoint(a_p, link.a, a2_it, a_after_middle);
Point b_p = b1 + normal(b, dist);
ListPolyIt new_b = addNewPolyPoint(b_p, link.b, b2_it, b_after_middle);
result.emplace(ProximityPointLink(new_a, new_b, proximity_distance, ProximityPointLinkType::ENDING));
}
else if (dist == 0)
{
// addProximityLink(link.a, link.b, proximity_distance);
// won't be inserted any way, because there already is such a link!
}
}
ListPolyIt PolygonProximityLinker::addNewPolyPoint(const Point point, const ListPolyIt line_start, const ListPolyIt line_end, const ListPolyIt before_this)
{
if (point == line_start.p())
{
return line_start;
}
if (point == line_end.p())
{
return line_end;
}
ListPolygon::iterator new_p = before_this.poly->insert(before_this.it, point);
ListPolyIt lpi(*before_this.poly, new_p);
line_grid.insert(lpi);
// TODO: remove new part of old segment from the line_grid (?)
new_points.emplace(lpi);
return lpi;
}
int64_t PolygonProximityLinker::proximityEndingDistance(Point& a1, Point& a2, Point& b1, Point& b2, int a1b1_dist)
{
int overlap = proximity_distance - a1b1_dist;
Point a = a2-a1;
Point b = b2-b1;
double cos_angle = INT2MM2(dot(a, b)) / vSizeMM(a) / vSizeMM(b);
// result == .5*overlap / tan(.5*angle) == .5*overlap / tan(.5*acos(cos_angle))
// [wolfram alpha] == 0.5*overlap * sqrt(cos_angle+1)/sqrt(1-cos_angle)
// [assuming positive x] == 0.5*overlap / sqrt( 2 / (cos_angle + 1) - 1 )
if (cos_angle <= 0
|| ! std::isfinite(cos_angle) )
{
return -1; // line_width / 2;
}
else if (cos_angle > .9999) // values near 1 can lead too large numbers for 1/x
{
return std::min(vSize(b), vSize(a));
}
else
{
int64_t dist = overlap * double ( 1.0 / (2.0 * sqrt(2.0 / (cos_angle+1.0) - 1.0)) );
return dist;
}
}
void PolygonProximityLinker::addSharpCorners()
{
for (ListPolygon& poly : list_polygons)
{
ListPolyIt here(poly, --poly.end());
ListPolyIt prev = here.prev();
for (ListPolygon::iterator it = poly.begin(); it != poly.end(); ++it)
{
ListPolyIt next(poly, it);
if (LinearAlg2D::isAcuteCorner(prev.p(), here.p(), next.p()) > 0)
{
addCornerLink(here, ProximityPointLinkType::SHARP_CORNER);
}
prev = here;
here = next;
}
}
}
void PolygonProximityLinker::addToPoint2LinkMap(Point p, ProximityPointLinks::iterator it)
{
const ProximityPointLink& link = *it;
point_to_link.emplace(p, link); // copy element from proximity_point_links set to Point2Link map
assert(p == link.a.p() || p == link.b.p());
}
void PolygonProximityLinker::proximity2HTML(const char* filename) const
{
PolygonProximityLinker copy = *this; // copy, cause getFlow might change the state of the overlap computation!
AABB aabb(copy.polygons);
aabb.expand(200);
SVG svg(filename, aabb, Point(1024 * 2, 1024 * 2));
svg.writeAreas(copy.polygons);
{ // output points and coords
for (ListPolygon poly : copy.list_polygons)
{
for (Point& p : poly)
{
svg.writePoint(p, true);
}
}
}
{ // output links
// output normal links
for (const ProximityPointLink& link : copy.proximity_point_links)
{
svg.writePoint(link.a.p(), false, 3, SVG::Color::GRAY);
svg.writePoint(link.b.p(), false, 3, SVG::Color::GRAY);
Point a = svg.transform(link.a.p());
Point b = svg.transform(link.b.p());
svg.printf("<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:rgb(%d,%d,0);stroke-width:1\" />", a.X, a.Y, b.X, b.Y, link.dist == proximity_distance? 0 : 255, link.dist==proximity_distance? 255 : 0);
}
}
}
bool PolygonProximityLinker::isLinked(ListPolyIt a, ListPolyIt b)
{
ProximityPointLink test_link(a, b, 0, ProximityPointLinkType::NORMAL);
return proximity_point_links.find(test_link) != proximity_point_links.end();
}
const ProximityPointLink* PolygonProximityLinker::getLink(ListPolyIt a, ListPolyIt b)
{
ProximityPointLink test_link(a, b, 0, ProximityPointLinkType::NORMAL);
ProximityPointLinks::const_iterator found = proximity_point_links.find(test_link);
if (found != proximity_point_links.end())
{
return &*found;
}
return nullptr;
}
}//namespace cura
+222
Ver Arquivo
@@ -0,0 +1,222 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_POLYGON_PROXIMITY_LINKER_H
#define UTILS_POLYGON_PROXIMITY_LINKER_H
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <utility> // pair
#include <functional> // hash function object
#include "intpoint.h"
#include "polygon.h"
#include "ListPolyIt.h"
#include "ProximityPointLink.h"
#include "SparseLineGrid.h"
namespace cura
{
/*!
* Class for computing which parts of polygons are close to which other parts of polygons
* A link always occurs between a point already on a polygon and either another point of a polygon or a point on a line segment of a polygon.
*
* In the latter case we insert the point into the polygon so that we can later look up by how much to reduce the extrusion at the corresponding line segment.
* This is the reason that the polygons are converted to (linked) lists before the proximity linking computation takes place, after which they are converted back.
*
* At the end of a sequence of proximity links the polygon segments diverge away from each other.
* Therefore points are introduced on the line segments involved and a link is created with a link distance of exactly the PolygonProximityLinker::proximity_distance.
*
* We end up with links which include a boolean field to represent whether the link is already processed from outside.
* This is used by functions which use the PolygonProximityLinker class when there is being looped over points in a polygon, which by definition loops over all links twice.
*
* Each point on the polygons maps to a link, so that we can easily look up which links corresponds to the current line segment being handled when compensating for wall overlaps for example.
*
* The main functionality of this class is performed by the constructor.
*/
class PolygonProximityLinker
{
public:
typedef std::unordered_set<ProximityPointLink> ProximityPointLinks; //!< The type of PolygonProximityLinker::overlap_point_links
typedef std::unordered_multimap<Point, ProximityPointLink> Point2Link; //!< The type of PolygonProximityLinker::point_to_link
private:
/////////////////////////////////////////////////////////////////////////////////////////////
/*!
* Locator to retrieve line segment data from a \ref ListPolyIt
*/
struct ListPolyItSegmentLocator
{
std::pair<Point, Point> operator()(const ListPolyIt& val) const
{
return std::pair<Point, Point>(val.p(), val.next().p());
}
};
Polygons& polygons; //!< The polygons for which to compensate overlapping walls for
ListPolygons list_polygons; //!< The PolygonProximityLinker::polygons converted
int proximity_distance; //!< The line width of the walls
int proximity_distance_2; //!< The squared line width of the walls
SparseLineGrid<ListPolyIt, ListPolyItSegmentLocator> line_grid; //!< Mapping from locations to lines
std::unordered_set<ListPolyIt> new_points; //!< The newly inserted points to create new links.
ProximityPointLinks proximity_point_links; //!< mapping from each link to its attributes
Point2Link point_to_link; //!< mapping from each point to the/a corresponding link (collisions are ignored as of yet)
/*!
* Create the initial \ref PolygonProximityLinker::line_grid
*
* Map locations where line segments of the input polygons occur to iterators pointing to the start of those line segments.
*
* Note that when points are introduced which split line segments in two,
* the line_grid maps locations to segments which are not close any more.
*/
void createLineGrid();
void findProximatePoints(); //!< find the basic proximity links (for trapezoids) and record them into PolygonProximityLinker::overlap_point_links
/*!
* Find the basic proximity link (for a trapezoid) between a given point and a line segment
* and record them into PolygonProximityLinker::overlap_point_links
*
* \param a_point_it Iterator to the point from which to check for proximity
* \param to_list_poly The polygon in which the line segment occurs
* \param b_from_it iterator to the one end point of the line segment
* \param b_to_it iterator to the other end point of the line segment
*/
void findProximatePoints(const ListPolyIt a_point_it, ListPolygon& to_list_poly, const ListPolyIt b_from_it, const ListPolyIt b_to_it);
/*!
* Add a new point to the polygon on a line segment between \p line_start and \p line_end
*
* Don't add the point if it's already the same as either of the end points of the line segment.
*
* \param point The point to insert
* \param line_start The start of the line segment on which to insert
* \param line_end The end of the line segment on which to insert
* \param before_this Either \p line_start or \p line_end such that inserting the new point before \p before_this results in the point being in between the two
* \return An iterator to the newly inserted point, or an existing point if \p coincided with either end point of the line
*/
ListPolyIt addNewPolyPoint(const Point point, const ListPolyIt line_start, const ListPolyIt line_end, const ListPolyIt before_this);
/*!
* Add a link between \p from and \p to to PolygonProximityLinker::overlap_point_links and add the appropriate mappings to PolygonProximityLinker::point_to_link
*
* \param from The one point of the link
* \param to The other point of the link
* \param dist The distance between the two points
* \param type The type of the link being introduced
* \return Whether the point has been added
*/
bool addProximityLink(ListPolyIt from, ListPolyIt to, int64_t dist, const ProximityPointLinkType type);
/*!
* Add a link for the corner at \p corner_point to PolygonProximityLinker::overlap_point_links and add the appropriate mappings to PolygonProximityLinker::point_to_link
*
* This is done by adding a link between the point and itself.
*
* \param corner_point The one point of the link
* \param type The type of the link being introduced
* \return Whether the point has been added
*/
bool addCornerLink(ListPolyIt corner_point, const ProximityPointLinkType type);
/*!
* Add links for the ending points of proximity regions, supporting the residual triangles.
*/
void addProximityEndings();
/*!
* Add a link for the ending point of a given proximity region, if it is an ending.
*
* \param link_pair The link which might be an ending
* \param a_next The next point from ListPolyIt::a of \p link
* \param b_next The next point from ListPolyIt::b of \p link (in the opposite direction of \p a_next)
* \param a_before_middle Where to insert a new point for a if this is indeed en ending
* \param b_before_middle Where to insert a new point for b if this is indeed en ending
* \param[out] result Where to store a link if a new one has been generated
*/
void addProximityEnding(const ProximityPointLink& link, const ListPolyIt& a_next, const ListPolyIt& b_next, const ListPolyIt& a_before_middle, const ListPolyIt& b_before_middle, ProximityPointLinks& result);
/*!
* Compute the distance between the points of the last link and the points introduced to account for the proximity endings.
*/
int64_t proximityEndingDistance(Point& a1, Point& a2, Point& b1, Point& b2, int a1b1_dist);
/*!
* Add proximity links for sharp corners, so that the proximity of two consecutive line segments is compensated for.
*
* Currently UNIMPLEMENTED.
*/
void addSharpCorners();
/*!
* Map a point to a link in PolygonProximityLinker::point_to_link
*
* \param p The key
* \param it The value
*/
void addToPoint2LinkMap(Point p, ProximityPointLinks::iterator it);
public:
void proximity2HTML(const char* filename) const; //!< debug
/*!
* Computes the neccesary priliminaries in order to efficiently compute the flow when generatign gcode paths.
* \param polygons The wall polygons for which to compute the overlaps
*/
PolygonProximityLinker(Polygons& polygons, int proximity_distance);
/*!
* Check whether a point has any links
* \param from the point for which to check whether it has any links
* \return Whether a link has been created between the point and another point
*/
bool isLinked(Point from);
/*!
* Get all links connected to a given point.
*
* The returned pair is an iterator range;
* The first is the starting iterator (inclusive)
* and the second is the end iterator (exclusive).
*
* Note that the returned iterators point to a pair,
* for which the second is the actual link.
* The first is \p from
*
* \param from The point to get all connected links for
* \return a pair containing two iterators
*/
std::pair<PolygonProximityLinker::Point2Link::iterator, PolygonProximityLinker::Point2Link::iterator> getLinks(Point from);
/*!
* Check whether two points are linked
* \param a an iterator to the first point (in \ref PolygonProximityLinker::list_polygons)
* \param b an iterator to the second point (in \ref PolygonProximityLinker::list_polygons)
* \return Whether a link has been created between the two points
*/
bool isLinked(ListPolyIt a, ListPolyIt b);
/*!
* Get the link between two points if they are linked already
* \param a an iterator to the first point (in \ref PolygonProximityLinker::list_polygons)
* \param b an iterator to the second point (in \ref PolygonProximityLinker::list_polygons)
* \return The link between the two points, or nullptr
*/
const ProximityPointLink* getLink(ListPolyIt a, ListPolyIt b);
};
}//namespace cura
#endif//UTILS_POLYGON_PROXIMITY_LINKER_H
+126
Ver Arquivo
@@ -0,0 +1,126 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_POLYGONS_POINT_INDEX_H
#define UTILS_POLYGONS_POINT_INDEX_H
#include <vector>
#include "intpoint.h"
#include "polygon.h"
namespace cura
{
/*!
* A class for iterating over the points in one of the polygons in a \ref Polygons object
*/
class PolygonsPointIndex
{
public:
/*!
* The polygons into which this index is indexing
*/
const Polygons* polygons; // (pointer to const polygons)
unsigned int poly_idx; //!< The index of the polygon in \ref PolygonsPointIndex::polygons
unsigned int point_idx; //!< The index of the point in the polygon in \ref PolygonsPointIndex::polygons
PolygonsPointIndex()
: polygons(nullptr)
, poly_idx(0)
, point_idx(0)
{
}
PolygonsPointIndex(const Polygons* polygons, unsigned int poly_idx, unsigned int point_idx)
: polygons(polygons)
, poly_idx(poly_idx)
, point_idx(point_idx)
{
}
Point p() const
{
if (!polygons)
{
return Point(0, 0);
}
return (*polygons)[poly_idx][point_idx];
}
/*!
* Get the polygon to which this PolygonsPointIndex refers
*/
const PolygonRef getPolygon() const
{
return (*polygons)[poly_idx];
}
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
* \param other The PolygonsPointIndex to test for equality
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
*/
bool operator==(const PolygonsPointIndex& other) const
{
return polygons == other.polygons && poly_idx == other.poly_idx && point_idx == other.point_idx;
}
bool operator!=(const PolygonsPointIndex& other) const
{
return !(*this == other);
}
PolygonsPointIndex& operator=(const PolygonsPointIndex& other)
{
polygons = other.polygons;
poly_idx = other.poly_idx;
point_idx = other.point_idx;
return *this;
}
//! move the iterator forward (and wrap around at the end)
PolygonsPointIndex& operator++()
{
point_idx = (point_idx + 1) % (*polygons)[poly_idx].size();
return *this;
}
//! move the iterator backward (and wrap around at the beginning)
PolygonsPointIndex& operator--()
{
if (point_idx == 0)
{
point_idx = (*polygons)[poly_idx].size();
}
point_idx--;
return *this;
}
//! move the iterator forward (and wrap around at the end)
PolygonsPointIndex next() const
{
PolygonsPointIndex ret(*this);
++ret;
return ret;
}
//! move the iterator backward (and wrap around at the beginning)
PolygonsPointIndex prev() const
{
PolygonsPointIndex ret(*this);
--ret;
return ret;
}
};
}//namespace cura
namespace std
{
/*!
* Hash function for \ref PolygonsPointIndex
*/
template <>
struct hash<cura::PolygonsPointIndex>
{
size_t operator()(const cura::PolygonsPointIndex& lpi) const
{
return std::hash<cura::Point>()(lpi.p());
}
};
}//namespace std
#endif//UTILS_POLYGONS_POINT_INDEX_H
+20
Ver Arquivo
@@ -0,0 +1,20 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ProximityPointLink.h"
namespace cura
{
ProximityPointLink::ProximityPointLink(const ListPolyIt a, const ListPolyIt b, int dist, const ProximityPointLinkType type)
: a(a)
, b(b)
, dist(dist)
, type(type)
{
}
bool ProximityPointLink::operator==(const ProximityPointLink& other) const
{
return (a == other.a && b == other.b) || (a == other.b && b == other.a);
}
}//namespace cura
+63
Ver Arquivo
@@ -0,0 +1,63 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef PROXIMITY_POINT_LINK_H
#define PROXIMITY_POINT_LINK_H
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <utility> // pair
#include <functional> // hash function object
#include "intpoint.h"
#include "polygon.h"
#include "linearAlg2D.h"
#include "optional.h"
#include "ListPolyIt.h"
namespace cura
{
/*!
* Type of ProximityPointLink signifying why/how it was created
*/
enum class ProximityPointLinkType
{
NORMAL, // Point is close to line segment or to another point
ENDING, // link where two line segments diverge and have the maximum proximity, i.e. where the overlap will be zero
ENDING_CORNER, // when an overlap area ends in a point
SHARP_CORNER // The corner in the polygon is so sharp that it will overlap with itself
};
/*!
* A class recording the amount of overlap implicitly by recording the distance between two points on two different polygons or one and the same polygon.
* The order of the two points doesn't matter.
*/
struct ProximityPointLink
{
const ListPolyIt a; //!< the one point (invalidated after list_polygons have been cleared!)
const ListPolyIt b; //!< the other point (invalidated after list_polygons have been cleared!)
const int dist; //!< The distance between the two points
const ProximityPointLinkType type; //!< The type of link; why/how it was created
ProximityPointLink(const ListPolyIt a, const ListPolyIt b, int dist, const ProximityPointLinkType type);
bool operator==(const ProximityPointLink& other) const;
};
}//namespace cura
namespace std
{
template <>
struct hash<cura::ProximityPointLink>
{
size_t operator()(const cura::ProximityPointLink & pp) const
{ // has to be symmetric wrt a and b!
return std::hash<cura::Point>()(pp.a.p()) + std::hash<cura::Point>()(pp.b.p());
}
};
}//namespace std
#endif//PROXIMITY_POINT_LINK_H
+10 -3
Ver Arquivo
@@ -47,6 +47,7 @@ private:
const AABB aabb; // the boundary box to display
const Point aabb_size;
const Point border;
const Point canvas_size;
const double scale;
public:
@@ -54,6 +55,7 @@ public:
: aabb(aabb)
, aabb_size(aabb.max - aabb.min)
, border(200,100)
, canvas_size(canvas_size)
, scale(std::min(double(canvas_size.X - border.X * 2) / aabb_size.X, double(canvas_size.Y - border.Y * 2) / aabb_size.Y))
{
out = fopen(filename, "w");
@@ -77,7 +79,12 @@ public:
*/
Point transform(const Point& p)
{
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + border;
return Point((p.X-aabb.min.X)*scale, canvas_size.X - border.X - (p.Y-aabb.min.Y)*scale) + border;
}
void writeComment(std::string comment)
{
fprintf(out, "<!-- %s -->\n", comment.c_str());
}
void writeAreas(const Polygons& polygons, Color color = Color::GRAY, Color outline_color = Color::BLACK)
@@ -167,11 +174,11 @@ public:
fprintf(out,"\" />\n"); //Write the end of the tag.
}
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK, int stroke_width = 1)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:%i\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str(), stroke_width);
}
/*!
+410
Ver Arquivo
@@ -0,0 +1,410 @@
/** Copyright (C) 2016 Scott Lenser - Released under terms of the AGPLv3 License */
#ifndef UTILS_SPARSE_GRID_H
#define UTILS_SPARSE_GRID_H
#include "intpoint.h"
#include <cassert>
#include <unordered_map>
#include <vector>
#include <functional>
namespace cura {
/*! \brief Sparse grid which can locate spatially nearby elements efficiently.
*
* \note This is an abstract template class which doesn't have any functions to insert elements.
* \see SparsePointGrid
*
* \tparam ElemT The element type to store.
*/
template<class ElemT>
class SparseGrid
{
public:
using Elem = ElemT;
/*! \brief Constructs a sparse grid with the specified cell size.
*
* \param[in] cell_size The size to use for a cell (square) in the grid.
* Typical values would be around 0.5-2x of expected query radius.
* \param[in] elem_reserve Number of elements to research space for.
* \param[in] max_load_factor Maximum average load factor before rehashing.
*/
SparseGrid(coord_t cell_size, size_t elem_reserve=0U, float max_load_factor=1.0f);
/*! \brief Returns all data within radius of query_pt.
*
* Finds all elements with location within radius of \p query_pt. May
* return additional elements that are beyond radius.
*
* Average running time is a*(1 + 2 * radius / cell_size)**2 +
* b*cnt where a and b are proportionality constance and cnt is
* the number of returned items. The search will return items in
* an area of (2*radius + cell_size)**2 on average. The max range
* of an item from the query_point is radius + cell_size.
*
* \param[in] query_pt The point to search around.
* \param[in] radius The search radius.
* \return Vector of elements found
*/
std::vector<Elem> getNearby(const Point &query_pt, coord_t radius) const;
static const std::function<bool(const Elem&)> no_precondition;
/*!
* Find the nearest element to a given \p query_pt within \p radius.
*
* \param[in] query_pt The point for which to find the nearest object.
* \param[in] radius The search radius.
* \param[out] elem_nearest the nearest element. Only valid if function returns true.
* \param[in] precondition A precondition which must return true for an element
* to be considered for output
* \return True if and only if an object has been found within the radius.
*/
bool getNearest(const Point &query_pt, coord_t radius, Elem &elem_nearest,
const std::function<bool(const Elem& elem)> precondition = no_precondition) const;
/*! \brief Process elements from cells that might contain sought after points.
*
* Processes elements from cell that might have elements within \p
* radius of \p query_pt. Processes all elements that are within
* radius of query_pt. May process elements that are up to radius +
* cell_size from query_pt.
*
* \param[in] query_pt The point to search around.
* \param[in] radius The search radius.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell. Processing stops if function returns false.
*/
void processNearby(const Point &query_pt, coord_t radius,
const std::function<bool (const ElemT&)>& process_func) const;
/*! \brief Process elements from cells that might contain sought after points along a line.
*
* Processes elements from cells that cross the line \p query_line.
* May process elements that are up to sqrt(2) * cell_size from \p query_line.
*
* \param[in] query_line The line along which to check each cell
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cells. Processing stops if function returns false.
*/
void processLine(const std::pair<Point, Point> query_line,
const std::function<bool (const Elem&)>& process_elem_func) const;
coord_t getCellSize() const;
protected:
using GridPoint = Point;
using grid_coord_t = coord_t;
using GridMap = std::unordered_multimap<GridPoint, Elem>;
/*! \brief Process elements from the cell indicated by \p grid_pt.
*
* \param[in] grid_pt The grid coordinates of the cell.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell. Processing stops if function returns false.
* \return Whether we need to continue processing a next cell.
*/
bool processFromCell(const GridPoint &grid_pt,
const std::function<bool (const Elem&)>& process_func) const;
/*! \brief Process cells along a line indicated by \p line.
*
* \param[in] line The line along which to process cells
* \param[in] process_func Processes each cell. process_func(elem) is
* called for each cell. Processing stops if function returns false.
*/
void processLineCells(const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func);
/*! \brief Process cells along a line indicated by \p line.
*
* \param[in] line The line along which to process cells
* \param[in] process_func Processes each cell. process_func(elem) is
* called for each cell. Processing stops if function returns false.
*/
void processLineCells(const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func) const;
/*! \brief Compute the grid coordinates of a point.
*
* \param[in] point The actual location.
* \return The grid coordinates that correspond to \p point.
*/
GridPoint toGridPoint(const Point& point) const;
/*! \brief Compute the grid coordinate of a print space coordinate.
*
* \param[in] coord The actual location.
* \return The grid coordinate that corresponds to \p coord.
*/
grid_coord_t toGridCoord(const coord_t& coord) const;
/*! \brief Compute the lowest point in a grid cell.
* The lowest point is the point in the grid cell closest to the origin.
*
* \param[in] location The grid location.
* \return The print space coordinates that correspond to \p location.
*/
Point toLowerCorner(const GridPoint& location) const;
/*! \brief Compute the lowest coord in a grid cell.
* The lowest point is the point in the grid cell closest to the origin.
*
* \param[in] grid_coord The grid coordinate.
* \return The print space coordinate that corresponds to \p grid_coord.
*/
coord_t toLowerCoord(const grid_coord_t& grid_coord) const;
/*! \brief Map from grid locations (GridPoint) to elements (Elem). */
GridMap m_grid;
/*! \brief The cell (square) size. */
coord_t m_cell_size;
grid_coord_t nonzero_sign(const grid_coord_t z) const;
};
#define SGI_TEMPLATE template<class ElemT>
#define SGI_THIS SparseGrid<ElemT>
SGI_TEMPLATE
SGI_THIS::SparseGrid(coord_t cell_size, size_t elem_reserve, float max_load_factor)
{
assert(cell_size > 0U);
m_cell_size = cell_size;
// Must be before the reserve call.
m_grid.max_load_factor(max_load_factor);
if (elem_reserve != 0U) {
m_grid.reserve(elem_reserve);
}
}
SGI_TEMPLATE
typename SGI_THIS::GridPoint SGI_THIS::toGridPoint(const Point &point) const
{
return Point(toGridCoord(point.X), toGridCoord(point.Y));
}
SGI_TEMPLATE
typename SGI_THIS::grid_coord_t SGI_THIS::toGridCoord(const coord_t& coord) const
{
// This mapping via truncation results in the cells with
// GridPoint.x==0 being twice as large and similarly for
// GridPoint.y==0. This doesn't cause any incorrect behavior,
// just changes the running time slightly. The change in running
// time from this is probably not worth doing a proper floor
// operation.
return coord / m_cell_size;
}
SGI_TEMPLATE
typename cura::Point SGI_THIS::toLowerCorner(const GridPoint& location) const
{
return cura::Point(toLowerCoord(location.X), toLowerCoord(location.Y));
}
SGI_TEMPLATE
typename cura::coord_t SGI_THIS::toLowerCoord(const grid_coord_t& grid_coord) const
{
// This mapping via truncation results in the cells with
// GridPoint.x==0 being twice as large and similarly for
// GridPoint.y==0. This doesn't cause any incorrect behavior,
// just changes the running time slightly. The change in running
// time from this is probably not worth doing a proper floor
// operation.
return grid_coord * m_cell_size;
}
SGI_TEMPLATE
bool SGI_THIS::processFromCell(
const GridPoint &grid_pt,
const std::function<bool (const Elem&)>& process_func) const
{
auto grid_range = m_grid.equal_range(grid_pt);
for (auto iter = grid_range.first; iter != grid_range.second; ++iter)
{
if (!process_func(iter->second))
{
return false;
}
}
return true;
}
SGI_TEMPLATE
void SGI_THIS::processLineCells(
const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func)
{
static_cast<const SGI_THIS*>(this)->processLineCells(line, process_cell_func);
}
SGI_TEMPLATE
void SGI_THIS::processLineCells(
const std::pair<Point, Point> line,
const std::function<bool (GridPoint)>& process_cell_func) const
{
Point start = line.first;
Point end = line.second;
if (end.X < start.X)
{ // make sure X increases between start and end
std::swap(start, end);
}
const GridPoint start_cell = toGridPoint(start);
const GridPoint end_cell = toGridPoint(end);
const coord_t y_diff = end.Y - start.Y;
const grid_coord_t y_dir = nonzero_sign(y_diff);
grid_coord_t x_cell_start = start_cell.X;
for (grid_coord_t cell_y = start_cell.Y; cell_y * y_dir <= end_cell.Y * y_dir; cell_y += y_dir)
{ // for all Y from start to end
// nearest y coordinate of the cells in the next row
coord_t nearest_next_y = toLowerCoord(cell_y + ((nonzero_sign(cell_y) == y_dir || cell_y == 0) ? y_dir : coord_t(0)));
grid_coord_t x_cell_end; // the X coord of the last cell to include from this row
if (y_diff == 0)
{
x_cell_end = end_cell.X;
}
else
{
coord_t area = (end.X - start.X) * (nearest_next_y - start.Y);
// corresponding_x: the x coordinate corresponding to nearest_next_y
coord_t corresponding_x = start.X + area / y_diff;
x_cell_end = toGridCoord(corresponding_x + ((corresponding_x < 0) && ((area % y_diff) != 0)));
if (x_cell_end < start_cell.X)
{ // process at least one cell!
x_cell_end = x_cell_start;
}
}
for (grid_coord_t cell_x = x_cell_start; cell_x <= x_cell_end; ++cell_x)
{
GridPoint grid_loc(cell_x, cell_y);
bool continue_ = process_cell_func(grid_loc);
if (!continue_)
{
return;
}
if (grid_loc == end_cell)
{
return;
}
}
// TODO: this causes at least a one cell overlap for each row, which
// includes extra cells when crossing precisely on the corners
// where positive slope where x > 0 and negative slope where x < 0
x_cell_start = x_cell_end;
}
assert(false && "We should have returned already before here!");
}
SGI_TEMPLATE
typename SGI_THIS::grid_coord_t SGI_THIS::nonzero_sign(const grid_coord_t z) const
{
return (z >= 0) - (z < 0);
}
SGI_TEMPLATE
void SGI_THIS::processNearby(const Point &query_pt, coord_t radius,
const std::function<bool (const Elem&)>& process_func) const
{
Point min_loc(query_pt.X - radius, query_pt.Y - radius);
Point max_loc(query_pt.X + radius, query_pt.Y + radius);
GridPoint min_grid = toGridPoint(min_loc);
GridPoint max_grid = toGridPoint(max_loc);
for (coord_t grid_y = min_grid.Y; grid_y <= max_grid.Y; ++grid_y)
{
for (coord_t grid_x = min_grid.X; grid_x <= max_grid.X; ++grid_x)
{
GridPoint grid_pt(grid_x,grid_y);
bool continue_ = processFromCell(grid_pt, process_func);
if (!continue_)
{
return;
}
}
}
}
SGI_TEMPLATE
void SGI_THIS::processLine(const std::pair<Point, Point> query_line,
const std::function<bool (const Elem&)>& process_elem_func) const
{
const std::function<bool (const GridPoint&)> process_cell_func = [&process_elem_func, this](GridPoint grid_loc)
{
return processFromCell(grid_loc, process_elem_func);
};
processLineCells(query_line, process_cell_func);
}
SGI_TEMPLATE
std::vector<typename SGI_THIS::Elem>
SGI_THIS::getNearby(const Point &query_pt, coord_t radius) const
{
std::vector<Elem> ret;
const std::function<bool (const Elem&)> process_func = [&ret](const Elem &elem)
{
ret.push_back(elem);
return true;
};
processNearby(query_pt, radius, process_func);
return ret;
}
SGI_TEMPLATE
const std::function<bool(const typename SGI_THIS::Elem &)>
SGI_THIS::no_precondition =
[](const typename SGI_THIS::Elem &)
{
return true;
};
SGI_TEMPLATE
bool SGI_THIS::getNearest(
const Point &query_pt, coord_t radius, Elem &elem_nearest,
const std::function<bool(const Elem& elem)> precondition) const
{
bool found = false;
int64_t best_dist2 = static_cast<int64_t>(radius) * radius;
const std::function<bool (const Elem&)> process_func =
[&query_pt, &elem_nearest, &found, &best_dist2, &precondition](const Elem &elem)
{
if (!precondition(elem))
{
return true;
}
int64_t dist2 = vSize2(elem.point - query_pt);
if (dist2 < best_dist2)
{
found = true;
elem_nearest = elem;
best_dist2 = dist2;
}
return true;
};
processNearby(query_pt, radius, process_func);
return found;
}
SGI_TEMPLATE
coord_t SGI_THIS::getCellSize() const
{
return m_cell_size;
}
#undef SGI_TEMPLATE
#undef SGI_THIS
} // namespace cura
#endif // UTILS_SPARSE_GRID_H
+188
Ver Arquivo
@@ -0,0 +1,188 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef UTILS_SPARSE_LINE_GRID_H
#define UTILS_SPARSE_LINE_GRID_H
#include <cassert>
#include <unordered_map>
#include <vector>
#include <functional>
#include "intpoint.h"
#include "SparseGrid.h"
#include "SVG.h" // debug
namespace cura {
/*! \brief Sparse grid which can locate spatially nearby elements efficiently.
*
* \tparam ElemT The element type to store.
* \tparam Locator The functor to get the start and end locations from ElemT.
* must have: std::pair<Point, Point> operator()(const ElemT &elem) const
* which returns the location associated with val.
*/
template<class ElemT, class Locator>
class SparseLineGrid : public SparseGrid<ElemT>
{
public:
using Elem = ElemT;
/*! \brief Constructs a sparse grid with the specified cell size.
*
* \param[in] cell_size The size to use for a cell (square) in the grid.
* Typical values would be around 0.5-2x of expected query radius.
* \param[in] elem_reserve Number of elements to research space for.
* \param[in] max_load_factor Maximum average load factor before rehashing.
*/
SparseLineGrid(coord_t cell_size, size_t elem_reserve = 0U, float max_load_factor = 1.0f);
/*! \brief Inserts elem into the sparse grid.
*
* \param[in] elem The element to be inserted.
*/
void insert(const Elem &elem);
void debugHTML(std::string filename);
static void debugTest();
protected:
using GridPoint = typename SparseGrid<ElemT>::GridPoint;
using grid_coord_t = typename SparseGrid<ElemT>::grid_coord_t;
/*! \brief Accessor for getting locations from elements. */
Locator m_locator;
};
#define SGI_TEMPLATE template<class ElemT, class Locator>
#define SGI_THIS SparseLineGrid<ElemT, Locator>
SGI_TEMPLATE
SGI_THIS::SparseLineGrid(coord_t cell_size, size_t elem_reserve, float max_load_factor)
: SparseGrid<ElemT>(cell_size, elem_reserve, max_load_factor)
{
}
SGI_TEMPLATE
void SGI_THIS::insert(const Elem &elem)
{
const std::pair<Point, Point> line = m_locator(elem);
using GridMap = std::unordered_multimap<GridPoint, Elem>;
// below is a workaround for the fact that lambda functions cannot access private or protected members
// first we define a lambda which works on any GridMap and then we bind it to the actual protected GridMap of the parent class
std::function<bool (GridMap*, const GridPoint)> process_cell_func_ = [&elem, this](GridMap* m_grid, const GridPoint grid_loc)
{
m_grid->emplace(grid_loc, elem);
return true;
};
using namespace std::placeholders; // for _1, _2, _3...
GridMap* m_grid = &(this->m_grid);
std::function<bool (const GridPoint)> process_cell_func(std::bind(process_cell_func_, m_grid, _1));
SparseGrid<ElemT>::processLineCells(line, process_cell_func);
}
SGI_TEMPLATE
void SGI_THIS::debugHTML(std::string filename)
{
AABB aabb;
for (std::pair<GridPoint, ElemT> cell: SparseGrid<ElemT>::m_grid)
{
aabb.include(SparseGrid<ElemT>::toLowerCorner(cell.first));
aabb.include(SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), SparseGrid<ElemT>::nonzero_sign(cell.first.Y))));
}
SVG svg(filename.c_str(), aabb);
for (std::pair<GridPoint, ElemT> cell: SparseGrid<ElemT>::m_grid)
{
// doesn't draw cells at x = 0 or y = 0 correctly (should be double size)
Point lb = SparseGrid<ElemT>::toLowerCorner(cell.first);
Point lt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(0, SparseGrid<ElemT>::nonzero_sign(cell.first.Y)));
Point rt = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), SparseGrid<ElemT>::nonzero_sign(cell.first.Y)));
Point rb = SparseGrid<ElemT>::toLowerCorner(cell.first + GridPoint(SparseGrid<ElemT>::nonzero_sign(cell.first.X), 0));
if (lb.X == 0)
{
lb.X = -SparseGrid<ElemT>::m_cell_size;
lt.X = -SparseGrid<ElemT>::m_cell_size;
}
if (lb.Y == 0)
{
lb.Y = -SparseGrid<ElemT>::m_cell_size;
rb.Y = -SparseGrid<ElemT>::m_cell_size;
}
// svg.writePoint(lb, true, 1);
svg.writeLine(lb, lt, SVG::Color::GRAY);
svg.writeLine(lt, rt, SVG::Color::GRAY);
svg.writeLine(rt, rb, SVG::Color::GRAY);
svg.writeLine(rb, lb, SVG::Color::GRAY);
std::pair<Point, Point> line = m_locator(cell.second);
svg.writePoint(line.first, true);
svg.writePoint(line.second, true);
svg.writeLine(line.first, line.second, SVG::Color::BLACK);
}
}
SGI_TEMPLATE
void SGI_THIS::debugTest()
{
struct PairLocator
{
std::pair<Point, Point> operator()(const std::pair<Point, Point>& val) const
{
return val;
}
};
SparseLineGrid<std::pair<Point, Point>, PairLocator> line_grid(10);
// straight lines
line_grid.insert(std::make_pair<Point, Point>(Point(50, 0), Point(50, 70)));
line_grid.insert(std::make_pair<Point, Point>(Point(0, 90), Point(50, 90)));
line_grid.insert(std::make_pair<Point, Point>(Point(253, 103), Point(253, 173)));
line_grid.insert(std::make_pair<Point, Point>(Point(203, 193), Point(253, 193)));
line_grid.insert(std::make_pair<Point, Point>(Point(-50, 0), Point(-50, -70)));
line_grid.insert(std::make_pair<Point, Point>(Point(0, -90), Point(-50, -90)));
line_grid.insert(std::make_pair<Point, Point>(Point(-253, -103), Point(-253, -173)));
line_grid.insert(std::make_pair<Point, Point>(Point(-203, -193), Point(-253, -193)));
// diagonal lines
line_grid.insert(std::make_pair<Point, Point>(Point(113, 133), Point(166, 125)));
line_grid.insert(std::make_pair<Point, Point>(Point(13, 73), Point(26, 25)));
line_grid.insert(std::make_pair<Point, Point>(Point(166, 33), Point(113, 25)));
line_grid.insert(std::make_pair<Point, Point>(Point(26, 173), Point(13, 125)));
line_grid.insert(std::make_pair<Point, Point>(Point(-24, -18), Point(-19, -64)));
line_grid.insert(std::make_pair<Point, Point>(Point(-113, -133), Point(-166, -125)));
line_grid.insert(std::make_pair<Point, Point>(Point(-166, -33), Point(-113, -25)));
line_grid.insert(std::make_pair<Point, Point>(Point(-26, -173), Point(-13, -125)));
// diagonal lines exactly crossing cell corners
line_grid.insert(std::make_pair<Point, Point>(Point(160, 190), Point(220, 170)));
line_grid.insert(std::make_pair<Point, Point>(Point(60, 130), Point(80, 70)));
line_grid.insert(std::make_pair<Point, Point>(Point(220, 90), Point(160, 70)));
line_grid.insert(std::make_pair<Point, Point>(Point(80, 220), Point(60, 160)));
line_grid.insert(std::make_pair<Point, Point>(Point(-160, -190), Point(-220, -170)));
line_grid.insert(std::make_pair<Point, Point>(Point(-60, -130), Point(-80, -70)));
line_grid.insert(std::make_pair<Point, Point>(Point(-220, -90), Point(-160, -70)));
line_grid.insert(std::make_pair<Point, Point>(Point(-80, -220), Point(-60, -160)));
// single cell
line_grid.insert(std::make_pair<Point, Point>(Point(203, 213), Point(203, 213)));
line_grid.insert(std::make_pair<Point, Point>(Point(223, 213), Point(223, 215)));
line_grid.insert(std::make_pair<Point, Point>(Point(243, 213), Point(245, 213)));
line_grid.insert(std::make_pair<Point, Point>(Point(263, 213), Point(265, 215)));
line_grid.insert(std::make_pair<Point, Point>(Point(283, 215), Point(285, 213)));
line_grid.insert(std::make_pair<Point, Point>(Point(-203, -213), Point(-203, -213)));
// around origin
line_grid.insert(std::make_pair<Point, Point>(Point(20, -20), Point(-20, 20)));
line_grid.debugHTML("line_grid.html");
}
#undef SGI_TEMPLATE
#undef SGI_THIS
} // namespace cura
#endif // UTILS_SPARSE_LINE_GRID_H

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