Comparar commits

..

207 Commits

Autor SHA1 Mensagem Data
Ghostkeeper 6c931e2935 Fix modulo by size of lines
All lines have size 2, which is why it was never a problem, but in theory if the polygon has a different line size it will now work correctly.

Contributes to issue CURA-329.
2016-02-03 12:55:19 +01:00
Ghostkeeper 295a5a9fc4 Factor out points of insert distance calculations
These points are factored out to intermediary variables to reduce width of the code at the cost of height.

Contributes to issue CURA-329.
2016-02-03 12:55:14 +01:00
Tim Kuipers cc8dadf571 fix: variable name shadowing in pathorderOptimizer (CURA-329) 2016-02-03 12:55:09 +01:00
Ghostkeeper 118e71c7af Cache lines for simpler expressions
Some of these expressions were deemed too complex to understand. In an effort to make them simpler, more sloc were added.

Contributes to issue CURA-329.
2016-02-03 12:54:55 +01:00
Ghostkeeper b5d29a66b2 Fix modular arithmetic with negative input
Stupid ISO14882. Modular arithmetic would define that -2 % 7 results in 5, not -2. But C++ implements modulus as keeping the same sign as the left-hand parameter. Thus we have to make sure the left parameter is always positive.

Contributes to issue CURA-329.
2016-02-03 12:54:52 +01:00
Ghostkeeper e937e98b27 Extract trying to insert a line in a cluster to function
I don't wholly agree with this change, since it basically has to transfer the entire context to a separate function of which the functionality is not entirely separate. But at least it makes the cluster() function a bit shorter.

Contributes to issue CURA-329.
2016-02-03 12:54:49 +01:00
Ghostkeeper c3705604cd Extract trying to insert a line in a cluster to function
I don't wholly agree with this change, since it basically has to transfer the entire context to a separate function of which the functionality is not entirely separate. But at least it makes the cluster() function a bit shorter.

Contributes to issue CURA-329.
2016-02-03 12:54:46 +01:00
Ghostkeeper e1993a8ccd Remove TODOs: No action required.
It has been decided and these outlined functions will stay this way.

Contributes to issue CURA-329.
2016-02-03 12:54:43 +01:00
Ghostkeeper b819f696eb Revert Add function to check if two lines are near to each other
Was kind of a superfluous function.

Contributes to issue CURA-329.
2016-02-03 12:54:39 +01:00
Ghostkeeper b1b37cf19b Rename orientation to orientation_index
If the type is not visible directly in the code, this clarifies that it is supposed to be an index in an array.

Contributes to issue CURA-329.
2016-02-03 12:54:36 +01:00
Ghostkeeper 9808be9dfa Cache first and last line indices in get_orientations
No need to fetch them from the vector every time. This is faster if it wouldn't be optimised out by the compiler, and results in more readable code.

Contributes to issue CURA-329.
2016-02-03 12:54:34 +01:00
Ghostkeeper 06e8848ee2 Rename shuffle to shuffled
The vector that holds the shuffled waypoints holds the shuffled stuff, so call it shuffled.

Contributes to issue CURA-329.
2016-02-03 12:54:31 +01:00
Ghostkeeper c43d5ccbf3 Fix codestyle violations
These codestyle violations shouldn't be fixed here since it ruins the git history, but it seems this change isn't getting through without formatting. Therefore I fix them now. Fixed using Netbeans' auto-formatter (and checked manually: it rightly interpreted template brackets as binary operators but we don't see them that way).

Contributes to issue CURA-329.
2016-02-03 12:54:28 +01:00
Ghostkeeper e3e4ccdba8 Add function to check if two lines are near to each other
If they are, they should be placed in the same cluster. Currently this check is completely superfluous since the same check needs to be used to determine in what direction a line should be placed.

Contributes to issue CURA-329.
2016-02-03 12:54:25 +01:00
Ghostkeeper e0eacf0675 Remove unused header of checkIfLineIsBest
The implementation of this function was removed. The header should also have been removed.

Contributes to issue CURA-329.
2016-02-03 12:54:22 +01:00
Ghostkeeper ad1489e0ee Outline the procedures to insert waypoints
In the optimisation step, outline the procedures to insert elements in the resulting lists to their own functions. I really don't agree with this change, since I think it reduces readability as well as efficiency, but it was desired by code review to reduce the deepest indentation of the code, which is a good thing.

Contributes to issue CURA-329.
2016-02-03 12:54:19 +01:00
Ghostkeeper 3efe717154 Codestyle
The line in this loop was updated by my IDE's refactor, so I should've updated it to the code style.

Contributes to issue CURA-329.
2016-02-03 12:54:16 +01:00
Ghostkeeper 17bd906318 Rename LineOrderOptimizer's polygons to lines
They should only be lines. There is no check for it anywhere, though.

Contributes to issue CURA-329.
2016-02-03 12:54:14 +01:00
Ghostkeeper b8cdf0da24 Rename LineOrderOptimizer's polygons to lines
They should only be lines. There is no check for it anywhere, though.

Contributes to issue CURA-329.
2016-02-03 12:54:12 +01:00
Ghostkeeper 801641823c Update documentation
See that's the problem of having lots of documentation: The documentation may be out of date.

Contributes to issue CURA-329.
2016-02-03 12:54:09 +01:00
Ghostkeeper ac25e429b8 Update documentation of findPath.
I removed some old functions earlier but kept the wrong documentation and the documentation wasn't updated for the orientation change either.

Contributes to issue CURA-329.
2016-02-03 12:54:07 +01:00
Ghostkeeper 54e4b2d44e Create typedef Cluster for line clusters
A cluster is a vector of indices to the polygons vector.

Contributes to issue CURA-329.
2016-02-03 12:53:47 +01:00
Ghostkeeper 573699fd07 Extend the cluster() documentation
It describes how lines are precisely clustered and why.

Contributes to issue CURA-329.
2016-02-03 12:53:43 +01:00
Ghostkeeper 304a84a25c Introduce some newlines to improve readability
It should better segment the code into pieces.

Contributes to issue CURA-329.
2016-02-03 12:53:41 +01:00
Ghostkeeper 0e6412289c Update documentation of initial polyStart
Contributes to issue CURA-329.
2016-02-03 12:53:39 +01:00
Ghostkeeper e5901df8e1 Remove unused checkIfLineIsBest function
Penalising sharp corners is no longer applicable, since within clusters both endpoints of adjacent lines must be near each other, so the corner will always be approximately 180 degrees. Between clusters, combing deforms most of the travel moves which makes the corner angle calculation inaccurate.

Contributes to issue CURA-329.
2016-02-03 12:53:37 +01:00
Ghostkeeper e2f96fb1ad Document the usage of indices to polygons in LineOrderOptimizer
I can't document the real reason why indices are used instead of references. That is located in a different part of the code base that is much order.

Contributes to issue CURA-329.
2016-02-03 12:53:34 +01:00
Ghostkeeper 190908ab27 Store waypoint start/end points as pair
The pairs are termed "orientations". This actually makes the code a lot more self-documenting.

Contributes to issue CURA-329.
2016-02-03 12:53:22 +01:00
Ghostkeeper ff775d12f2 Separate declaration of functions from TSP solver declaration
The declaration of the TSP solver with two bulky anonymous functions is unwieldy. The functions are now defined separately and passed to the constructor of TSP.

Contributes to issue CURA-329.
2016-02-03 12:53:19 +01:00
Ghostkeeper a2fd4c0bf3 Document template types
The element template type was a source of confusion with respect to passing by value or by reference. It should be better documented now.

Contributes to issue CURA-329.
2016-02-03 12:53:17 +01:00
Ghostkeeper 404edc5cdc Code readability and documentation of orientation decoding
Where the optimal orientation is used to correctly place the line in order, the code could be made more readable.

Contributes to issue CURA-329.
2016-02-03 12:53:14 +01:00
Ghostkeeper de4edc36bb Fix mirroring when cluster size is 1
In the path order, the mirroring was incorrectly detected when cluster size is 1, since there are only 2 orientations for those clusters instead of 4.

Contributes to issue CURA-329.
2016-02-03 12:53:11 +01:00
Ghostkeeper 1623e58063 Improve clustering heuristic when not aligned
In grid infill, the infill lines are not necessarily aligned. This makes clustering more difficult, but this should help at no additional computational cost. The best distance is now only computed from the travel move that's to be made, while the check for both endpoints to be near remains in effect.

Contributes to issue CURA-329.
2016-02-03 12:53:09 +01:00
Ghostkeeper bb1c00f255 Smaller clustering grid size fallback
The clustering grid size should only be 0 if there is no infill. In those cases, the skin is the only part that needs clustering. For skin, a cluster size of 2mm is more appropriate.

Contributes to issue CURA-329.
2016-02-03 12:53:06 +01:00
Ghostkeeper 773e152454 Vary clustering grid size by infill density
This makes the algorithm run much faster and more reliably if the infill density is low.

Contributes to issue CURA-329.
2016-02-03 12:53:03 +01:00
Ghostkeeper 9c59eff999 Rename ListElement to WaypointListIterator
It technically points to an element, and is not the element itself. It is also used to iterate over the list, so yes it should be called an iterator.

Contributes to CURA-329.
2016-02-03 12:53:01 +01:00
Ghostkeeper b21b7e3115 Remove unused constructor
It used to be used to construct the empty waypoint for the starting_point, but that waypoint is no longer created.

Contributes to CURA-329.
2016-02-03 12:52:57 +01:00
Ghostkeeper 36b09753af Fix even/odd parity check for reversing elements
Because modulo 1 on integers doesn't do anything.

Contributes to CURA-329.
2016-02-03 12:52:55 +01:00
Ghostkeeper 71b466a933 Allow mirroring of infill/skin lines
To allow infill and skin lines to be traversed individually in reverse direction, the TSP solver now accepts an arbitrary number of orientations to insert an element. This also allows us to easily check for the actual start and end points of an element.

Contributes to CURA-329.
2016-02-03 12:52:52 +01:00
Ghostkeeper f0141230b2 Add test for basic bijective result
All points in the unoptimised set must be included in the optimised set and vice-versa.

Contributes to CURA-329.
2016-02-03 12:52:49 +01:00
Ghostkeeper d4631e3f69 Don't make stroke width 4
I must've accidentally included this in a previous commit. It was necessary to debug larger images properly with my image viewer.
2016-02-03 12:52:46 +01:00
Ghostkeeper 64aa1bcd97 Properly reverse list if starting point is at the end
If the optimal point to start is at the end of the TSP path, the TSP path is now properly reversed instead of just reversing the internal order of the elements.

Contributes to issue CURA-329.
2016-02-03 12:52:43 +01:00
Ghostkeeper d34e168562 Fix calculation of path closing distance
This distance was calculated using list.end(), which is a nullptr. This gave uninitialised data, and valgrind wasn't even able to detect it. Luckily, I was. It should be list.back().

Contributes to issue CURA-329.
2016-02-03 12:52:41 +01:00
Ghostkeeper 8f7afd75da Correct documentation
It is now inserting at the end rather than the start when best_insert is equal to result.end(), and inserting results in putting the element before the specified element.

Contributes to issue CURA-329.
2016-02-03 12:52:38 +01:00
Ghostkeeper 0380fd183d Set starting point depending on direction of elements
The direction of the elements is known at this point, so why not use it to optimise further.

Contributes to issue CURA-329.
2016-02-03 12:52:35 +01:00
Ghostkeeper c114904828 Fix checking for the last element inserting starting point
We were using --result.end(), but it turns out that this doesn't work in one statement. We have to create a variable and decrement that.

Contributes to issue CURA-329.
2016-02-03 12:52:32 +01:00
Ghostkeeper 9c08d3dd82 Properly average points
Arithmetic on points isn't defined, though the compiler doesn't complain. We'll have to do it manually.

Contributes to issue CURA-329.
2016-02-03 12:49:54 +01:00
Ghostkeeper f3dbde8e7f Properly iterate over cluster in reverse order
The index was off.

Contributes to issue CURA-329.
2016-02-03 12:49:51 +01:00
Ghostkeeper 7b0f4d204b Add method to write vector of polygons to SVG
This helps with debugging. I just want to be able to draw any geometry to SVG.
2016-02-03 12:49:48 +01:00
Ghostkeeper 79a2dc0a72 Only compute reverse elements if reverse is allowed
Otherwise we're just wasting computer cycles, man.

Contributes to issue CURA-329.
2016-02-03 12:49:45 +01:00
Ghostkeeper c97bcdf005 Order with average points and insert starting point last
This commit has two major changes for which a rewrite was required: The order of the pieces is now determined with point-based TSP rather than line-based. The average between the start and end point is used. This provides better results if the clusters are big, but worse results if the lines intersect (which is not our use case). Then to determine the direction of the elements, it is simply iterated through one by one after all elements are in the sequence.
Also, rather than inserting the starting point at the start, it is now inserted at the end. If it turns out that the starting point should've been inserted somewhere in the middle, that will be corrected after all elements are inserted.

Contributes to issue CURA-329.
2016-02-03 12:49:43 +01:00
Ghostkeeper 03677b8ac7 No longer sort line clusters
The clusters are now constructed with a nearest neighbour-ish search. This guarantees that the lines are already in order. No sorting necessary.

Contributes to issue CURA-329.
2016-02-03 12:49:40 +01:00
Ghostkeeper 9db6352281 Store polyStart during clustering
During clustering, the best orientation of the line is already decided in order to find the closest line. This orientation is immediately stored at that time, instead of guessing it later by the order of the lines at the serialisation. This should make the algorithm more robust at no additional computational cost.

Contributes to issue CURA-329.
2016-02-03 12:49:34 +01:00
Ghostkeeper 3da8607afd Remove old line order optimisation code
This code was kept commented out for a while to have a reference, but I won't need it any longer.

Contributes to issue CURA-329.
2016-02-03 12:49:31 +01:00
Ghostkeeper 4f248088ce Rewrite clustering code
The clustering now uses a nearest neighbour algorithm on the grid to generate the initial clusters to sort.

Contributes to issue CURA-329.
2016-02-03 12:49:28 +01:00
Ghostkeeper d08580f969 Terminate early if no lines to optimise
About 2/3rd of the times there are no lines to optimise for the LineOrderOptimizer. Instead of creating empty cluster vectors, intermediary vectors for reversing, optimising empty vectors with TSP, etc. it now just quits at the start of the optimiser.

Contributes to issue CURA-329.
2016-02-03 12:49:24 +01:00
Ghostkeeper a3652a4fc4 Communicate path order via polyStart properly
The polyStart field should contain an entry for every polygon in the same order as the input polygons, rather than the order of the output polygons.

Contributes to issue CURA-329.
2016-02-03 12:49:22 +01:00
Ghostkeeper 49426a0417 Visualise PolygonRef in SVG
This new method allows you to easily visualise PolygonRefs.
2016-02-03 12:49:17 +01:00
Ghostkeeper 01163bf581 Create constructor for AABB for PolygonRef and vector<PolygonRef>
This is so I won't have to unpack it in the code that uses the AABB.
2016-02-03 12:49:14 +01:00
Ghostkeeper b96bdf41b5 Use default comparator for std::pair
Turns out that the default comparator for std::pair already uses .first to compare two pairs, which is exactly what I had implemented.

Contributes to issue CURA-329.
2016-02-03 12:49:11 +01:00
Ghostkeeper 943c41ed7b Fix off-by-one error on reverse path lookup
When it serialises a path that should be reversed, there was an off-by one error. It now starts polygon_index by 1, which fixes the segfault.

Contributes to issue CURA-329.
2016-02-03 12:49:09 +01:00
Ghostkeeper a6cd192156 Fix off-by-one error on reverse path lookup
When it serialises a path that should be reversed, there was an off-by one error. It now starts polygon_index by 1, which fixes the segfault.

Contributes to issue CURA-329.
2016-02-03 12:49:06 +01:00
Ghostkeeper 06c82f3426 Factor out the cluster when serialising resulting optimised path
This makes the code a bit more maintainable.

Contributes to issue CURA-329.
2016-02-03 12:49:04 +01:00
Ghostkeeper eb06f19f11 Reverse sorting order within cluster
Since the lines were already almost sorted, this makes the sorting a lot faster.

Contributes to issue CURA-329.
2016-02-03 12:49:01 +01:00
Ghostkeeper 4fc471ecf9 Don't forget to use the sorted lines within a cluster
The lines were beautifully sorted by y-intercept as far as I know, but the sorted list was then discarded and the unsorted list was used. This fixes it.

Contributes to issue CURA-329.
2016-02-03 12:48:57 +01:00
Ghostkeeper ff018d40c0 Fix segfault
The construct of `bool picked[n] = {false}` gave a segfault for some reason, even though I just copied it from the internet where they used the same construct with an int array. Maybe this doesn't work properly for booleans with this construct in GCC.

Contributes to issue CURA-329.
2016-02-03 12:48:54 +01:00
Ghostkeeper cc9199ee50 Use clustering to make path optimiser more efficient
Groups of lines where both endpoints are close together are clustered. Within a cluster, the lines are sorted in order of increasing y-intercept, which should work excellently for infill and skin lines. The clusters are then optimised with TSP. Not properly tested yet since elsewhere in the code it has a segfault, but I wanted to make these separate commits.

Contributes to issue CURA-329.
2016-02-03 12:48:48 +01:00
Ghostkeeper 7bcef0ad97 Prettier array initialisation
Should've looked this up first. I keep forgetting this syntactic sugar.

Contributes to issue CURA-329.
2016-02-03 12:48:45 +01:00
Ghostkeeper f5f9595627 Write a clustering method for infill lines
This will cluster lines of infill together so that they don't need to be ordered all individually. The clustering method is fairly dumb right now. It could be made much more effective with some smarter data structures.

Contributes to issue CURA-329.
2016-02-03 12:48:42 +01:00
Ghostkeeper d2f2417012 Optimise indices of size_t instead of ints
Contributes to issue CURA-329.
2016-02-03 12:48:39 +01:00
Ghostkeeper 5299c76521 Document the calling of TSP.
Contributes to issue CURA-329.
2016-02-03 12:48:36 +01:00
Ghostkeeper 80f976d1f3 Remove duplicate code for reverse paths
In the previous commit I immediately finalised reverse paths in their waypoints as soon as it was known that the path was to be reversed. This changed the code for the non-reversed try (when reverse paths are allowed) more or less equal to the code for when reversing paths isn't allowed. I removed the code for this non-reversed try, making it always try non-reversed and only try reversed if reversed is allowed.

Contributes to issue CURA-329.
2016-02-03 12:48:33 +01:00
Ghostkeeper b117e64288 Store reverse path into waypoint
This makes the algorithm run a bit faster and makes the code simpler. Instead of relying only on the is_reversed field and checking that field every time we need to look up the endpoints of the element, we now simply swap the endpoints if the element needs to be reversed.

Contributes to issue CURA-329.
2016-02-03 12:48:30 +01:00
Ghostkeeper ca41665c3e std::list.insert() inserts before an item, not after
I understood this wrong from the documentation. This makes several things easier. This could be optimised slightly but that is of later concern.

Contributes to issue CURA-329.
2016-02-03 12:48:25 +01:00
Ghostkeeper b94c95149a Use vSize rather than a custom float-implementation
This one is a bit slower but it is neater to re-use code, and we are more certain that it works.

Contributes to issue CURA-329.
2016-02-03 12:48:22 +01:00
Ghostkeeper 5b95b7a557 Properly call TravellingSalesman from optimiser
It needs to optimise the order of indices to PolygonRefs at the moment. I'd like to refactor this construction away... But that takes some effort.

Contributes to issue CURA-329.
2016-02-03 12:48:20 +01:00
Ghostkeeper bdc31425ba Skip the starting waypoint
If there is a starting point, it produces a waypoint somewhere in the path (either the first or the last point). This waypoint shouldn't be included in the eventual path. Skip it. This can be done a bit more efficient but that is of later concern.

Contributes to issue CURA-329.
2016-02-03 12:48:17 +01:00
Ghostkeeper f001f31e78 TSP pass by value instead of by reference
This prevents segfaults when the underlying vectors are resized for any reason.
2016-02-03 12:48:14 +01:00
Ghostkeeper 6d56e2c622 Actually add waypoints to shuffle vector
It seems I had forgotten to add waypoints to be shuffled.

Contributes to issue CURA-329.
2016-02-03 12:48:11 +01:00
Ghostkeeper c983a7e83b TSP prepends new path before first element
Instead of trying to prepend new elements before the original first element, it now prepends new elements before the currently first element.
2016-02-03 12:48:08 +01:00
Ghostkeeper 86d0ce4e6b Refactor the TSP solver
The algorithm is no longer implemented twice, but once with a load of ifs checking for allow_reverse. I'm still not sure which is neater...
2016-02-03 12:48:05 +01:00
Ghostkeeper f79933b140 Implement findPath
This version tries to insert elements also in reverse direction, which has some effects on various parts of the code. It also turned out that the previous version wasn't compiling properly but that didn't come up because the function was never used, so it was being optimised out.
2016-02-03 12:45:44 +01:00
Ghostkeeper a47716161a Document the linearising of the result
This wasn't quite clear in a re-read of my code.
2016-02-03 12:45:41 +01:00
Ghostkeeper 831f5ea16e Refactor TSP to use templates
Kind of a big rewrite and not properly tested yet.
2016-02-03 12:45:37 +01:00
Ghostkeeper 219d400e62 Correct license header
Yeah that was wrong.
2016-02-03 12:45:34 +01:00
Ghostkeeper 2b8c92cba9 Use TSP solver in path order optimiser
It also prints out the optimised path now, though that is not what we'd like to see eventually.
2016-02-03 12:45:30 +01:00
Ghostkeeper 44c5c11160 Fix memory pre-allocation
Turns out that it doesn't work to put the required memory in the constructor. You have to call .reserve(int) separately.
2016-02-03 12:45:26 +01:00
Ghostkeeper 1f5cdf5d6b Add TSP for line segments
This implementation is for TSP that visits a selection of line segments, following each line segment in the order that makes the total path short. Line segments may also be traversed in reverse direction.
2016-02-03 12:45:23 +01:00
Ghostkeeper 7607992eb6 Fix start point check
The check to never insert before the start point was wrong. It should've inserted before only if the selected point is not the starting point or there is no starting point.
2016-02-03 12:45:19 +01:00
Ghostkeeper 2461073d64 Add new waypoints to bucket grid
This would allow them to be found quickly, or rather, to be found at all when looking for nearby points.
2016-02-03 12:45:15 +01:00
Ghostkeeper 3a43fe2885 Delete waypoints after use
This prevents a memory leak.
2016-02-03 12:45:10 +01:00
Ghostkeeper 9e2d5217d0 Add return documentation
Forgot to add that previously.
2016-02-03 12:45:07 +01:00
Ghostkeeper 353e151c6b Initial TSP implementation
This implementation should work for TSP with points. TSP with lines coming up next. Has not yet been tested thoroughly, but the code is not used anywhere.
2016-02-03 12:45:01 +01:00
Ghostkeeper 6826581497 Fix compile error
That needs to be a C-string.
2016-02-02 13:08:25 +01:00
Jaime van Kessel 4fead4612b Merge branch 'new_arcus_api' of github.com:Ultimaker/CuraEngine into 2.1 2016-02-02 11:25:45 +01:00
Ghostkeeper f763edfb05 Send FinishedSlicing message later
The FinishedSlicing message was being sent after the mesh group is done slicing, but it should've been sent after all slicing is done (also other mesh groups and after sending the metadata). This indicates that the slicer is really really done.

Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Ghostkeeper 0e662d7d67 Register SlicingFinished message type
The message type of this message is 8 (as documented in the proto file).

Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Ghostkeeper 94d8c3ff32 Document protobuf message typeid
Contributes to issue CURA-427.
2016-02-01 10:21:53 +01:00
Arjen Hiemstra 8e91753afc Ignore ObjectList messages with no objects and Objects with no vertices 2016-01-28 18:17:24 +01:00
Arjen Hiemstra 21e59cc1e2 Update CommandSocket with the new Arcus API 2016-01-28 03:24:12 +01:00
Tim Kuipers 14b1c5333a fix: retraction was prevented for non-direct support to support moves which did avoid other parts (CURA-777)
most notably I introduced path[0] == lastPosition && path[1] == p to the condistion; rest is just restruction
2016-01-27 12:16:20 +01:00
Tim Kuipers bbe809dabc fix: sendPolygons in coasting had a lil bug (CURA-778)
sendPolygons was called for lines toward the starting point instead of the new point

also a line was sent for the coasted path, which is actually just a move path
2016-01-25 17:08:45 +01:00
Tim Kuipers ed581a92b2 fix: reintroduced infill_overlap for the right features (CURA-765) 2016-01-25 16:38:47 +01:00
Tim Kuipers 9b68305851 refactor: matrix ==> rotation_matrix (CURA-401) 2016-01-25 12:01:09 +01:00
Tim Kuipers 940d3a86bd refactor: moved some implementation comments and renamed 'uneven' to 'odd' (CURA-401) 2016-01-25 11:27:52 +01:00
Tim Kuipers 55047120d8 refactor: split ZigZagConnectorProcessor into separate files (CURA-401) 2016-01-25 11:05:58 +01:00
Tim Kuipers e50b00fd73 fix: introduced new socket msg SlicingFinished (CURA-427) 2016-01-22 10:57:27 +01:00
Tim Kuipers 7377f30fd0 lil (remove unused var) 2016-01-21 10:26:24 +01:00
Tim Kuipers c9245e0926 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 14:55:28 +01:00
Tim Kuipers 7149b7dbf1 fix: infill was overlapping with wall with incorrect amount; skin was incorrect when using a single wall and the outer wall line width differs from the inner walls (CURA-729) 2016-01-20 14:37:13 +01:00
Ghostkeeper daa3927a99 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 14:34:13 +01:00
Ghostkeeper 074d1125e6 Fix STL header size
This was actually causing errors with loading STL files.

Sort of contributes to issue CURA-605.
2016-01-20 14:33:52 +01:00
Tim Kuipers a92274678d Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-20 11:22:36 +01:00
Tim Kuipers 081393d7f0 fix: offset issues (CURA-401) 2016-01-20 11:22:10 +01:00
Tim Kuipers 30da3097d0 lil code convention refactor (CURA-401) 2016-01-19 17:33:46 +01:00
Tim Kuipers d830fc515b lil 2016-01-19 17:28:47 +01:00
Tim Kuipers 5f2d924771 refactor: let fill_angle and line_distance be retrieved from Infill members (CURA-401) 2016-01-19 17:23:01 +01:00
Tim Kuipers b1ab1cae5a refactor: renamed variables to lower_case (code convention) (CURA-401) 2016-01-19 17:19:49 +01:00
Tim Kuipers 1fb6b20d90 refactor: let in_outline and outlineOffset be retrieved from Infill members (CURA-401) 2016-01-19 17:14:29 +01:00
Tim Kuipers dfb4c98e35 doc: infill documentation (CURA-401) 2016-01-19 13:40:21 +01:00
Tim Kuipers e455d63ad2 refactor: made infill generation functions public and let them use Infill member variables; also a bugfix where a PointMatrix was requested but a double was given (CURA-401) 2016-01-19 13:07:45 +01:00
Tim Kuipers cb024c73b1 refactor: made infill generation functions non-static (CURA-401) 2016-01-19 12:45:59 +01:00
Tim Kuipers d2d25058cb more comments for infill generation (CURA-401) 2016-01-19 12:29:12 +01:00
Tim Kuipers e85362370b more comments for (zigzag) infill generation (CURA-401) 2016-01-19 12:24:18 +01:00
Tim Kuipers 00c6f5c092 refactor: extrusionWidth ==> infill_line_width (CURA-401) 2016-01-18 17:29:14 +01:00
Tim Kuipers 818c7da951 lil comments (CURA-401) 2016-01-18 17:28:58 +01:00
Tim Kuipers 41723c8b38 removed unused registerPolyStart (CURA-401) 2016-01-18 17:22:10 +01:00
Tim Kuipers c8d75dd913 refactor: cleaned up inheritance structure of ZigZagConnectorProcessor classes (CURA-401)
removed code duplication by introducing another class called ActualZigzagConnectorProcessor.

explained the inheritance structure in the base class description.

made some things protected instead of public.
2016-01-18 16:49:39 +01:00
Tim Kuipers faa60c408f doc: documentation cleanup and expansion (CURA-401) 2016-01-18 15:27:40 +01:00
Tim Kuipers 7e322bc57c refactor: correction of tabs and spaces only (CURA-401) 2016-01-18 14:55:07 +01:00
Tim Kuipers 888fc54660 refactor: made all global infill functions private static (CURA-401) 2016-01-18 14:53:33 +01:00
Tim Kuipers fd660fcc11 refactor: connect_zigzags ==> connected_zigzags (cura-401) 2016-01-18 14:18:56 +01:00
Tim Kuipers e8080422a4 refactor: generateLineInfill_alt ==> generateLinearBasedInfill (CURA-401) 2016-01-18 14:16:33 +01:00
Tim Kuipers 5fecf2cd17 removed all old unused generateLine/ZigZagInfill code (CURA-401) 2016-01-18 14:15:26 +01:00
Tim Kuipers b4bf17c6be refactor: let rotation_matrix bubble down from Infill class; introduce some appropriate consts (CURA-401) 2016-01-18 14:07:38 +01:00
Ghostkeeper fabd658b53 STL file binary vs. ASCII detection robust against whitespace
The ASCII file may contain whitespace at the start of the file. This causes the STL file to be flagged as binary (since it doesn't start with "solid"). This fix first skips all whitespace before trying to find "solid".

Contributes to issue CURA-605.
2016-01-17 04:42:48 +01:00
Ghostkeeper a2050de513 Binary STL load use file size instead of face count
To determine the amount of memory allocated and the amount of faces to try to load, use the size of the file rather than the reported face count in the file. The reported face count may be missing or corrupt, in which case the engine would crash. If that happens now, it might miss one face. It will also give a warning if the reported face count does not match.

Contributes to issue CURA-605.
2016-01-17 04:09:14 +01:00
Tim Kuipers 9bfb5b17e9 refactor: made ZigzagConnectedEndpieces and ZigzagDisconnectgedEndpieces inherit from virtual ZigzagEndpieces; fix: several bugfixes (CURA-401)
notably the disconnected endpieces now disconnects all endpieces instead of half
2016-01-15 17:37:03 +01:00
Tim Kuipers e93d39d6d5 fix: disconnected endpieces (zigzag); reordered some of the connected endpieces code (CURA-401) 2016-01-15 17:16:46 +01:00
Tim Kuipers 175a65415a fix: support zigzag connected endpieces bugfixes (CURA-401)
before, only half of the endpieces were drawn

some problems had slipped into the translation, such as the resetting after each polygon
2016-01-15 16:53:39 +01:00
Tim Kuipers 74986dd95c lil refactor: changed last_var to this_var at the moment it was set for conceptual clarity (CURA-401) 2016-01-15 16:49:55 +01:00
Tim Kuipers 55dd35ae5e refactor: logic simplifications; fix: reintroduce skipping of scanline if poly is simple (CURA-401)
rewrote some for-if combinations which could be made simpler

small polygons which only cover a single scanline which are printed in total don't need the scanline...
2016-01-15 15:39:25 +01:00
Tim Kuipers 8caf810642 refactor: code conventions etc for generate(Lines/Zigzag)Infill (CURA-401) 2016-01-15 15:23:41 +01:00
Tim Kuipers 76efc41407 refactor: merge of generate(Lines/ZigZag)Infill (CURA-401) 2016-01-15 14:50:24 +01:00
Tim Kuipers b0a0fe8f30 refactor: code conventions in generate infill paths (CURA-401) 2016-01-14 11:17:06 +01:00
Tim Kuipers 653b631ffd lil 2016-01-14 11:16:36 +01:00
Tim Kuipers b9a01ed031 bugfix: made MargeInfill.isConvertible not static, cause it needs the nozzle_size class member (CURA-581) 2016-01-13 12:10:37 +01:00
Tim Kuipers de9d3bb447 lil fix: MergeInfill.nozzle_size used instead of line_width (CURA-581)
MergeInfillLines already had a member nozzle_size, though still it had a line defining a local variable 'int nozzle_size = line_width; // TODO'

Also some minor code convention brackets...
2016-01-13 11:50:47 +01:00
Arjen Hiemstra 8f5a46f77d Ensure we use libc++ as stdlib on OSX 2016-01-06 16:56:04 +01:00
Tim Kuipers c22793b5d7 Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2016-01-05 18:22:52 +01:00
Tim Kuipers 1e684f23a7 refactor: removed old sendPolygons functions (CURA-379) 2016-01-05 18:22:34 +01:00
Tim Kuipers 7d0025975b refactor: removed old sendPolygons functions (CURa-379) 2016-01-05 18:20:44 +01:00
Tim Kuipers 5c892b564b fix: send all polygons at end of slicing process instead of during (CURA-379) 2016-01-05 18:17:03 +01:00
Tim Kuipers d5327ec3f1 refactor: let all CommandSocket calls go throught CommandSocket.getInstance() (CURA-379) 2016-01-05 17:35:12 +01:00
Tim Kuipers c48104bc86 refactor: made CommandSocket a kind of singleton class (CURA-379) 2016-01-05 17:09:08 +01:00
Tim Kuipers f58f1daec3 refactor: commandSocket ==> command_socket (CURA-379) 2016-01-05 17:08:24 +01:00
Tim Kuipers 669bb523a3 bugfix: commandSocket depends on there being a NoneType (CURA-581) 2016-01-05 13:07:26 +01:00
Tim Kuipers fa74f672ac lil 2016-01-05 12:23:44 +01:00
Tim Kuipers e224d9c853 MergeInfillLines early abort if space filling type is not Lines (CURA-581) 2016-01-05 12:23:10 +01:00
Tim Kuipers 338d80a7a9 lil fix in test (CURA-581) 2016-01-05 12:20:46 +01:00
Tim Kuipers 87123dac31 introduction of GCOdePath.space_fill_type (Polygon,Line or PolyLine) (CURA-581) 2016-01-05 12:20:09 +01:00
Tim Kuipers 73de288f44 lil cleanup of unused commented out code (CURA-581) 2016-01-05 11:30:43 +01:00
Tim Kuipers 9f5b5f405d lil enhancements mergeInfillLines (CURA-581) 2016-01-05 11:27:06 +01:00
Tim Kuipers 2850b34227 fix: mergeInfillLines aborts if applied to a print feature other than infill or skin (CURA-581) 2016-01-05 11:23:51 +01:00
Tim Kuipers 072d320f1d refactor: FeatureType ==> PrintFeatureType (CURA-581) 2016-01-05 11:11:39 +01:00
Tim Kuipers 8e4b61d2a5 merge of 4c139d6441 (CURA-581) 2016-01-05 11:10:56 +01:00
Tim Kuipers ba4e26f801 bugfix: concentric infill overlapped with skin; extra offset for concentric infill moved inside infill.cpp (CURA-650) 2016-01-05 10:04:33 +01:00
Tim Kuipers cea0a0a98f bugfix: no draft shield or concentric infill sendPolygons (CURA-639) 2016-01-04 16:55:41 +01:00
Ghostkeeper 2b51a11739 Fix new command socket not propagating to LayerPlanBuffer
This caused the command socket in LayerPlanBuffer to remain null, thus never flushing the g-code until the very end, thus causing the front-end to interpret the entire g-code as one layer, thus causing the post processing plugin to inject g-code at the wrong places.

Contributes to issue CURA-443.
2015-12-31 11:22:37 +01:00
Tim Kuipers d32443ca9b bugfixes: merge infill (CURA-581) 2015-12-18 14:25:07 +01:00
Tim Kuipers 792aa9e8e8 merge infill improvements (CURA-581) 2015-12-18 12:50:50 +01:00
Tim Kuipers a63abb4e88 merge infill improvements maybe (CURA-581) 2015-12-18 10:49:12 +01:00
Tim Kuipers 9f82f58f03 bugfix: moveInside called with max_dist instead of max_dist_squared (CURA-522) 2015-12-15 16:33:51 +01:00
Tim Kuipers df04467ab1 better doc (CURA-522) 2015-12-09 17:24:05 +01:00
Tim Kuipers bbd9412b5f bugfixes moveInside (CURA-522) 2015-12-09 17:23:50 +01:00
Tim Kuipers c476acd522 bugfix: moveInside distance was BS (CURA-522) 2015-12-09 17:22:50 +01:00
Tim Kuipers 278efdeb39 merge of cherry-pick of 6115bde (CURA-522) 2015-12-09 17:22:09 +01:00
Tim Kuipers bb8a9dacba removed Comb.moveInside and Comb.isInside and inlined the implementation (CURA-522)
Since comb_boundary_inside is now a field of gcodeplanner these utility functions were at an awkward place.

Conflicts:
	src/gcodePlanner.cpp
	src/gcodePlanner.h
2015-12-09 17:18:55 +01:00
Tim Kuipers 42420edeed merge of cherry-pick of 5c3727d (CURA-522) 2015-12-09 17:11:56 +01:00
Tim Kuipers be2e96f3fd getLayerSecondOrInnermostWalls functions (CURA-522) 2015-12-09 17:04:50 +01:00
Tim Kuipers 18adc0bbc9 merge of cherry-pick of c7ce43e (CURA-522) 2015-12-09 17:02:37 +01:00
Tim Kuipers e7824faefe merge of cherry-pick of c7ce43e (CURA-522) 2015-12-09 17:01:37 +01:00
Tim Kuipers 7199be1b4f merge of cherry-pick of d9e2b39 (CURA-522) 2015-12-09 16:54:13 +01:00
Tim Kuipers 727c863f1a optimization: handle socket object list after listening for messages (CURA-441) 2015-12-08 15:58:00 +01:00
Tim Kuipers d732c49dd7 bugfix: close socket before exiting the program (CURA-441) 2015-12-08 15:57:26 +01:00
Tim Kuipers 781fc5ed7b lil doc (CURA-524) 2015-12-08 14:48:19 +01:00
Tim Kuipers 7243cf6da4 renamed commandSocket.d to commandSocket.private_data (CURA-524) 2015-12-08 14:44:15 +01:00
Tim Kuipers 1cdcce4205 revert of 6224713998 (CURA-524) 2015-12-08 14:42:00 +01:00
Tim Kuipers acf381c008 bugfix: no vizualization of last layers trvel moves (CURA-524) 2015-12-08 14:29:44 +01:00
Tim Kuipers 4b7df9ddc0 lil refac + doc (CURA*524) 2015-12-08 14:28:52 +01:00
Tim Kuipers 7788a4a234 refactor: sendGCodeLayer ==> flushGcode (CURA-524) 2015-12-08 14:28:20 +01:00
Tim Kuipers 6224713998 renamed commandSocket.d to commandSocket.private_data (CURA-524) 2015-12-08 13:53:22 +01:00
Tim Kuipers db8b30d77a refactor: Min Volume Before Coasting doesn't include Coasting Volume anymore (CURA-528) 2015-12-08 13:32:31 +01:00
Tim Kuipers 050b9c88f2 bugfix: coasting volume more than min volume (CURA-528) 2015-12-07 18:03:40 +01:00
Ghostkeeper 8ac63fca6e Log a warning when trying to override nonexistent setting
If there's a problem in the front-end this should expose it.

Contributes to issue CURA-544.
2015-12-07 15:38:21 +01:00
Ghostkeeper 1383882bc5 Fixed segfault when provided with unknown settings
When an override of a setting is provided via the JSON the engine would crash. This ignores those settings.

Contributes to issue CURA-544.
2015-12-07 15:18:39 +01:00
Tim Kuipers 3a773d3c0f bugfix: layer plan buffer flush did flush water, but left the turd (CURA-463) 2015-12-07 12:39:25 +01:00
Tim Kuipers 734ddce3c8 lil (CURA-463) 2015-12-07 12:38:14 +01:00
Tim Kuipers 0b19936299 main didn't allow for next meshgroup to be processed (CURA-463) 2015-12-07 12:37:02 +01:00
Tim Kuipers 691d5de591 lil doc (CURA-499) 2015-12-04 17:04:45 +01:00
Tim Kuipers 490cef1a5c Merge branch '2.1' of https://github.com/Ultimaker/CuraEngine into 2.1 2015-12-04 16:57:00 +01:00
Tim Kuipers b777b55935 bugfix: output command line call (CURA-499) 2015-12-04 16:56:49 +01:00
Ghostkeeper 0428c08152 Fix indenting
The main function of runtest.py was indented with tabs instead of spaces.
2015-12-04 15:58:53 +01:00
Tim Kuipers 20c74dd22d lil bugfix CURA-486 (stiekum gekoppeld) 2015-12-03 18:47:12 +01:00
Tim Kuipers 188b190d21 inlined writePathWithCoasting (CURA-486) 2015-12-03 18:05:29 +01:00
Tim Kuipers 1cd128decd coasting collapsed: no more diff between move-coasting and retract-coasting (CURA-486) 2015-12-03 18:01:34 +01:00
Tim Kuipers 925247a54d bugfix: don't perform the retraction *during* coasting (CURA-486) 2015-12-03 18:00:34 +01:00
66 arquivos alterados com 2712 adições e 1627 exclusões
+15 -17
Ver Arquivo
@@ -2,14 +2,7 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
find_package(Arcus REQUIRED)
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -17,6 +10,10 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
@@ -73,6 +70,12 @@ set(engine_SRCS # Except main.cpp.
src/Weaver.cpp
src/Wireframe2gcode.cpp
src/infill/NoZigZagConnectorProcessor.cpp
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/utils/gettime.cpp
src/utils/logoutput.cpp
src/utils/polygonUtils.cpp
@@ -83,19 +86,15 @@ set(engine_SRCS # Except main.cpp.
set(engine_TEST
GCodePlannerTest
LinearAlg2DTest
TravellingSalesmanTest
)
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
# Generating ProtoBuf protocol.
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
# Compiling CuraEngine itself.
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
target_link_libraries(_CuraEngine clipper)
if (ENABLE_ARCUS)
target_link_libraries(_CuraEngine Arcus)
endif ()
target_link_libraries(_CuraEngine clipper Arcus)
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
@@ -119,5 +118,4 @@ endif()
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
include(CPackConfig.cmake)
+4
Ver Arquivo
@@ -98,3 +98,7 @@ message Setting {
message GCodePrefix {
bytes data = 2;
}
// typeid 8
message SlicingFinished {
}
+103 -164
Ver Arquivo
@@ -21,8 +21,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
gcode.resetTotalPrintTimeAndFilament();
}
if (command_socket)
command_socket->beginGCode();
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->beginGCode();
setConfigFanSpeedLayerTime();
@@ -72,17 +72,11 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
processLayer(storage, layer_nr, total_layers, has_raft);
}
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper, command_socket);
Progress::messageProgressStage(Progress::Stage::FINISH, &time_keeper);
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
max_object_height = std::max(max_object_height, storage.model_max.z);
if (command_socket)
{
command_socket->sendGCodeLayer();
command_socket->endSendSlicedObject();
}
layer_plan_buffer.flush();
}
@@ -104,12 +98,9 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extr);
CoastingConfig& coasting_config = storage.coasting_config.back();
coasting_config.coasting_enable = train->getSettingBoolean("coasting_enable");
coasting_config.coasting_volume_move = train->getSettingInCubicMillimeters("coasting_volume_move");
coasting_config.coasting_min_volume_move = train->getSettingInCubicMillimeters("coasting_min_volume_move");
coasting_config.coasting_speed_move = train->getSettingInPercentage("coasting_speed_move") / 100.0;
coasting_config.coasting_volume_retract = train->getSettingInCubicMillimeters("coasting_volume_retract");
coasting_config.coasting_min_volume_retract = train->getSettingInCubicMillimeters("coasting_min_volume_retract");
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
coasting_config.coasting_volume = train->getSettingInCubicMillimeters("coasting_volume");
coasting_config.coasting_min_volume = train->getSettingInCubicMillimeters("coasting_min_volume");
coasting_config.coasting_speed = train->getSettingInPercentage("coasting_speed") / 100.0;
}
}
@@ -162,8 +153,8 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
}
{ // support
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_infill_extruder_nr"));
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_infill"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
SettingsBase* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("support_extruder_nr"));
storage.support_config.init(getSettingInMillimetersPerSecond("speed_support_lines"), getSettingInMicrons("support_line_width"), train->getSettingInPercentage("material_flow"));
storage.support_roof_config.init(getSettingInMillimetersPerSecond("speed_support_roof"), getSettingInMicrons("support_roof_line_width"), train->getSettingInPercentage("material_flow"));
}
@@ -173,20 +164,19 @@ void FffGcodeWriter::initConfigs(SliceDataStorage& storage)
mesh.inset0_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_0"), mesh.getSettingInMicrons("wall_line_width_0"), mesh.getSettingInPercentage("material_flow"));
mesh.insetX_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_x"), mesh.getSettingInMicrons("wall_line_width_x"), mesh.getSettingInPercentage("material_flow"));
mesh.skin_config.init(mesh.getSettingInMillimetersPerSecond("speed_topbottom"), mesh.getSettingInMicrons("skin_line_width"), mesh.getSettingInPercentage("material_flow"));
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].init(mesh.getSettingInMillimetersPerSecond("speed_infill"), mesh.getSettingInMicrons("infill_line_width") * (idx + 1), mesh.getSettingInPercentage("material_flow"));
}
mesh.wall_reinforcement_config.init(mesh.getSettingInMillimetersPerSecond("speed_wall_reinforcement"), mesh.getSettingInMicrons("wall_reinforcement_line_width"), mesh.getSettingInPercentage("material_flow"));
}
storage.primeTower.initConfigs(storage.meshgroup, storage.retraction_config_per_extruder);
}
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
{
if (!command_socket)
if (!CommandSocket::isInstantiated())
{
std::ostringstream prefix;
prefix << "FLAVOR:" << toString(gcode.getFlavor());
@@ -273,25 +263,34 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
storage.raft_surface_config.setLayerHeight(train->getSettingInMicrons("raft_surface_thickness"));
}
// some infill config for all lines infill generation below
Polygons* in_between = nullptr;
int offset_from_poly_outline = 0;
bool avoidOverlappingPerimeters = false;
double fill_overlap = 0; // raft line shouldn't be expanded - there is no boundary polygon printed
Polygons raft_polygons; // should remain empty, since we only have the lines pattern for the raft...
{ // raft base layer
int layer_nr = -n_raft_surface_layers - 2;
int layer_height = getSettingInMicrons("raft_base_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
gcode_layer.setExtruder(extruder_nr);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
}
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &storage.raft_base_config);
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_base_config.getLineWidth());
double fill_angle = 0;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_base_config.getLineWidth(), train->getSettingInMicrons("raft_base_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines, in_between);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_base_config, SpaceFillType::Lines);
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -304,17 +303,18 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
int layer_nr = -n_raft_surface_layers - 1;
int layer_height = train->getSettingInMicrons("raft_interface_thickness");
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config);
sendPolygons(SupportType, layer_nr, raftLines, storage.raft_interface_config.getLineWidth());
double fill_angle = train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_interface_config.getLineWidth(), train->getSettingInMicrons("raft_interface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raftLines, in_between);
gcode_layer.addLinesByOptimizer(raftLines, &storage.raft_interface_config, SpaceFillType::Lines);
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -329,17 +329,20 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
{ // raft surface layers
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
z += layer_height;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_height, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, retraction_combing, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setIsInside(false);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, layer_height);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendLayerInfo(layer_nr, z, layer_height);
}
Polygons raft_lines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config);
sendPolygons(SupportType, layer_nr, raft_lines, storage.raft_surface_config.getLineWidth());
double fill_angle = 90 * raftSurfaceLayer;
Infill infill_comp(EFillMethod::LINES, storage.raftOutline, offset_from_poly_outline, avoidOverlappingPerimeters, storage.raft_surface_config.getLineWidth(), train->getSettingInMicrons("raft_surface_line_spacing"), fill_overlap, fill_angle);
infill_comp.generate(raft_polygons, raft_lines, in_between);
gcode_layer.addLinesByOptimizer(raft_lines, &storage.raft_surface_config, SpaceFillType::Lines);
last_position_planned = gcode_layer.getLastPosition();
current_extruder_planned = gcode_layer.getExtruder();
@@ -351,7 +354,7 @@ void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_l
void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, command_socket);
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers);
int layer_thickness = getSettingInMicrons("layer_height");
if (layer_nr == 0)
@@ -359,18 +362,9 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
layer_thickness = getSettingInMicrons("layer_height_0");
}
int max_nozzle_size = 0;
std::vector<bool> extruders_used = storage.getExtrudersUsed(layer_nr);
for (int extr_nr = 0; extr_nr < storage.meshgroup->getExtruderCount(); extr_nr++)
{
if (extruders_used[extr_nr])
{
max_nozzle_size = std::max(max_nozzle_size, storage.meshgroup->getExtruderTrain(extr_nr)->getSettingInMicrons("machine_nozzle_size"));
}
}
int64_t comb_offset_from_outlines = max_nozzle_size * 2;// TODO: only used when there is no second wall.
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(current_extruder_planned)->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(command_socket, storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
GCodePlanner& gcode_layer = layer_plan_buffer.emplace_back(storage, layer_nr, z, layer_thickness, last_position_planned, current_extruder_planned, fan_speed_layer_time_settings, getSettingBoolean("retraction_combing"), comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
if (layer_nr == 0)
{
@@ -446,21 +440,20 @@ void FffGcodeWriter::processDraftShield(SliceDataStorage& storage, GCodePlanner&
{
return;
}
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int layer_height_0 = getSettingInMicrons("layer_height_0");
int layer_height = getSettingInMicrons("layer_height");
int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
if (int(layer_nr) > max_screen_layer)
{
return;
}
gcode_layer.setIsInside(false);
gcode_layer.addPolygonsByOptimizer(storage.draft_protection_shield, &storage.skirt_config[0]); // TODO: skirt config idx should correspond to draft shield extruder nr
}
std::vector<unsigned int> FffGcodeWriter::calculateMeshOrder(SliceDataStorage& storage, int current_extruder)
@@ -531,7 +524,7 @@ void FffGcodeWriter::addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, Slic
lines.add(p);
}
}
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config);
gcode_layer.addLinesByOptimizer(lines, &mesh->inset0_config, SpaceFillType::PolyLines);
}
@@ -577,28 +570,16 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
int wall_reinforcement_line_distance = mesh->getSettingInMicrons("wall_reinforcement_line_distance");
int wall_reinforcement_line_width = mesh->wall_reinforcement_config.getLineWidth();
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
for (unsigned int wall_idx = part.reinforcement_walls.size() - 1; int(wall_idx) >= 0; wall_idx--)
{
ReinforcementWall& reinforcement_wall = part.reinforcement_walls[wall_idx];
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, true);
}
}
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
for (ReinforcementWall& reinforcement_wall : part.reinforcement_walls)
{
processWallReinforcement(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width, false);
}
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, infill_line_width);
}
@@ -636,13 +617,13 @@ void FffGcodeWriter::processMultiLayerInfill(GCodePlanner& gcode_layer, SliceMes
//Print the thicker infill lines first. (double or more layer thickness, infill combined with previous layers)
for(unsigned int n=1; n<part.infill_area.size(); n++)
{
Infill infill_comp(mesh->getSettingAsFillMethod("infill_pattern"), part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
EFillMethod infill_pattern = mesh->getSettingAsFillMethod("infill_pattern");
Infill infill_comp(infill_pattern, part.infill_area[n], 0, false, extrusion_width, infill_line_distance, infill_overlap, infill_angle, false, false);
Polygons infill_polygons;
Polygons infill_lines;
infill_comp.generate(infill_polygons, infill_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[n]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n]);
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[n], (infill_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
}
}
@@ -665,61 +646,12 @@ void FffGcodeWriter::processSingleLayerInfill(GCodePlanner& gcode_layer, SliceMe
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->infill_config[0]);
if (pattern == EFillMethod::GRID || pattern == EFillMethod::LINES || pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], mesh->getSettingInMicrons("infill_wipe_dist"));
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], SpaceFillType::Lines, mesh->getSettingInMicrons("infill_wipe_dist"));
}
else
{
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0]);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->infill_config[0], (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
sendPolygons(InfillType, layer_nr, infill_lines, extrusion_width);
}
void FffGcodeWriter::processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out)
{
if (wall_reinforcement_line_distance == 0 || (reinforcement_wall.wall_reinforcement_area.size() == 0 && reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0) )
{
return;
}
if (inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
processWallReinforcement_infill(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_distance, infill_overlap, infill_angle, wall_reinforcement_line_width);
if (!inside_out)
{
processWallReinforcement_extraWalls(gcode_layer, mesh, reinforcement_wall, layer_nr, wall_reinforcement_line_width, inside_out);
}
}
void FffGcodeWriter::processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out)
{
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
for(int inset_number=reinforcement_wall.wall_reinforcement_axtra_walls.size()-1; inset_number>-1; inset_number--)
{
gcode_layer.addPolygonsByOptimizer(reinforcement_wall.wall_reinforcement_axtra_walls[inset_number], &mesh->insetX_config);
}
}
}
void FffGcodeWriter::processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width)
{
if (reinforcement_wall.wall_reinforcement_area.size() == 0)
{
return;
}
Polygons infill_polygons;
Polygons infill_lines;
EFillMethod pattern = mesh->getSettingAsFillMethod("wall_reinforcement_pattern");
Infill infill_comp(pattern, reinforcement_wall.wall_reinforcement_area, 0, false, wall_reinforcement_line_width, wall_reinforcement_line_distance, infill_overlap, infill_angle, false, false);
infill_comp.generate(infill_polygons, infill_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(infill_polygons, &mesh->wall_reinforcement_config);
gcode_layer.addLinesByOptimizer(infill_lines, &mesh->wall_reinforcement_config);
sendPolygons(SupportInfillType, layer_nr, infill_lines, wall_reinforcement_line_width);
}
void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type)
@@ -775,15 +707,10 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
}
Polygons* inner_skin_outline = nullptr;
int offset_from_inner_skin_outline = 0;
if (pattern == EFillMethod::CONCENTRIC)
{
offset_from_inner_skin_outline = -extrusion_width/2;
}
else
if (pattern != EFillMethod::CONCENTRIC)
{
for (Polygons& skin_perimeter : skin_part.insets)
{
sendPolygons(SkinType, layer_nr, skin_perimeter, mesh->skin_config.getLineWidth());
gcode_layer.addPolygonsByOptimizer(skin_perimeter, &mesh->skin_config); // add polygons to gcode in inward order
}
if (skin_part.insets.size() > 0)
@@ -792,7 +719,13 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
offset_from_inner_skin_outline = -extrusion_width/2;
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int line_distance = extrusion_width;
int outline_offset = 0;
Infill infill_comp(EFillMethod::LINES, skin_part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
infill_comp.generate(result_polygons, skin_lines, in_between);
}
}
}
@@ -806,17 +739,22 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
infill_comp.generate(skin_polygons, skin_lines, &part.perimeterGaps);
gcode_layer.addPolygonsByOptimizer(skin_polygons, &mesh->skin_config);
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config);
sendPolygons(SkinType, layer_nr, skin_polygons, mesh->skin_config.getLineWidth());
sendPolygons(SkinType, layer_nr, skin_lines, mesh->skin_config.getLineWidth());
gcode_layer.addLinesByOptimizer(skin_lines, &mesh->skin_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
// handle gaps between perimeters etc.
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
Polygons perimeter_gap_lines;
generateLineInfill(part.perimeterGaps, 0, perimeter_gap_lines, extrusion_width, extrusion_width, 0, infill_angle);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config);
Polygons result_polygons; // should remain empty, since we're only allowing for lines infill
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int line_distance = extrusion_width;
int outline_offset = 0;
Infill infill_comp(EFillMethod::LINES, part.perimeterGaps, outline_offset, avoidOverlappingPerimeters, extrusion_width, line_distance, infill_overlap, infill_angle);
infill_comp.generate(result_polygons, perimeter_gap_lines, in_between);
gcode_layer.addLinesByOptimizer(perimeter_gap_lines, &mesh->skin_config, SpaceFillType::Lines);
}
}
@@ -826,9 +764,9 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
return;
int support_roof_extruder_nr = getSettingAsIndex("support_roof_extruder_nr");
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
bool print_support_before_rest = support_infill_extruder_nr == extruder_nr_before
bool print_support_before_rest = support_extruder_nr == extruder_nr_before
|| support_roof_extruder_nr == extruder_nr_before;
// TODO: always print support after rest when only one nozzle is used for the whole meshgroup
@@ -841,24 +779,24 @@ void FffGcodeWriter::addSupportToGCode(SliceDataStorage& storage, GCodePlanner&
if (storage.support.supportLayers[layer_nr].roofs.size() > 0)
{
if (support_roof_extruder_nr != support_infill_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
if (support_roof_extruder_nr != support_extruder_nr && support_roof_extruder_nr == current_extruder_nr)
{
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
}
else
{
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
addSupportRoofsToGCode(storage, gcode_layer, layer_nr);
}
}
else
{
addSupportInfillToGCode(storage, gcode_layer, layer_nr);
addSupportLinesToGCode(storage, gcode_layer, layer_nr);
}
}
void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr)
{
if (!storage.support.generated
|| layer_nr > storage.support.layer_nr_max_filled_layer
@@ -866,17 +804,16 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
{
return;
}
int support_line_distance = getSettingInMicrons("support_line_distance");
int extrusion_width = storage.support_config.getLineWidth();
EFillMethod support_pattern = getSettingAsFillMethod("support_pattern");
if (layer_nr == 0 && (support_pattern == EFillMethod::LINES || support_pattern == EFillMethod::ZIG_ZAG)) { support_pattern = EFillMethod::GRID; }
int support_infill_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_infill_extruder_nr");
int support_extruder_nr = (layer_nr == 0)? getSettingAsIndex("support_extruder_nr_layer_0") : getSettingAsIndex("support_extruder_nr");
double infill_overlap = storage.meshgroup->getExtruderTrain(support_infill_extruder_nr)->getSettingInPercentage("infill_overlap");
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_infill_extruder_nr);
setExtruder_addPrime(storage, gcode_layer, layer_nr, support_extruder_nr);
Polygons& support = storage.support.supportLayers[layer_nr].supportAreas;
@@ -893,21 +830,25 @@ void FffGcodeWriter::addSupportInfillToGCode(SliceDataStorage& storage, GCodePla
{
PolygonsPart& island = support_islands[island_order_optimizer.polyOrder[n]];
double infill_overlap = 0; // support infill should not be expanded outward
int offset_from_outline = 0;
Infill infill_comp(support_pattern, island, offset_from_outline, false, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
bool remove_overlapping_perimeters = false;
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
{
Polygons boundary;
PolygonUtils::offsetSafe(island, -extrusion_width / 2, extrusion_width, boundary, remove_overlapping_perimeters);
gcode_layer.addPolygonsByOptimizer(boundary, &storage.support_config);
offset_from_outline = -extrusion_width;
infill_overlap = storage.meshgroup->getExtruderTrain(support_extruder_nr)->getSettingInPercentage("infill_overlap"); // support lines area should be expanded outward to overlap with the boundary polygon
}
Infill infill_comp(support_pattern, island, offset_from_outline, remove_overlapping_perimeters, extrusion_width, support_line_distance, infill_overlap, 0, getSettingBoolean("support_connect_zigzags"), true);
Polygons support_polygons;
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines, nullptr);
if (support_pattern == EFillMethod::GRID || support_pattern == EFillMethod::TRIANGLES)
{
gcode_layer.addPolygonsByOptimizer(island, &storage.support_config);
sendPolygons(SupportType, layer_nr, island, storage.support_config.getLineWidth());
}
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
sendPolygons(SupportInfillType, layer_nr, support_polygons, storage.support_config.getLineWidth());
sendPolygons(SupportInfillType, layer_nr, support_lines, storage.support_config.getLineWidth());
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config, (support_pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
}
@@ -939,7 +880,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
{
fillAngle = 45 + (layer_nr % 2) * 90; // alternate between the two kinds of diagonal: / and \ .
}
double infill_overlap = 0;
double infill_overlap = 0; // the roofs should never be expanded outwards
int outline_offset = 0;
Infill infill_comp(pattern, storage.support.supportLayers[layer_nr].roofs, outline_offset, false, storage.support_roof_config.getLineWidth(), support_line_distance, infill_overlap, fillAngle, false, true);
@@ -948,9 +889,7 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
infill_comp.generate(support_polygons, support_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_roof_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config);
sendPolygons(SupportType, layer_nr, support_polygons, storage.support_roof_config.getLineWidth());
sendPolygons(SupportType, layer_nr, support_lines, storage.support_roof_config.getLineWidth());
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_roof_config, (pattern == EFillMethod::ZIG_ZAG)? SpaceFillType::PolyLines : SpaceFillType::Lines);
}
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
@@ -987,12 +926,12 @@ void FffGcodeWriter::addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcod
bool prime_tower_dir_outward = getSettingBoolean("prime_tower_dir_outward");
bool wipe = getSettingBoolean("prime_tower_wipe_enabled");
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
storage.primeTower.addToGcode(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
}
void FffGcodeWriter::finalize()
{
if (command_socket)
if (CommandSocket::isInstantiated())
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
@@ -1002,7 +941,7 @@ void FffGcodeWriter::finalize()
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
}
command_socket->sendGCodePrefix(prefix.str());
CommandSocket::getInstance()->sendGCodePrefix(prefix.str());
}
gcode.finalize(getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
+3 -54
Ver Arquivo
@@ -17,6 +17,7 @@
#include "commandSocket.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "PrintFeature.h"
#include "LayerPlanBuffer.h"
@@ -42,7 +43,6 @@ private:
LayerPlanBuffer layer_plan_buffer;
GCodeExport gcode;
CommandSocket* command_socket;
std::ofstream output_file;
/*!
@@ -59,30 +59,18 @@ private:
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, layer_plan_buffer(this, command_socket, gcode)
, layer_plan_buffer(this, gcode)
, last_position_planned(no_point)
, current_extruder_planned(0) // TODO: make configurable
{
meshgroup_number = 1;
max_object_height = 0;
command_socket = NULL;
}
void resetFileNumber()
{
meshgroup_number = 1;
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
}
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (command_socket)
command_socket->sendPolygons(type, layer_nr, polygons, line_width);
}
bool setTargetFile(const char* filename)
{
output_file.open(filename);
@@ -124,8 +112,6 @@ private:
*/
void initConfigs(SliceDataStorage& storage);
void setConfigWallReinforcement(SliceMeshStorage& mesh, int layer_thickness);
/*!
* Set temperatures and perform initial priming.
* \param storage Input: where the slice data is stored.
@@ -242,43 +228,6 @@ private:
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, double infill_overlap, int fillAngle, int extrusionWidth);
/*!
* Add wall reinforcement for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
* \param inside_out Whether to print from inside outward or other way around
*/
void processWallReinforcement(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the inner extra walls of the wall reinforcement for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_extraWalls(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_width, bool inside_out);
/*!
* Add the infill of the wall reinforcement for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the gcode.
* \param reinforcement_wall The reinforcement wall for which to create gcode
* \param layer_nr The current layer number.
* \param wall_reinforcement_line_distance The distance between the infill lines
* \param infill_overlap The fraction of the extrusion width by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
* \param wall_reinforcement_line_width extrusionWidth
*/
void processWallReinforcement_infill(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, ReinforcementWall& reinforcement_wall, unsigned int layer_nr, int wall_reinforcement_line_distance, double infill_overlap, int infill_angle, int wall_reinforcement_line_width);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
@@ -317,7 +266,7 @@ private:
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
void addSupportLinesToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support roofs to the gcode of the current layer.
* \param storage Input: where the slice data is stored.
+26 -130
Ver Arquivo
@@ -16,6 +16,7 @@
#include "raft.h"
#include "debug.h"
#include "Progress.h"
#include "PrintFeature.h"
namespace cura
{
@@ -23,8 +24,8 @@ namespace cura
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
if (commandSocket)
commandSocket->beginSendSlicedObject();
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->beginSendSlicedObject();
if (!sliceModel(meshgroup, timeKeeper, storage))
{
@@ -38,7 +39,7 @@ bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* me
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
@@ -65,7 +66,7 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper); //Continue directly with the inset stage, which will also immediately stop.
return true; //This is NOT an error state!
}
@@ -79,18 +80,18 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
//sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
//sendPolygons("openoutline", layer_nr, layer.openPolygonList);
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size(), commandSocket);
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
log("Layer count: %i\n", layer_count);
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
@@ -126,16 +127,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
if (commandSocket)
if (CommandSocket::isInstantiated())
{
commandSocket->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
CommandSocket::getInstance()->sendLayerInfo(layer_nr, layer.printZ, layer_nr == 0? meshStorage.getSettingInMicrons("layer_height_0") : meshStorage.getSettingInMicrons("layer_height"));
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size(), commandSocket);
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::INSET, &timeKeeper);
return true;
}
@@ -152,7 +153,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processInsets(storage, layer_number);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers);
}
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
@@ -163,21 +164,24 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
return;
}
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
AreaSupport::generateSupportAreas(storage, total_layers);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Infill, layer_idx, support, 100); //getSettingInMicrons("support_line_width"));
}
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
if (getSettingBoolean("magic_spiralize"))
{
@@ -192,12 +196,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processSkinsAndInfill(storage, layer_number);
}
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
}
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
processWallReinforcement(storage, layer_number);
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers);
}
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
@@ -222,17 +221,6 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
processFuzzyWalls(mesh);
}
else if (mesh.getSettingAsCount("wall_line_count") > 0)
{ // only send polygon data
for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
{
SliceLayer* layer = &mesh.layers[layer_nr];
for(SliceLayerPart& part : layer->parts)
{
sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
}
@@ -251,16 +239,6 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
inset_count += layer_nr % 2;
generateInsets(layer, mesh.getSettingInMicrons("machine_nozzle_size"), line_width_0, line_width_x, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() > 0)
{
// sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_0); // done after processing fuzzy skin
for(unsigned int inset=1; inset<layer->parts[partNr].insets.size(); inset++)
sendPolygons(InsetXType, layer_nr, layer->parts[partNr].insets[inset], line_width_x);
}
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
@@ -273,72 +251,6 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
segment.add(polyline[point_idx-1]);
segment.add(polyline[point_idx]);
}
sendPolygons(Inset0Type, layer_nr, segments, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
}
void FffPolygonGenerator::processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr)
{
for(SliceMeshStorage& mesh : storage.meshes)
{ // generate infill area
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
continue;
}
if (mesh.getSettingInMicrons("wall_reinforcement_thickness") == 0.0 && mesh.getSettingAsCount("wall_reinforcement_line_count") == 0)
{
return;
}
int inset_count = mesh.getSettingAsCount("wall_reinforcement_line_count");
int wall_line_width = mesh.getSettingInMicrons("wall_line_width_x");
SliceLayer* layer = &mesh.layers[layer_nr];
for (SliceLayerPart& part : layer->parts)
{
if (part.infill_area.size() == 0)
{
continue;
}
int wall_reinforcement_count = mesh.getSettingAsCount("wall_reinforcement_count");
part.reinforcement_walls.reserve(wall_reinforcement_count);
for (unsigned int wall_idx = 0; int(wall_idx) < wall_reinforcement_count; wall_idx++)
{
part.reinforcement_walls.emplace_back();
ReinforcementWall& reinforcement_wall = part.reinforcement_walls.back();
Polygons outer_wall_reinforcement_edge = part.infill_area[0].offset(-mesh.getSettingInMicrons("wall_reinforcement_thickness"));
reinforcement_wall.wall_reinforcement_area = part.infill_area[0].difference(outer_wall_reinforcement_edge);
if (mesh.getSettingAsCount("wall_reinforcement_line_count") > 0)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(outer_wall_reinforcement_edge.offset(-wall_line_width/2));
}
else
{
part.infill_area[0] = outer_wall_reinforcement_edge.offset(-wall_line_width/2);
}
// generate reinforcement wall extra walls
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() == 0)
{
continue;
}
generateWallReinforcementWallExtraWalls(&part, reinforcement_wall, wall_line_width, inset_count, mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (reinforcement_wall.wall_reinforcement_axtra_walls.size() > 0)
{
part.infill_area[0] = reinforcement_wall.wall_reinforcement_axtra_walls.back().offset(-wall_line_width/2); // update the infill area to one reinforcement wall insetted (updated each time a reinforcement wall is generated)
}
if (part.insets.size() > 0)
{
for(Polygons& polys : reinforcement_wall.wall_reinforcement_axtra_walls)
sendPolygons(SupportType, layer_nr, polys, wall_line_width);
}
}
}
}
@@ -393,17 +305,17 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
int wall_line_count = mesh.getSettingAsCount("wall_line_count");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = mesh.getSettingInMicrons("wall_line_width_x");
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
{
int infill_skin_overlap = 0;
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
{
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap, wall_line_count);
generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count);
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
@@ -413,20 +325,6 @@ void FffPolygonGenerator::processSkinsAndInfill(SliceDataStorage& storage, unsig
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
}
bool frontend_can_show_polygon_as_filled_polygon = false;
if (frontend_can_show_polygon_as_filled_polygon)
{
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
}
}
}
}
}
@@ -506,7 +404,6 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
Polygons skirt_sent = storage.skirt[0];
for (int extruder = 1; extruder < storage.meshgroup->getExtruderCount(); extruder++)
skirt_sent.add(storage.skirt[extruder]);
sendPolygons(SkirtType, 0, skirt_sent, getSettingInMicrons("skirt_line_width"));
}
@@ -567,7 +464,6 @@ void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
}
}
skin = results;
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
+2 -33
Ver Arquivo
@@ -9,6 +9,7 @@
#include "settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
#include "PrintFeature.h"
namespace cura
{
@@ -24,26 +25,14 @@ namespace cura
*/
class FffPolygonGenerator : public SettingsMessenger, NoCopy
{
private:
CommandSocket* commandSocket;
public:
/*!
* Basic constructor; doesn't set the FffAreaGenerator::commandSocket .
* Basic constructor
*/
FffPolygonGenerator(SettingsBase* settings_)
: SettingsMessenger(settings_)
, commandSocket(nullptr)
{
}
/*!
* Set the FffAreaGenerator::commandSocket
*/
void setCommandSocket(CommandSocket* socket)
{
commandSocket = socket;
}
/*!
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
@@ -55,17 +44,6 @@ public:
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* Send polygons over the command socket, if there is one.
* \param type The type of polygon to send
* \param layer_nr The layer number at which the polygons occur
* \param polygons The polygons to be sent
*/
void sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
if (commandSocket)
commandSocket->sendPolygons(type, layer_nr, polygons, line_width);
}
/*!
* Slice the \p object and store the outlines in the \p storage.
@@ -101,15 +79,6 @@ private:
*/
void processInsets(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Generate the wall reinforcement extra wall polygons and its infill area which form the reinforcement.
*
* Also redefines the infill area;
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
void processWallReinforcement(SliceDataStorage& storage, unsigned int layer_nr);
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
+11 -6
Ver Arquivo
@@ -11,6 +11,7 @@ std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_
std::stringstream sstream;
if (first_meshgroup)
{
sstream << getAllLocalSettingsString(); // global settings
sstream << " -g";
}
else
@@ -66,7 +67,7 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
if (meshgroup->meshes.empty())
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
@@ -78,11 +79,11 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
log("starting Neith Weaver...\n");
Weaver w(this);
w.weave(meshgroup, command_socket);
w.weave(meshgroup);
log("starting Neith Gcode generation...\n");
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
gcoder.writeGCode(command_socket);
gcoder.writeGCode();
log("finished Neith Gcode generation...\n");
} else
@@ -93,13 +94,17 @@ bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
return false;
}
gcode_writer.setCommandSocket(command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper, command_socket);
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
gcode_writer.writeGCode(storage, time_keeper);
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1, command_socket); //Report the GUI that a file has been fully processed.
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->endSendSlicedObject();
}
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, first_meshgroup);
-9
Ver Arquivo
@@ -26,7 +26,6 @@ private:
, gcode_writer(this)
, first_meshgroup(true)
{
command_socket = NULL;
}
public:
static FffProcessor* getInstance()
@@ -37,7 +36,6 @@ public:
private:
FffPolygonGenerator polygon_generator;
FffGcodeWriter gcode_writer;
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
bool first_meshgroup;
@@ -55,13 +53,6 @@ public:
gcode_writer.resetFileNumber();
}
void setCommandSocket(CommandSocket* socket)
{
command_socket = socket;
gcode_writer.setCommandSocket(socket);
polygon_generator.setCommandSocket(socket);
}
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
+6 -10
Ver Arquivo
@@ -14,12 +14,14 @@ void LayerPlanBuffer::flush()
{
insertPreheatCommands(); // insert preheat commands of the very last layer
}
for (GCodePlanner& layer_plan : buffer)
while (!buffer.empty())
{
layer_plan.writeGCode(gcode, getSettingBoolean("cool_lift_head"), layer_plan.getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->flushGcode();
buffer.pop_front();
}
}
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
@@ -218,10 +220,4 @@ void LayerPlanBuffer::insertPreheatCommands()
}
}
} // namespace cura
+3 -6
Ver Arquivo
@@ -17,8 +17,6 @@ namespace cura
class LayerPlanBuffer : SettingsMessenger
{
CommandSocket* command_socket;
GCodeExport& gcode;
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
@@ -29,9 +27,8 @@ class LayerPlanBuffer : SettingsMessenger
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, CommandSocket* command_socket, GCodeExport& gcode)
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
: SettingsMessenger(settings)
, command_socket(command_socket)
, gcode(gcode)
{ }
@@ -55,8 +52,8 @@ public:
if (buffer.size() > buffer_size)
{
buffer.front().writeGCode(gcode, getSettingBoolean("cool_lift_head"), buffer.front().getLayerNr() > 0 ? getSettingInMicrons("layer_height") : getSettingInMicrons("layer_height_0"));
if (command_socket)
command_socket->sendGCodeLayer();
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->flushGcode();
buffer.pop_front();
}
return buffer.back();
+112 -37
Ver Arquivo
@@ -2,6 +2,8 @@
#include <algorithm> // min
#include "utils/linearAlg2D.h"
namespace cura
{
@@ -12,6 +14,7 @@ void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath&
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());
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
@@ -51,38 +54,88 @@ bool MergeInfillLines::mergeInfillLines(double speed, unsigned int& path_idx)
return false;
};
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first)
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
int64_t max_line_width = nozzle_size * 3 / 2;
unsigned int idx = path_idx_first_move;
if (idx + 3 > paths.size()-1) return false;
if (paths[idx+0].config != &travelConfig) return false;
if (paths[idx+1].points.size() > 1) return false;
if (paths[idx+1].config == &travelConfig) return false;
// if (paths[idx+2].points.size() > 1) return false;
if (paths[idx+2].config != &travelConfig) return false;
if (paths[idx+3].points.size() > 1) return false;
if (paths[idx+3].config == &travelConfig) return false;
if (idx + 3 > paths.size()-1)
{
return false;
}
if ( paths[idx+0].config != &travelConfig // must be travel
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|| paths[idx+1].config == &travelConfig // must be extrusion
// || paths[idx+2].points.size() > 1 // travel must be direct
|| paths[idx+2].config != &travelConfig // must be travel
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|| paths[idx+3].config == &travelConfig // must be extrusion
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
)
{
return false;
}
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
return false;
}
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
{ // both extrusion moves must be of lines space filling type!
return false;
}
int64_t line_width = paths[idx+1].config->getLineWidth();
Point& a = paths[idx+0].points.back(); // first extruded line from
Point& b = paths[idx+1].points.back(); // first extruded line to
Point& c = paths[idx+2].points.back(); // second extruded line from
Point& d = paths[idx+3].points.back(); // second extruded line to
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
}
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
use_second_middle_as_first = false;
int64_t max_line_width = nozzle_size * 3 / 2;
Point ab = b - a;
Point cd = d - c;
if (b == c)
{
return false; // the line segments are connected!
}
int64_t ab_size = vSize(ab);
int64_t cd_size = vSize(cd);
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
{
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
}
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < vSize(ab) * vSize(cd)) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
{
return false; // extrusion moves not in the same or opposite diraction
if (prod < 0) { ab = ab * -1; }
}
Point infill_vector = (cd + ab) / 2;
if (!shorterThen(infill_vector, 5 * nozzle_size)) return false; // infill lines too far apart
// make lines in the same direction by flipping one
if (prod < 0)
{
ab = ab * -1;
}
else if (prod == 0)
{
return false; // lines are orthogonal!
}
else if (b == d || a == c)
{
return false; // the line segments are connected!
}
first_middle = (use_second_middle_as_first)?
second_middle :
(a + b) / 2;
@@ -90,25 +143,47 @@ bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& fi
Point dir_vector_perp = crossZ(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0) return false;
if (dir_vector_perp_length > 5 * nozzle_size) return false; // infill lines too far apart
line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (line_width > max_line_width) return false; // combined lines would be too wide
if (line_width == 0) return false; // dot is zero, so lines are in each others extension, not next to eachother
{ // check whether the two lines are adjacent
Point ca = first_middle - c;
double ca_size = vSizeMM(ca);
double cd_size = vSizeMM(cd);
double prod = INT2MM(dot(ca, cd));
double fraction = prod / ( ca_size * cd_size );
int64_t line2line_dist = MM2INT(cd_size * std::sqrt(1.0 - fraction * fraction));
if (line2line_dist + 20 > paths[idx+1].config->getLineWidth()) return false; // there is a gap between the two lines
if (dir_vector_perp_length == 0)
{
return false;
}
if (dir_vector_perp_length > 5 * nozzle_size)
{
return false; // infill lines too far apart
}
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
// compute the resulting line width
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (resulting_line_width > max_line_width)
{
return false; // combined lines would be too wide
}
if (resulting_line_width == 0)
{
return false; // dot is zero, so lines are in each others extension, not next to eachother
}
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
Point ac = c - first_middle;
Point infill_vector_perp = crossZ(infill_vector);
int64_t perp_proj = dot(ac, infill_vector_perp);
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
{
return false; // lines are too far apart or too close together
}
// check whether the two line segments are adjacent.
// full infill in a narrow area might result in line segments with arbitrary distance between them
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
{
return false;
}
return true;
};
+40 -7
Ver Arquivo
@@ -12,24 +12,42 @@ class MergeInfillLines
{
// void merge(Point& from, Point& p0, Point& p1);
GCodeExport& gcode; //!< Where to write the combined line to
int layer_nr; //!< The current layer number
std::vector<GCodePath>& paths; //!< The paths currently under consideration
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& line_width, bool use_second_middle_as_first);
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
*
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
*
* \param a first from
* \param b first to
* \param c second from
* \param d second to
* \param line_width The line width of the moves
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
*
@@ -43,8 +61,8 @@ public:
/*!
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
*/
MergeInfillLines(GCodeExport& gcode, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size)
: gcode(gcode), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size) { }
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) { }
/*!
* Check for lots of small moves and combine them into one large line.
@@ -60,6 +78,21 @@ public:
*/
bool mergeInfillLines(double speed, unsigned int& path_idx);
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, 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);
}
}
};
}//namespace cura
+40 -7
Ver Arquivo
@@ -64,26 +64,38 @@ bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
fseek(f, 0L, SEEK_END);
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
rewind(f); //Seek back to start.
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return false;
}
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
uint32_t reported_face_count;
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return false;
}
if (reported_face_count != face_count)
{
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
mesh->faces.reserve(faceCount);
mesh->vertices.reserve(faceCount);
for(unsigned int i=0;i<faceCount;i++)
mesh->faces.reserve(face_count);
mesh->vertices.reserve(face_count);
for (unsigned int i = 0; i < face_count; i++)
{
if (fread(buffer, 50, 1, f) != 1)
{
@@ -105,10 +117,31 @@ bool loadMeshSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
bool loadMeshSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
if (f == nullptr)
{
return false;
}
//Skip any whitespace at the beginning of the file.
unsigned long long num_whitespace = 0; //Number of whitespace characters.
unsigned char whitespace;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
while(isspace(whitespace))
{
num_whitespace++;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
}
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
char buffer[6];
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
+27 -15
Ver Arquivo
@@ -5,6 +5,7 @@
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "infill.h"
#include "PrintFeature.h"
namespace cura
{
@@ -22,7 +23,7 @@ void PrimeTower::initConfigs(MeshGroup* meshgroup, std::vector<RetractionConfig>
for (int extr = 0; extr < extruder_count; extr++)
{
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], "SUPPORT");// so that visualization in the old Cura still works (TODO)
config_per_extruder.emplace_back(&retraction_config_per_extruder[extr], PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
@@ -55,13 +56,14 @@ void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{ // compute max_object_height_per_extruder
for (SliceMeshStorage& mesh : storage.meshes)
{
max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
std::max( max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
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]
int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
max_object_height_per_extruder[support_extruder_nr] =
std::max( max_object_height_per_extruder[support_extruder_nr]
, 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] =
@@ -168,7 +170,7 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
double infill_overlap = 15; // so that it can't be zero
double infill_overlap = 15; // so that it can't be zero; EDIT: wtf?
generateGroundpoly(storage);
@@ -179,14 +181,22 @@ void PrimeTower::generatePaths3(SliceDataStorage& storage)
std::vector<Polygons>& patterns = patterns_per_extruder.back();
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
generateLineInfill(ground_poly, -line_width/2, patterns[pattern_idx], line_width, line_width, infill_overlap, 45 + pattern_idx*90);
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Polygons* in_between = nullptr;
bool avoidOverlappingPerimeters = false;
int outline_offset = -line_width/2;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons& result_lines = patterns[pattern_idx];
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, avoidOverlappingPerimeters, line_width, line_distance, infill_overlap, fill_angle);
infill_comp.generate(result_polygons, result_lines, in_between);
}
}
}
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
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)
{
if (!( storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
@@ -208,10 +218,10 @@ void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer,
{
wipe = false;
}
addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed, command_socket);
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, CommandSocket* command_socket)
void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
@@ -227,12 +237,14 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
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);
gcodeLayer.addLinesByOptimizer(pattern, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[new_extruder] = layer_nr;
if (command_socket)
command_socket->sendPolygons(SupportType, layer_nr, pattern, config.getLineWidth());
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::Support, layer_nr, pattern, config.getLineWidth());
}
if (wipe)
{ //Make sure we wipe the old extruder on the prime tower.
@@ -240,7 +252,7 @@ void PrimeTower::addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer
}
}
void PrimeTower::addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket)
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)
{
if (layer_nr > storage.max_object_height_second_to_last_extruder + 1)
{
+3 -3
Ver Arquivo
@@ -54,9 +54,9 @@ public:
PrimeTower();
void addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
void addToGcode_OLD(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
void addToGcode3(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed, CommandSocket* command_socket);
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);
};
+12 -13
Ver Arquivo
@@ -4,24 +4,23 @@
namespace cura
{
enum class EPrintFeature : unsigned int // unused!!
{ // TODO: use in gcodePathConfigs ?
OUTER_WALL,
INNER_WALLS,
INFILL,
SKIN,
HELPERS,
UNCLASSIFIED,
ENUM_COUNT
enum class PrintFeatureType
{
NoneType, // unused, but libArcus depends on it
OuterWall,
InnerWall,
Skin,
Support,
Skirt,
Infill,
SupportInfill,
MoveCombing,
MoveRetraction
};
} // namespace cura
#endif // PRINT_FEATURE
+6 -6
Ver Arquivo
@@ -64,22 +64,22 @@ void Progress::init()
total_timing = accumulated_time;
}
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* command_socket)
void Progress::messageProgress(Progress::Stage stage, int progress_in_stage, int progress_in_stage_max)
{
float percentage = calcOverallProgress(stage, float(progress_in_stage) / float(progress_in_stage_max));
if (command_socket)
if (CommandSocket::getInstance())
{
command_socket->sendProgress(percentage);
CommandSocket::getInstance()->sendProgress(percentage);
}
logProgress(names[(int)stage].c_str(), progress_in_stage, progress_in_stage_max, percentage);
}
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper, CommandSocket* command_socket)
void Progress::messageProgressStage(Progress::Stage stage, TimeKeeper* time_keeper)
{
if (command_socket)
if (CommandSocket::getInstance())
{
command_socket->sendProgressStage(stage);
CommandSocket::getInstance()->sendProgressStage(stage);
}
if (time_keeper)
+3 -5
Ver Arquivo
@@ -52,22 +52,20 @@ private:
public:
static void init(); //!< Initialize some values needed in a fast computation of the progress
/*!
* Message progress over the \p commandSocket and to the terminal (if the command line arg '-p' is provided).
* Message progress over the CommandSocket and to the terminal (if the command line arg '-p' is provided).
*
* \param stage The current stage of processing
* \param progress_in_stage Any number giving the progress within the stage
* \param progress_in_stage_max The maximal value of \p progress_in_stage
* \param commandSocket The command socket over which to communicate the progress.
*/
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max, CommandSocket* commandSocket);
static void messageProgress(Stage stage, int progress_in_stage, int progress_in_stage_max);
/*!
* Message the progress stage over the command socket.
*
* \param stage The current stage
* \param timeKeeper The stapwatch keeping track of the timings for each stage (optional)
* \param commandSocket The command socket over which to communicate (optional)
*/
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper, CommandSocket* commandSocket);
static void messageProgressStage(Stage stage, TimeKeeper* timeKeeper);
};
+25
Ver Arquivo
@@ -0,0 +1,25 @@
#ifndef SPACE_FILL_TYPE
#define SPACE_FILL_TYPE
namespace cura
{
/*!
* Enum class enumerating the strategies with which an area can be occupied with filament
*
* The walls/perimeters are Polygons
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
* Grid, Triangles and lines infill is Lines
*/
enum class SpaceFillType
{
None,
Polygons,
PolyLines,
Lines
};
} // namespace cura
#endif // SPACE_FILL_TYPE
+10 -9
Ver Arquivo
@@ -6,11 +6,12 @@
#include "Progress.h"
#include "weaveDataStorage.h"
#include "PrintFeature.h"
namespace cura
{
void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
void Weaver::weave(MeshGroup* meshgroup)
{
wireFrame.meshgroup = meshgroup;
@@ -52,8 +53,8 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
for (cura::Slicer* slicer : slicerList)
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, 0, wireFrame.bottom_outline, 1);
if (slicerList.empty()) //Wait, there is nothing to slice.
{
@@ -70,10 +71,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
else
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::INSET, nullptr);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::INSET, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
@@ -84,8 +85,8 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
if (CommandSocket::isInstantiated())
CommandSocket::getInstance()->sendPolygons(PrintFeatureType::OuterWall, layer_idx - starting_layer_idx, chainified, 1);
if (chainified.size() > 0)
{
@@ -107,10 +108,10 @@ void Weaver::weave(MeshGroup* meshgroup, CommandSocket* commandSocket)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::SKIN, nullptr);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size(), commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::SKIN, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
+1 -2
Ver Arquivo
@@ -61,9 +61,8 @@ public:
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
*
* \param objects The objects for which to create a wireframe print
* \param commandSocket the commandSocket
*/
void weave(MeshGroup* objects, CommandSocket* commandSocket);
void weave(MeshGroup* objects);
private:
+10 -16
Ver Arquivo
@@ -12,15 +12,15 @@ namespace cura
{
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
void Wireframe2gcode::writeGCode()
{
gcode.preSetup(wireFrame.meshgroup);
if (commandSocket)
commandSocket->beginGCode();
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
processStartingCode(commandSocket);
processStartingCode();
int maxObjectHeight;
if (wireFrame.layers.empty())
@@ -32,7 +32,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
maxObjectHeight = wireFrame.layers.back().z1;
}
processSkirt(commandSocket);
processSkirt();
unsigned int total_layers = wireFrame.layers.size();
@@ -76,10 +76,10 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_nr];
@@ -166,12 +166,6 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
gcode.writeFanCommand(0);
finalize();
if (commandSocket)
{
commandSocket->sendGCodeLayer();
commandSocket->endSendSlicedObject();
}
}
@@ -550,11 +544,11 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
void Wireframe2gcode::processStartingCode()
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
if (!command_socket)
if (!CommandSocket::isInstantiated())
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
}
@@ -595,7 +589,7 @@ void Wireframe2gcode::processStartingCode(CommandSocket* command_socket)
}
void Wireframe2gcode::processSkirt(CommandSocket* commandSocket)
void Wireframe2gcode::processSkirt()
{
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
{
+3 -3
Ver Arquivo
@@ -71,7 +71,7 @@ public:
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
void writeGCode(CommandSocket* commandSocket);
void writeGCode();
private:
@@ -80,12 +80,12 @@ private:
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
*/
void processStartingCode(CommandSocket* command_socket);
void processStartingCode();
/*!
* Lay down a skirt
*/
void processSkirt(CommandSocket* commandSocket);
void processSkirt();
/*!
* End gcode: nozzle temp down
+133 -119
Ver Arquivo
@@ -6,9 +6,7 @@
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#endif
#include <string> // stoi
@@ -25,7 +23,8 @@ namespace cura {
#define FLOATS_PER_VECTOR 3
#define VECTORS_PER_FACE 3
#ifdef ARCUS
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
class CommandSocket::Private
{
public:
@@ -68,64 +67,59 @@ public:
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
};
#endif
CommandSocket::CommandSocket()
#ifdef ARCUS
: d(new Private)
#endif
: private_data(new Private)
{
#ifdef ARCUS
FffProcessor::getInstance()->setCommandSocket(this);
#endif
}
CommandSocket* CommandSocket::getInstance()
{
return instance;
}
void CommandSocket::instantiate()
{
instance = new CommandSocket();
}
bool CommandSocket::isInstantiated()
{
return instance != nullptr;
}
void CommandSocket::connect(const std::string& ip, int port)
{
#ifdef ARCUS
d->socket = new Arcus::Socket();
//d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(1, &cura::proto::Slice::default_instance());
d->socket->registerMessageType(2, &cura::proto::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &cura::proto::Progress::default_instance());
d->socket->registerMessageType(4, &cura::proto::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &cura::proto::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &cura::proto::SettingList::default_instance());
d->socket->registerMessageType(7, &cura::proto::GCodePrefix::default_instance());
private_data->socket = new Arcus::Socket();
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicedObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::ObjectPrintTime::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
d->socket->connect(ip, port);
private_data->socket->connect(ip, port);
log("Connecting to %s:%i", ip.c_str(), port);
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
log("Connected to %s:%i", ip.c_str(), port);
bool slice_another_time = true;
// Start & continue listening as long as socket is not closed and there is no error.
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error && slice_another_time)
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
{
//If there is an object to slice, do so.
if(d->objects_to_slice.size())
{
FffProcessor::getInstance()->resetFileNumber();
for(auto object : d->objects_to_slice)
{
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
d->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
sendGCodeLayer();
sendPrintTime();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//d->processor->processModel(d->object_to_slice.get());
//d->object_to_slice.reset();
//d->processor->resetFileNumber();
//sendPrintTime();
}
// Actually start handling messages.
Arcus::MessagePtr message = d->socket->takeNextMessage();
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if(setting_list)
{
@@ -142,33 +136,63 @@ void CommandSocket::connect(const std::string& ip, int port)
if(slice)
{
// Reset object counts
d->object_count = 0;
d->object_ids.clear();
private_data->object_count = 0;
private_data->object_ids.clear();
for(auto object : slice->object_lists())
{
handleObjectList(&object);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if(!d->socket->errorString().empty())
//If there is an object to slice, do so.
if(private_data->objects_to_slice.size())
{
logError("%s\n", d->socket->errorString().data());
d->socket->clearError();
FffProcessor::getInstance()->resetFileNumber();
for(auto object : private_data->objects_to_slice)
{
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
private_data->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
flushGcode();
sendPrintTime();
sendFinishedSlicing();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//private_data->processor->processModel(private_data->object_to_slice.get());
//private_data->object_to_slice.reset();
//private_data->processor->resetFileNumber();
//sendPrintTime();
}
if(private_data->socket->getLastError().isValid())
{
logError("%s\n", private_data->socket->getLastError().toString().c_str());
private_data->socket->clearError();
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
#endif
private_data->socket->close();
}
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
{
if(list->objects_size() <= 0)
{
return;
}
FMatrix3x3 matrix;
//d->object_count = 0;
//d->object_ids.clear();
d->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = d->objects_to_slice.back().get();
//private_data->object_count = 0;
//private_data->object_ids.clear();
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
for(auto setting : list->settings())
{
@@ -182,6 +206,14 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
for(auto object : list->objects())
{
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
if(face_count <= 0)
{
logWarning("Got an empty mesh, ignoring it!");
continue;
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
int extruder_train_nr = 0; // TODO: make primary extruder configurable!
for(auto setting : object.settings())
@@ -197,8 +229,6 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = meshgroup->meshes.back();
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
for(int i = 0; i < face_count; ++i)
{
//TODO: Apply matrix
@@ -225,11 +255,11 @@ void CommandSocket::handleObjectList(cura::proto::ObjectList* list)
mesh.setSetting(setting.name(), setting.value());
}
d->object_ids.push_back(object.id());
private_data->object_ids.push_back(object.id());
mesh.finish();
}
d->object_count++;
private_data->object_count++;
meshgroup->finalize();
}
@@ -240,54 +270,47 @@ void CommandSocket::handleSettingList(cura::proto::SettingList* list)
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
}
#endif
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
#ifdef ARCUS
if(!d->current_sliced_object)
if(!private_data->current_sliced_object)
{
return;
}
cura::proto::Layer* layer = d->getLayerById(layer_nr);
cura::proto::Layer* layer = private_data->getLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
#endif
}
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
void CommandSocket::sendPolygons(PrintFeatureType type, int layer_nr, Polygons& polygons, int line_width)
{
#ifdef ARCUS
if(!d->current_sliced_object)
if(!private_data->current_sliced_object)
return;
if (polygons.size() == 0)
return;
cura::proto::Layer* layer = d->getLayerById(layer_nr);
cura::proto::Layer* proto_layer = private_data->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
cura::proto::Polygon* p = layer->add_polygons();
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);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::Progress>();
amount /= d->object_count;
amount += d->sliced_objects * (1. / d->object_count);
amount /= private_data->object_count;
amount += private_data->sliced_objects * (1. / private_data->object_count);
message->set_amount(amount);
d->socket->sendMessage(message);
#endif
private_data->socket->sendMessage(message);
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
@@ -297,12 +320,10 @@ void CommandSocket::sendProgressStage(Progress::Stage stage)
void CommandSocket::sendPrintTime()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::ObjectPrintTime>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(0));
d->socket->sendMessage(message);
#endif
private_data->socket->sendMessage(message);
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -316,68 +337,62 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
void CommandSocket::beginSendSlicedObject()
{
#ifdef ARCUS
if(!d->sliced_object_list)
if(!private_data->sliced_object_list)
{
d->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
private_data->sliced_object_list = std::make_shared<cura::proto::SlicedObjectList>();
}
d->current_sliced_object = d->sliced_object_list->add_objects();
d->current_sliced_object->set_id(d->object_ids[d->sliced_objects]);
#endif
private_data->current_sliced_object = private_data->sliced_object_list->add_objects();
private_data->current_sliced_object->set_id(private_data->object_ids[private_data->sliced_objects]);
}
void CommandSocket::endSendSlicedObject()
{
#ifdef ARCUS
d->sliced_objects++;
d->current_layer_offset = d->current_layer_count;
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
private_data->sliced_objects++;
private_data->current_layer_offset = private_data->current_layer_count;
std::cout << "End sliced object called. Sliced objects " << private_data->sliced_objects << " object count: " << private_data->object_count << std::endl;
std::cout << "current layer count" << d->current_layer_count << std::endl;
std::cout << "current layer offset" << d->current_layer_offset << std::endl;
if(d->sliced_objects >= d->object_count)
if(private_data->sliced_objects >= private_data->object_count)
{
d->socket->sendMessage(d->sliced_object_list);
d->sliced_objects = 0;
d->current_layer_count = 0;
d->current_layer_offset = 0;
d->sliced_object_list.reset();
d->current_sliced_object = nullptr;
private_data->socket->sendMessage(private_data->sliced_object_list);
private_data->sliced_objects = 0;
private_data->current_layer_count = 0;
private_data->current_layer_offset = 0;
private_data->sliced_object_list.reset();
private_data->current_sliced_object = nullptr;
auto done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
}
#endif
}
void CommandSocket::sendFinishedSlicing()
{
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
}
void CommandSocket::beginGCode()
{
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&d->gcode_output_stream);
#endif
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
}
void CommandSocket::sendGCodeLayer()
void CommandSocket::flushGcode()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodeLayer>();
message->set_id(d->object_ids[0]);
message->set_data(d->gcode_output_stream.str());
d->socket->sendMessage(message);
message->set_id(private_data->object_ids[0]);
message->set_data(private_data->gcode_output_stream.str());
private_data->socket->sendMessage(message);
d->gcode_output_stream.str("");
#endif
private_data->gcode_output_stream.str("");
}
void CommandSocket::sendGCodePrefix(std::string prefix)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
message->set_data(prefix);
d->socket->sendMessage(message);
#endif
private_data->socket->sendMessage(message);
}
#ifdef ARCUS
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
@@ -398,6 +413,5 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
return layer;
}
#endif
}//namespace cura
+44 -15
Ver Arquivo
@@ -5,20 +5,29 @@
#include "utils/polygon.h"
#include "settings.h"
#include "Progress.h"
#include "PrintFeature.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura
{
namespace cura {
class CommandSocket
{
private:
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
public:
CommandSocket();
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
static void instantiate(); //!< Instantiate the CommandSocket.
static bool isInstantiated(); //!< Check whether the singleton is instantiated
/*!
* Connect with the GUI
* This creates and initialises the arcus socket and then continues listening for messages.
@@ -26,8 +35,7 @@ public:
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
@@ -39,18 +47,22 @@ public:
* This simply sets all the settings by using key value pair
*/
void handleSettingList(cura::proto::SettingList* list);
#endif
/*!
* Does nothing at the moment
* Send info on a 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::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
void sendPolygons(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send a polygon to the engine if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
*/
@@ -71,18 +83,35 @@ public:
*/
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
/*!
* Start the slicing of a new meshgroup
*/
void beginSendSlicedObject();
/*!
* Conclude the slicing of the current meshgroup, so that we can start the next
*/
void endSendSlicedObject();
/*!
* \brief Sends a message to indicate that all the slicing is done.
*
* This should indicate that no more data (g-code, prefix/postfix, metadata
* or otherwise) should be sent any more regarding the latest slice job.
*/
void sendFinishedSlicing();
void beginGCode();
void sendGCodeLayer();
/*!
* Flush the gcode in gcode_output_stream into a message queued in the socket.
*/
void flushGcode();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> d;
#endif
const std::unique_ptr<Private> private_data;
};
}//namespace cura
+41 -7
Ver Arquivo
@@ -5,13 +5,13 @@
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "PrintFeature.h"
namespace cura {
GCodeExport::GCodeExport()
: output_stream(&std::cout)
, currentPosition(0,0,MM2INT(20))
, commandSocket(nullptr)
, layer_nr(0)
{
current_e_value = 0;
@@ -29,8 +29,7 @@ GCodeExport::~GCodeExport()
{
}
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
commandSocket = commandSocket_;
void GCodeExport::setLayerNr(unsigned int layer_nr_) {
layer_nr = layer_nr_;
}
@@ -184,6 +183,41 @@ void GCodeExport::writeTypeComment(const char* type)
{
*output_stream << ";TYPE:" << type << "\n";
}
void GCodeExport::writeTypeComment(PrintFeatureType type)
{
switch (type)
{
case PrintFeatureType::OuterWall:
*output_stream << ";TYPE:WALL-OUTER\n";
break;
case PrintFeatureType::InnerWall:
*output_stream << ";TYPE:WALL-INNER\n";
break;
case PrintFeatureType::Skin:
*output_stream << ";TYPE:SKIN\n";
break;
case PrintFeatureType::Support:
*output_stream << ";TYPE:SUPPORT\n";
break;
case PrintFeatureType::Skirt:
*output_stream << ";TYPE:SKIRT\n";
break;
case PrintFeatureType::Infill:
*output_stream << ";TYPE:FILL\n";
break;
case PrintFeatureType::SupportInfill:
*output_stream << ";TYPE:SUPPORT\n";
break;
case PrintFeatureType::MoveCombing:
case PrintFeatureType::MoveRetraction:
default:
// do nothing
break;
}
}
void GCodeExport::writeLayerComment(int layer_nr)
{
*output_stream << ";LAYER:" << layer_nr << "\n";
@@ -366,16 +400,16 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
else
{
*output_stream << "G0";
if (commandSocket)
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->sendPolygons(extruder_attr[current_extruder].retraction_e_amount_current ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, extruder_attr[current_extruder].retraction_e_amount_current ? MM2INT(0.2) : MM2INT(0.1));
}
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));
}
}
if (currentSpeed != speed)
+19 -18
Ver Arquivo
@@ -18,13 +18,9 @@ namespace cura {
struct CoastingConfig
{
bool coasting_enable;
double coasting_volume_move;
double coasting_speed_move;
double coasting_min_volume_move;
double coasting_volume_retract;
double coasting_speed_retract;
double coasting_min_volume_retract;
double coasting_volume;
double coasting_speed;
double coasting_min_volume;
};
class RetractionConfig
@@ -44,19 +40,19 @@ public:
class GCodePathConfig
{
private:
double speed_base; //!< movement speed (mm/s) specific to this print feature
double speed_current; //!< current movement speed (mm/s) (modified by layer_nr etc.)
double speed_iconic; //!< movement speed (mm/s) specific to this print feature
double speed; //!< current movement speed (mm/s) (modified by layer_nr etc.)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow in %
int layer_thickness; //!< layer height
double extrusion_mm3_per_mm;//!< mm^3 filament moved per mm line extruded
public:
const char* name; //!< name of the feature type
PrintFeatureType type; //!< name of the feature type
bool spiralize;
RetractionConfig *const retraction_config;
// GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed_base(0), speed_current(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
GCodePathConfig(RetractionConfig* retraction_config, PrintFeatureType type) : speed_iconic(0), speed(0), line_width(0), extrusion_mm3_per_mm(0.0), type(type), spiralize(false), retraction_config(retraction_config) {}
/*!
* Initialize some of the member variables.
@@ -65,8 +61,8 @@ public:
*/
void init(double speed, int line_width, double flow)
{
speed_base = speed;
this->speed_current = speed;
speed_iconic = speed;
this->speed = speed;
this->line_width = line_width;
this->flow = flow;
}
@@ -91,7 +87,7 @@ public:
*/
void smoothSpeed(double min_speed, int layer_nr, double max_speed_layer)
{
speed_current = (speed_base*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
speed = (speed_iconic*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
/*!
@@ -107,7 +103,7 @@ public:
*/
double getSpeed()
{
return speed_current;
return speed;
}
int getLineWidth()
@@ -120,6 +116,11 @@ public:
return line_width == 0;
}
double getFlowPercentage()
{
return flow;
}
private:
void calculateExtrusion()
{
@@ -191,8 +192,7 @@ private:
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
CommandSocket* commandSocket; //!< for sending travel data
unsigned int layer_nr; //!< for sending travel data
public:
@@ -200,7 +200,7 @@ public:
GCodeExport();
~GCodeExport();
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
void setLayerNr(unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
@@ -238,6 +238,7 @@ public:
void writeComment(std::string comment);
void writeTypeComment(const char* type);
void writeTypeComment(PrintFeatureType type);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
+74 -88
Ver Arquivo
@@ -22,17 +22,18 @@ TimeMaterialEstimates& TimeMaterialEstimates::operator-=(const TimeMaterialEstim
return *this;
}
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, float flow)
GCodePath* GCodePlanner::getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
{
std::vector<GCodePath>& paths = extruder_plans.back().paths;
if (paths.size() > 0 && paths[paths.size()-1].config == config && !paths[paths.size()-1].done && paths[paths.size()-1].flow == flow)
return &paths[paths.size()-1];
paths.push_back(GCodePath());
GCodePath* ret = &paths[paths.size()-1];
if (paths.size() > 0 && paths.back().config == config && !paths.back().done && paths.back().flow == flow)
return &paths.back();
paths.emplace_back();
GCodePath* ret = &paths.back();
ret->retract = false;
ret->config = config;
ret->done = false;
ret->flow = flow;
ret->space_fill_type = space_fill_type;
if (config != &storage.travel_config)
{
last_retraction_config = config->retraction_config;
@@ -47,9 +48,8 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_thickness, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, commandSocket(commandSocket)
, layer_nr(layer_nr)
, z(z)
, layer_thickness(layer_thickness)
@@ -105,7 +105,6 @@ void GCodePlanner::setIsInside(bool _is_inside)
is_inside = _is_inside;
}
bool GCodePlanner::setExtruder(int extruder)
{
if (extruder == extruder_plans.back().extruder)
@@ -115,6 +114,7 @@ bool GCodePlanner::setExtruder(int extruder)
{ // handle end position of the prev extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder_plans.back().extruder);
bool end_pos_absolute = train->getSettingBoolean("machine_extruder_end_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point end_pos(train->getSettingInMicrons("machine_extruder_end_pos_x"), train->getSettingInMicrons("machine_extruder_end_pos_y"));
if (!end_pos_absolute)
{
@@ -122,25 +122,18 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
end_pos += extruder_offset; // absolute end pos is given as a head position
}
addTravel(end_pos); // + extruder_offset cause it
}
if (extruder_plans.back().paths.empty() && extruder_plans.back().inserts.empty())
{ // first extruder plan in a layer might be empty, cause it is made with the last extruder planned in the previous layer
extruder_plans.back().extruder = extruder;
}
else
{
extruder_plans.emplace_back(extruder);
}
extruder_plans.emplace_back(extruder);
// forceNewPathStart(); // automatic by the fact that we start a new ExtruderPlan
{ // handle starting pos of the new extruder
SettingsBase* train = storage.meshgroup->getExtruderTrain(extruder);
bool start_pos_absolute = train->getSettingBoolean("machine_extruder_start_pos_abs");
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
Point start_pos(train->getSettingInMicrons("machine_extruder_start_pos_x"), train->getSettingInMicrons("machine_extruder_start_pos_y"));
if (!start_pos_absolute)
{
@@ -148,7 +141,6 @@ bool GCodePlanner::setExtruder(int extruder)
}
else
{
Point extruder_offset(train->getSettingInMicrons("machine_nozzle_offset_x"), train->getSettingInMicrons("machine_nozzle_offset_y"));
start_pos += extruder_offset; // absolute start pos is given as a head position
}
lastPosition = start_pos;
@@ -158,13 +150,13 @@ bool GCodePlanner::setExtruder(int extruder)
void GCodePlanner::moveInsideCombBoundary(int distance)
{
int max_dist = MM2INT(2.0); // if we are further than this distance, we conclude we are not inside even though we thought we were.
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
Point p = lastPosition; // copy, since we are going to move p
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist) != NO_INDEX)
if (PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2) != NO_INDEX)
{
//Move inside again, so we move out of tight 90deg corners
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist);
PolygonUtils::moveInside(comb_boundary_inside, p, distance, max_dist2);
if (comb_boundary_inside.inside(p))
{
addTravel_simple(p);
@@ -187,21 +179,29 @@ void GCodePlanner::addTravel(Point p)
if (combed)
{
bool retract = combPaths.size() > 1;
if (!retract)
{ // check whether we want to retract
if (!retract && combPaths.size() == 1 && combPaths[0].throughAir && combPaths[0].size() > 2)
{ // retract when avoiding obstacles through air
retract = true;
}
for (unsigned int path_idx = 0; path_idx < combPaths.size() && !retract; path_idx++)
for (CombPath& combPath : combPaths)
{ // retract when path moves through a boundary
if (combPaths[path_idx].cross_boundary) { retract = true; }
if (combPath.cross_boundary || combPath.throughAir)
{
retract = true;
break;
}
}
if (combPaths.size() == 1)
{
CombPath path = combPaths[0];
if (path.throughAir && !path.cross_boundary && path.size() == 2 && path[0] == lastPosition && path[1] == p)
{ // limit the retractions from support to support, which didn't cross anything
retract = false;
}
}
}
if (retract && last_retraction_config->zHop > 0)
{ // TODO: stop comb calculation early! (as soon as we see we don't end in the same part as we began)
path = getLatestPathWithConfig(&storage.travel_config);
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
@@ -215,7 +215,7 @@ void GCodePlanner::addTravel(Point p)
{
continue;
}
path = getLatestPathWithConfig(&storage.travel_config);
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path->retract = retract;
for (Point& combPoint : combPath)
{
@@ -231,13 +231,13 @@ void GCodePlanner::addTravel(Point p)
// no combing? always retract!
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
if (was_inside)
{
if (was_inside) // when the previous location was from printing something which is considered inside (not support or prime tower etc)
{ // then move inside the printed part, so that we don't ooze on the outer wall while retraction, but on the inside of the print.
ExtruderTrain* extr = storage.meshgroup->getExtruderTrain(getExtruder());
assert (extr != nullptr);
moveInsideCombBoundary(extr->getSettingInMicrons("machine_nozzle_size") * 1);
}
path = getLatestPathWithConfig(&storage.travel_config);
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
path->retract = true;
}
}
@@ -250,16 +250,16 @@ void GCodePlanner::addTravel_simple(Point p, GCodePath* path)
{
if (path == nullptr)
{
path = getLatestPathWithConfig(&storage.travel_config);
path = getLatestPathWithConfig(&storage.travel_config, SpaceFillType::None);
}
path->points.push_back(p);
lastPosition = p;
}
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, float flow)
void GCodePlanner::addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow)
{
getLatestPathWithConfig(config, flow)->points.push_back(p);
getLatestPathWithConfig(config, space_fill_type, flow)->points.push_back(p);
lastPosition = p;
}
@@ -270,13 +270,17 @@ void GCodePlanner::addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig*
for(unsigned int i=1; i<polygon.size(); i++)
{
Point p1 = polygon[(startIdx + i) % polygon.size()];
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
p0 = p1;
}
if (polygon.size() > 2)
{
Point& p1 = polygon[startIdx];
addExtrusionMove(p1, config, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
addExtrusionMove(p1, config, SpaceFillType::Polygons, (wall_overlap_computation)? wall_overlap_computation->getFlow(p0, p1) : 1.0);
}
else
{
logWarning("WARNING: line added as polygon! (gcodePlanner)\n");
}
}
@@ -292,9 +296,9 @@ void GCodePlanner::addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* c
addPolygon(polygons[nr], orderOptimizer.polyStart[nr], config, wall_overlap_computation);
}
}
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist)
void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist)
{
LineOrderOptimizer orderOptimizer(lastPosition);
LineOrderOptimizer orderOptimizer(lastPosition, storage.getSettingInMicrons("infill_line_distance")); //Use infill line distance to make adjacent infill lines fall in the same cluster.
for(unsigned int i=0;i<polygons.size();i++)
orderOptimizer.addPolygon(polygons[i]);
orderOptimizer.optimize();
@@ -308,13 +312,13 @@ void GCodePlanner::addLinesByOptimizer(Polygons& polygons, GCodePathConfig* conf
Point& p0 = polygon[start];
addTravel(p0);
Point& p1 = polygon[end];
addExtrusionMove(p1, config);
addExtrusionMove(p1, config, space_fill_type);
if (wipe_dist != 0)
{
int line_width = config->getLineWidth();
if (vSize2(p1-p0) > line_width * line_width * 4)
{ // otherwise line will get optimized by combining multiple into a single extrusion move
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, 0.0);
addExtrusionMove(p1 + normal(p1-p0, wipe_dist), config, space_fill_type, 0.0);
}
}
}
@@ -466,7 +470,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{
completeConfigs();
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
gcode.setLayerNr(layer_nr);
gcode.writeLayerComment(layer_nr);
@@ -502,7 +506,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
}
if (path.config != &storage.travel_config && last_extrusion_config != path.config)
{
gcode.writeTypeComment(path.config->name);
gcode.writeTypeComment(path.config->type);
last_extrusion_config = path.config;
}
double speed = path.config->getSpeed();
@@ -511,10 +515,10 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
speed *= getTravelSpeedFactor();
else
speed *= getExtrudeSpeedFactor();
int64_t nozzle_size = 400; // TODO allow the machine settings to be passed on everywhere :: depends on which nozzle!
if (MergeInfillLines(gcode, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
int64_t nozzle_size = 400; // TODO
if (MergeInfillLines(gcode, layer_nr, paths, extruder_plan, storage.travel_config, nozzle_size).mergeInfillLines(speed, path_idx)) // !! has effect on path_idx !!
{ // !! has effect on path_idx !!
// works when path_idx is the index of the travel move BEFORE the infill lines to be merged
continue;
@@ -545,13 +549,11 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
bool coasting = coasting_config.coasting_enable;
if (coasting)
{
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness
, coasting_config.coasting_volume_move, coasting_config.coasting_speed_move, coasting_config.coasting_min_volume_move
, coasting_config.coasting_volume_retract, coasting_config.coasting_speed_retract, coasting_config.coasting_min_volume_retract);
coasting = writePathWithCoasting(gcode, extruder_plan_idx, path_idx, layerThickness, coasting_config.coasting_volume, coasting_config.coasting_speed, coasting_config.coasting_min_volume);
}
if (! coasting) // not same as 'else', cause we might have changed [coasting] in the line above...
{ // normal path to gcode algorithm
if ( // change |||||| to /\/\/\/\/ ...
if ( // change infill |||||| to /\/\/\/\/ ...
false &&
path_idx + 2 < paths.size() // has a next move
&& paths[path_idx+1].points.size() == 1 // is single extruded line
@@ -562,6 +564,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
&& 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());
gcode.writeMove(paths[path_idx+2].points.back(), speed, paths[path_idx+1].getExtrusionMM3perMM());
path_idx += 2;
}
@@ -569,6 +572,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
{
for(unsigned int point_idx = 0; point_idx < path.points.size(); point_idx++)
{
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
@@ -595,6 +599,7 @@ void GCodePlanner::writeGCode(GCodeExport& gcode, bool liftHeadIfNeeded, int lay
length += vSizeMM(p0 - p1);
p0 = p1;
gcode.setZ(z + layerThickness * length / totalLength);
sendPolygon(path.config->type, gcode.getPositionXY(), path.points[point_idx], path.getLineWidth());
gcode.writeMove(path.points[point_idx], speed, path.getExtrusionMM3perMM());
}
}
@@ -627,10 +632,9 @@ void GCodePlanner::completeConfigs()
for (SliceMeshStorage& mesh : storage.meshes)
{
mesh.inset0_config.setLayerHeight(layer_thickness);
mesh.insetX_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
mesh.skin_config.setLayerHeight(layer_thickness);
mesh.wall_reinforcement_config.setLayerHeight(layer_thickness);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].setLayerHeight(layer_thickness);
@@ -656,9 +660,7 @@ void GCodePlanner::processInitialLayersSpeedup()
initial_layer_speed = mesh.getSettingInMillimetersPerSecond("speed_layer_0");
mesh.inset0_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.insetX_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.skin_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
mesh.wall_reinforcement_config.smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
for(unsigned int idx=0; idx<MAX_INFILL_COMBINE; idx++)
{
mesh.infill_config[idx].smoothSpeed(initial_layer_speed, layer_nr, initial_speedup_layers);
@@ -735,8 +737,12 @@ bool GCodePlanner::makeRetractSwitchRetract(GCodeExport& gcode, unsigned int ext
}
}
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract)
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume)
{
if (coasting_volume <= 0)
{
return false;
}
std::vector<GCodePath>& paths = extruder_plans[extruder_plan_idx].paths;
GCodePath& path = paths[path_idx];
if (path_idx + 1 >= paths.size()
@@ -748,21 +754,6 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, unsigned int extrud
{
return false;
}
GCodePath& path_next = paths[path_idx + 1];
if (path_next.retract)
{
if (coasting_volume_retract <= 0) { return false; }
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_retract, coasting_speed_retract, coasting_min_volume_retract, makeRetractSwitchRetract(gcode, extruder_plan_idx, path_idx));
}
else
{
if (coasting_volume_move <= 0) { return false; }
return writePathWithCoasting(gcode, path, path_next, layerThickness, coasting_volume_move, coasting_speed_move, coasting_min_volume_move);
}
}
bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract)
{
int64_t coasting_min_dist_considered = 100; // hardcoded setting for when to not perform coasting
@@ -770,7 +761,8 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
double extrude_speed = path.config->getSpeed() * getExtrudeSpeedFactor(); // travel speed
int64_t coasting_dist = MM2INT(MM2_2INT(coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
int64_t coasting_min_dist = MM2INT(MM2_2INT(coasting_min_volume + coasting_volume) / layerThickness) / path.config->getLineWidth(); // closing brackets of MM2INT at weird places for precision issues
// /\ the minimal distance when coasting will coast the full coasting volume instead of linearly less with linearly smaller paths
std::vector<int64_t> accumulated_dist_per_point; // the first accumulated dist is that of the last point! (that of the last point is always zero...)
@@ -823,14 +815,11 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
}
}
}
if (acc_dist_idx_gt_coast_dist == NO_INDEX)
{ // something has gone wrong; coasting_min_dist < coasting_dist ?
return false;
}
assert (acc_dist_idx_gt_coast_dist < accumulated_dist_per_point.size()); // something has gone wrong; coasting_min_dist < coasting_dist ?
unsigned int point_idx_before_start = path.points.size() - 1 - acc_dist_idx_gt_coast_dist;
Point start;
{ // computation of begin point of coasting
int64_t residual_dist = actual_coasting_dist - accumulated_dist_per_point[acc_dist_idx_gt_coast_dist - 1];
@@ -838,27 +827,24 @@ bool GCodePlanner::writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GC
Point& b = path.points[point_idx_before_start + 1];
start = b + normal(a-b, residual_dist);
}
{ // 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());
gcode.writeMove(path.points[point_idx], extrude_speed, path.getExtrusionMM3perMM());
}
sendPolygon(path.config->type, gcode.getPositionXY(), start, path.getLineWidth());
gcode.writeMove(start, extrude_speed, path.getExtrusionMM3perMM());
}
if (path_next.retract)
{
writeRetraction(gcode, extruder_switch_retract, path.config->retraction_config);
}
// write coasting path
for (unsigned int point_idx = point_idx_before_start + 1; point_idx < path.points.size(); point_idx++)
{
gcode.writeMove(path.points[point_idx], coasting_speed * path.config->getSpeed(), 0);
}
gcode.addLastCoastedVolume(path.getExtrusionMM3perMM() * INT2MM(actual_coasting_dist));
return true;
}
+46 -33
Ver Arquivo
@@ -10,6 +10,7 @@
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
namespace cura
@@ -150,6 +151,7 @@ class GCodePath
{
public:
GCodePathConfig* config; //!< The configuration settings of the path.
SpaceFillType space_fill_type; //!< The type of space filling of which this path is a part
float flow; //!< A type-independent flow configuration (used for wall overlap compensation)
bool retract; //!< Whether the path is a move path preceded by a retraction move; whether the path is a retracted move path.
std::vector<Point> points; //!< The points constituting this path.
@@ -169,6 +171,15 @@ public:
{
return flow * config->getExtrusionMM3perMM();
}
/*!
* Get the actual line width (modulated by the flow)
* \return the actual line width as shown in layer view
*/
int getLineWidth()
{
return flow * config->getLineWidth() * config->getFlowPercentage() / 100.0;
}
};
class ExtruderPlan
@@ -236,8 +247,6 @@ class GCodePlanner : public NoCopy
private:
SliceDataStorage& storage;
CommandSocket* commandSocket;
int layer_nr;
int z;
@@ -272,10 +281,11 @@ private:
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
*
* \param config The config used for the path returned
* \param space_fill_type The type of space filling which this path employs
* \param flow (optional) A ratio for the extrusion speed
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, float flow = 1.0);
GCodePath* getLatestPathWithConfig(GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
@@ -295,7 +305,7 @@ public:
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
*/
GCodePlanner(CommandSocket* commandSocket, SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(SliceDataStorage& storage, unsigned int layer_nr, int z, int layer_height, Point last_position, int current_extruder, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, bool retraction_combing, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
private:
@@ -316,6 +326,22 @@ public:
return lastPosition;
}
/*!
* send a polygon through the command socket from the previous point to the given point
*/
void sendPolygon(PrintFeatureType print_feature_type, Point from, 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);
}
}
/*!
* Set whether the next destination is inside a layer part or not.
*
@@ -374,8 +400,16 @@ public:
* \param path (optional) The travel path to which to add the point \p p
*/
void addTravel_simple(Point p, GCodePath* path = nullptr);
void addExtrusionMove(Point p, GCodePathConfig* config, float flow = 1.0);
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
*
* \param p The point to extrude to
* \param config The config with which to extrude
* \param space_fill_type Of what space filling type this extrusion move is a part
* \param flow A modifier of the extrusion width which would follow from the \p config
*/
void addExtrusionMove(Point p, GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr);
@@ -385,9 +419,10 @@ public:
* Add lines to the gcode with optimized order.
* \param polygons The lines
* \param config The config of the lines
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, int wipe_dist = 0);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accountign for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
@@ -438,35 +473,13 @@ public:
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume_move The volume otherwise leaked during a normal move.
* \param coasting_speed_move The speed at which to move during move-coasting.
* \param coasting_min_volume_move The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_move.
* \param coasting_volume_retract The volume otherwise leaked during a retract move.
* \param coasting_speed_retract The speed at which to move during retract-coasting.
* \param coasting_min_volume_retract The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume_retract.
* \param coasting_volume The volume otherwise leaked during a normal move.
* \param coasting_speed The speed at which to move during move-coasting.
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume_move, double coasting_speed_move, double coasting_min_volume_move, double coasting_volume_retract, double coasting_speed_retract, double coasting_min_volume_retract);
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* Paths shorter than \p coasting_min_volume will use less \p coasting_volume linearly.
*
* \param gcode The gcode to write the planned paths to
* \param path The extrusion path to be written to GCode.
* \param path_next The next travel path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked.
* \param coasting_speed The speed at which to move during coasting.
* \param coasting_min_volume The minimal volume a path should have which builds up enough pressure to ooze as much as \p coasting_volume.
* \param extruder_switch_retract (optional) For a coasted path followed by a retraction: whether to retract normally, or do an extruder switch retraction.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, GCodePath& path, GCodePath& path_next, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume, bool extruder_switch_retract = false);
/*!
* Write a retraction: either an extruder switch retraction or a normal retraction based on the last extrusion paths retraction config.
* \param gcode The gcode to write the planned paths to
+180 -345
Ver Arquivo
@@ -2,7 +2,6 @@
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/AABB.h"
#include "utils/logoutput.h"
namespace cura {
@@ -16,36 +15,28 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
switch(pattern)
{
case EFillMethod::GRID:
generateGridInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 2, infill_overlap, fill_angle);
generateGridInfill(result_lines);
break;
case EFillMethod::LINES:
generateLineInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle);
generateLineInfill(result_lines, line_distance, fill_angle);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(in_outline, outlineOffset, result_lines, extrusion_width, line_distance * 3, infill_overlap, fill_angle);
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
if (outlineOffset != 0)
PolygonUtils::offsetSafe(in_outline, outline_offset - infill_line_width / 2, infill_line_width, outline_offsetted, false); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
outline = &outline_offsetted;
if (abs(infill_line_width - line_distance) < 10)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
generateConcentricInfillDense(*outline, result_polygons, in_between, remove_overlapping_perimeters);
}
if (abs(extrusion_width - line_distance) < 10)
{
generateConcentricInfillDense(*outline, result_polygons, in_between, extrusion_width, avoidOverlappingPerimeters);
}
else
else
{
generateConcentricInfill(*outline, result_polygons, line_distance);
}
break;
case EFillMethod::ZIG_ZAG:
if (outlineOffset != 0)
{
PolygonUtils::offsetSafe(in_outline, outlineOffset, extrusion_width, outline_offsetted, avoidOverlappingPerimeters);
outline = &outline_offsetted;
}
generateZigZagInfill(*outline, result_lines, extrusion_width, line_distance, infill_overlap, fill_angle, connect_zigzags, use_endPieces);
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
break;
default:
logError("Fill pattern has unknown value.\n");
@@ -53,9 +44,9 @@ void Infill::generate(Polygons& result_polygons, Polygons& result_lines, Polygon
}
}
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
void Infill::generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool avoidOverlappingPerimeters)
{
while(outline.size() > 0)
{
@@ -65,13 +56,13 @@ void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons*
result.add(r);
}
Polygons next_outline;
PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
PolygonUtils::offsetExtrusionWidth(outline, true, infill_line_width, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
}
}
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
void Infill::generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
while(outline.size() > 0)
{
@@ -85,375 +76,219 @@ void generateConcentricInfill(Polygons outline, Polygons& result, int inset_valu
}
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
void Infill::generateGridInfill(Polygons& result)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 90);
generateLineInfill(result, line_distance * 2, fill_angle);
generateLineInfill(result, line_distance * 2, fill_angle + 90);
}
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, double infillOverlap,
double rotation)
void Infill::generateTriangleInfill(Polygons& result)
{
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 60);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 120);
generateLineInfill(result, line_distance * 3, fill_angle);
generateLineInfill(result, line_distance * 3, fill_angle + 60);
generateLineInfill(result, line_distance * 3, fill_angle + 120);
}
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list)
{
auto addLine = [&](Point from, Point to)
{
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
p.add(rotation_matrix.unapply(from));
p.add(rotation_matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0) return -1;
if (n > 0) return 1;
if (n < 0)
{
return -1;
}
if (n > 0)
{
return 1;
}
return 0;
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
for(int64_t x = scanline_min_idx * line_distance; x < boundary.max.X; x += line_distance)
{
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
{
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
{ // segment is too short to create infill
continue;
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
}
addLine(Point(x, crossings[crossing_idx]), Point(x, crossings[crossing_idx + 1]));
}
scanline_idx += 1;
}
}
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle)
{
if (lineSpacing == 0) return;
if (in_outline.size() == 0) return;
Polygons outline = ((outlineOffset)? in_outline.offset(outlineOffset) : in_outline).offset(extrusionWidth * infillOverlap / 100);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
bool safe_outline_offset = false;
generateLinearBasedInfill(outline_offset, safe_outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags);
}
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
{
bool safe_outline_offset = true;
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
{
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
if (connected_zigzags)
{
Point p1 = outline[poly_idx][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
}
p0 = p1;
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
{
if (use_endPieces) return generateZigZagIninfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagIninfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
}
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags)
{
// if (in_outline.size() == 0) return;
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
Polygons empty;
Polygons outline = in_outline.difference(empty); // copy
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
else
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven = false;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
Point lastPoint = p0;
for(unsigned int i=0; i < outline[polyNr].size(); i++)
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, safe_outline_offset, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags);
}
}
/*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* rough explanation of the zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
*
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
*/
void Infill::generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags)
{
if (line_distance == 0)
{
return;
}
if (in_outline.size() == 0)
{
return;
}
Polygons outline;
if (outline_offset != 0)
{
PolygonUtils::offsetSafe(in_outline, outline_offset, infill_line_width, outline, remove_overlapping_perimeters && safe_outline_offset);
}
else
{
outline = in_outline;
}
if (line_distance > infill_line_width * 3 / 2)
{ // infill is not too dense to have overlap with surrounding polygon
outline = outline.offset(infill_overlap * infill_line_width / 100); // division by 100 cause it's a percentage.
}
if (outline.size() == 0)
{
return;
}
outline.applyMatrix(rotation_matrix);
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / line_distance;
int line_count = (boundary.max.X + (line_distance - 1)) / line_distance - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
{
cut_list.push_back(std::vector<int64_t>());
}
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
{
PolygonRef poly = outline[poly_idx];
Point p0 = poly.back();
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
lastPoint = p1;
Point p1 = poly[point_idx];
if (p1.X == p0.X)
{
zigzag_connector_processor.registerVertex(p1);
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -line_distance)) / line_distance; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -line_distance)) / line_distance; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
}
else
{
int x = scanline_idx * lineSpacing;
scanline_idx0 += 1; // only consider the scanlines in between the scansegments
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
{
int x = scanline_idx * line_distance;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
addLine(lastPoint, Point(x,y));
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
{ // add whole unevenBoundarySegment (including the just obtained point)
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
{
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
}
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
unevenBoundarySegment.clear();
}
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
unevenBoundarySegment.push_back(Point(x,y));
else
unevenBoundarySegment.clear();
}
lastPoint = Point(x,y);
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
}
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
}
if (!isFirstBoundarySegment)
{
if (isEvenScanSegment)
addLine(lastPoint, p1);
else if (connect_zigzags)
unevenBoundarySegment.push_back(p1);
}
lastPoint = p1;
zigzag_connector_processor.registerVertex(p1);
p0 = p1;
}
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
{
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
else if (!firstBoundarySegmentEndsInEven)
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
}
if (cutList.size() == 0) return;
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
zigzag_connector_processor.registerPolyFinished();
}
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation)
{
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
if (cut_list.size() == 0)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven = true;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
else boundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && !isEvenScanSegment)
{ // add whole boundarySegment (including the just obtained point)
for (unsigned int p = 1; p < boundarySegment.size(); p++)
{
addLine(boundarySegment[p-1], boundarySegment[p]);
}
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
boundarySegment.clear();
}
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
{
boundarySegment.clear();
boundarySegment.emplace_back(x,y);
} else
boundarySegment.clear();
}
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
boundarySegment.emplace_back(x,y);
}
}
if (!isFirstBoundarySegment && isEvenScanSegment)
boundarySegment.push_back(p1);
p0 = p1;
}
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
return;
}
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
{
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list);
}
}//namespace cura
+137 -79
Ver Arquivo
@@ -4,99 +4,141 @@
#include "utils/polygon.h"
#include "settings.h"
// #include "ZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessor.h"
#include "infill/NoZigZagConnectorProcessor.h"
#include "infill/ActualZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
namespace cura
{
class Infill
class Infill
{
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
bool remove_overlapping_perimeters; //!< Whether to remove overlapping perimeter parts
int infill_line_width; //!< The line width of the infill lines to generate
int line_distance; //!< The distance between two infill lines / polygons
double infill_overlap; //!< the percentage (of infill_line_width) to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
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
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outline_offset, bool remove_overlapping_perimeters, int infill_line_width, int line_distance, double infill_overlap, double fill_angle, bool connected_zigzags = false, bool use_endpieces = false)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
, remove_overlapping_perimeters(remove_overlapping_perimeters)
, infill_line_width(infill_line_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
EFillMethod pattern;
const Polygons& in_outline;
int outlineOffset;
bool avoidOverlappingPerimeters;
int extrusion_width;
int line_distance;
double infill_overlap;
double fill_angle;
bool connect_zigzags;
bool use_endPieces;
}
/*!
* Generate the infill.
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \param result_lines (output) The resulting line segments (from linear infill types)
* \param in_between (optional output) The areas in between two concecutive concentric infill polygons
*/
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
public:
Infill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces)
: pattern(pattern)
, in_outline(in_outline)
, outlineOffset(outlineOffset)
, avoidOverlappingPerimeters(avoidOverlappingPerimeters)
, extrusion_width(extrusion_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, connect_zigzags(connect_zigzags)
, use_endPieces(use_endPieces)
{
}
void generate(Polygons& result_polygons, Polygons& result_lines, Polygons* in_between);
};
void generateInfill(EFillMethod pattern, const Polygons& in_outline, int outlineOffset, Polygons& result_polygons, Polygons& result_lines, Polygons* in_between, bool avoidOverlappingPerimeters, int extrusion_width, int line_distance, double infill_overlap, double fill_angle, bool connect_zigzags, bool use_endPieces);
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
private:
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
* Generate sparse concentric infill
* \param outline The actual outline of the area within which to generate infill
* \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);
/*!
* Generate dense concentric infill (100%)
*
* \param outline The actual outline of the area within which to generate infill
* \param result (output) The resulting polygons
* \param in_between (output) The areas in between each two consecutive polygons
* \param remove_overlapping_perimeters Whether to remove overlapping perimeter parts
*/
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, bool remove_overlapping_perimeters);
/*!
* Generate a rectangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateTriangleInfill(Polygons& result);
/*!
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
* \param result (output) The resulting lines
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
*
* algorithm:
* 1) for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2) for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
*/
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
*
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
*
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param safe_outline_offset Whether to consider removing overlapping wall parts (not so for normal line infill)
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
*/
void generateLinearBasedInfill(const int outline_offset, bool safe_outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags);
/*!
* adapted from generateLineInfill(.)
*
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
* sigzag:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connect_zigzags])
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* <--
* ___
@@ -106,21 +148,37 @@ namespace cura
* -->
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
*
*
* v disconnected end piece: leave out last line segment
* _____
* | | | ,
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
*
*
* v connected end piece
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
*/
void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags);
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
};
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation);
}//namespace cura
#endif//INFILL_H
+47
Ver Arquivo
@@ -0,0 +1,47 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
/*!
* In contrast to NoZigZagConnectorProcessor
*/
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
{
protected:
/*!
* The line segments belonging the zigzag connector to which the very first vertex belongs.
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
*
* Because the boundary polygon may start in in the middle of a zigzag connector,
*/
std::vector<Point> first_zigzag_connector;
/*!
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
*/
std::vector<Point> zigzag_connector;
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
, is_first_zigzag_connector(true)
, first_zigzag_connector_ends_in_even_scanline(true)
, last_scanline_is_even(false)
{
}
};
} // namespace cura
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
+25
Ver Arquivo
@@ -0,0 +1,25 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "NoZigZagConnectorProcessor.h"
namespace cura
{
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
{
}
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
}
void NoZigZagConnectorProcessor::registerPolyFinished()
{
}
} // namespace cura
+28
Ver Arquivo
@@ -0,0 +1,28 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
namespace cura
{
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
{
public:
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
+154
Ver Arquivo
@@ -0,0 +1,154 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
namespace cura
{
/*!
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
*
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
* at the same time as the lines are created.
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connected_zigzags])
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
* The inheritance structure is as such:
* ZigzagConnectorProcessor
* / \ .
* / \ .
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
* / \ for lines infill .
* / \ .
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
* / \ for zigzag infill (without end pieces) .
* / \ .
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
*
* v v zigzag connectors
* <--
* :___: : < scanlines
* | | |
* | | | < infill lines along scanlines
* | |___|
* : : :
* --> winding order of polygon
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
*
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
* ^ connected end piece
* include a boundary segment also if it starts in an odd scanline and ends odd,
* or starts in an even scanline and ends in an even scanline,
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
*
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
*/
class ZigzagConnectorProcessor
{
protected:
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
Polygons& result; //!< The result of the computation
virtual ~ZigzagConnectorProcessor()
{}
/*!
* Add a line to the result bu unapplying the rotation rotation_matrix.
*
* \param from The one end of the line segment
* \param to The other end of the line segment
*/
void addLine(Point from, Point to)
{
PolygonRef line_poly = result.newPoly();
line_poly.add(rotation_matrix.unapply(from));
line_poly.add(rotation_matrix.unapply(to));
}
/*!
* Basic constructor. Inheriting children should call this constructor.
*
* \param rotation_matrix The rotation matrix used to enforce the infill angle
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
*/
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: rotation_matrix(rotation_matrix)
, result(result)
{}
public:
/*!
* Handle the next vertex on the outer boundary.
* \param vertex The vertex
*/
virtual void registerVertex(const Point& vertex) = 0;
/*!
* Handle the next intersection between a scanline and the outer boundary.
*
* \param intersection The intersection
* \param scanline_is_even Whether the scanline was even
*/
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
/*!
* Handle the end of a polygon and prepare for the next.
* This function should reset all member variables.
*/
virtual void registerPolyFinished() = 0;
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
@@ -0,0 +1,75 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even)
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|| is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,27 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
@@ -0,0 +1,79 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole oddBoundarySegment (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
// skip the last segment to the [intersection]
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// write very last line segment if needed
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only add last element if boundary segment ends in odd scanline
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,26 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
@@ -0,0 +1,27 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
addLine(last_connector_point, vertex);
}
else
{ // it's yet unclear whether the line segment should be included, so we store it until we know
zigzag_connector.push_back(vertex);
}
last_connector_point = vertex;
}
} // namespace cura
@@ -0,0 +1,32 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
namespace cura
{
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
{
protected:
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
, last_connector_point(0,0)
{
}
public:
void registerVertex(const Point& vertex);
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
@@ -0,0 +1,72 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorNoEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{
zigzag_connector.push_back(vertex);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
{
zigzag_connector.push_back(intersection);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
{
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -0,0 +1,29 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
{
public:
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
-29
Ver Arquivo
@@ -71,33 +71,4 @@ void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int l
}
}
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
{
// optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[0].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
}
if (reinforcement_wall.wall_reinforcement_axtra_walls[0].size() > 0)
{
for(int i=1; i<insetCount; i++)
{
reinforcement_wall.wall_reinforcement_axtra_walls.push_back(Polygons());
PolygonUtils::offsetExtrusionWidth(reinforcement_wall.wall_reinforcement_axtra_walls[i-1], true, line_width_x, reinforcement_wall.wall_reinforcement_axtra_walls[i], &part->perimeterGaps, avoidOverlappingPerimeters);
//Finally optimize all the polygons. Every point removed saves time in the long run.
reinforcement_wall.wall_reinforcement_axtra_walls[i].simplify();
if (reinforcement_wall.wall_reinforcement_axtra_walls[i].size() < 1)
{
reinforcement_wall.wall_reinforcement_axtra_walls.pop_back();
break;
}
}
}
}
}//namespace cura
-10
Ver Arquivo
@@ -36,16 +36,6 @@ void generateInsets(SliceLayerPart* part, int nozzle_width, int line_width_0, in
*/
void generateInsets(SliceLayer* layer, int nozzle_width, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generates the wall reinforcement extra walls for a single layer part.
*
* \param part The part for which to generate the extra walls.
* \param line_width_x line width of the walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateWallReinforcementWallExtraWalls(SliceLayerPart* part, ReinforcementWall& reinforcement_wall, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+4 -3
Ver Arquivo
@@ -66,7 +66,7 @@ void print_call(int argc, char **argv)
void connect(int argc, char **argv)
{
CommandSocket* commandSocket = new CommandSocket();
CommandSocket::instantiate();
std::string ip;
int port = 49674;
@@ -107,7 +107,7 @@ void connect(int argc, char **argv)
}
}
commandSocket->connect(ip, port);
CommandSocket::getInstance()->connect(ip, port);
}
void slice(int argc, char **argv)
@@ -148,12 +148,13 @@ void slice(int argc, char **argv)
FffProcessor::getInstance()->time_keeper.restart();
delete meshgroup;
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_extruder_train = meshgroup->createExtruderTrain(0);
last_settings_object = meshgroup;
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
+162 -123
Ver Arquivo
@@ -2,25 +2,27 @@
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/TravellingSalesman.h"
#define INLINE static inline
namespace cura {
namespace cura
{
/**
*
*/
*
*/
void PathOrderOptimizer::optimize()
{
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
memset(picked, false, sizeof (bool) * polygons.size()); /// initialized as falses
for (unsigned int i_polygon = 0; i_polygon < polygons.size(); i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point in polygon
for (unsigned int i_point = 0; i_point < poly.size(); i_point++) /// get closest point in polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
@@ -37,23 +39,23 @@ void PathOrderOptimizer::optimize()
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
for (unsigned int polygon_idx = 0; polygon_idx < polygons.size(); polygon_idx++) /// actual path order optimizer
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
for (unsigned int polygon2_idx = 0; polygon2_idx < polygons.size(); polygon2_idx++)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
if (picked[polygon2_idx] || polygons[polygon2_idx].size() < 1) /// skip single-point-polygons
continue;
assert (polygons[i_polygon].size() != 2);
assert(polygons[polygon2_idx].size() != 2);
float dist = vSize2f(polygons[i_polygon][polyStart[i_polygon]] - prev_point);
float dist = vSize2f(polygons[polygon2_idx][polyStart[polygon2_idx]] - prev_point);
if (dist < bestDist)
{
best = i_polygon;
best = polygon2_idx;
bestDist = dist;
}
@@ -74,7 +76,7 @@ void PathOrderOptimizer::optimize()
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
for (unsigned int n = 0; n < polyOrder.size(); n++) /// decide final starting points in each polygon
{
int poly_idx = polyOrder[n];
int point_idx = getPolyStart(prev_point, poly_idx);
@@ -88,24 +90,23 @@ int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
switch (type)
{
case EZSeamType::BACK: return getFarthestPointInPolygon(poly_idx);
case EZSeamType::RANDOM: return getRandomPointInPolygon(poly_idx);
case EZSeamType::BACK: return getFarthestPointInPolygon(poly_idx);
case EZSeamType::RANDOM: return getRandomPointInPolygon(poly_idx);
case EZSeamType::SHORTEST: return getClosestPointInPolygon(prev_point, poly_idx);
default: return getClosestPointInPolygon(prev_point, poly_idx);
}
}
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i_point=0 ; i_point<poly.size() ; i_point++)
for (unsigned int i_point = 0; i_point < poly.size(); i_point++)
{
float dist = vSize2f(poly[i_point] - prev_point);
Point n0 = normal(poly[(i_point-1+poly.size())%poly.size()] - poly[i_point], 2000);
Point n0 = normal(poly[(i_point - 1 + poly.size()) % poly.size()] - poly[i_point], 2000);
Point n1 = normal(poly[i_point] - poly[(i_point + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1); /// prefer binnenbocht
if (orientation)
@@ -124,13 +125,12 @@ int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
return rand() % polygons[poly_idx].size();
}
int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
PolygonRef poly = polygons[poly_idx];
int best_point_idx = -1;
float best_y = std::numeric_limits<float>::min();
for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
if (poly[point_idx].Y > best_y)
{
@@ -141,136 +141,175 @@ int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
return best_point_idx;
}
LineOrderOptimizer::LineOrderOptimizer(const Point& start_point, unsigned long long cluster_grid_size)
: cluster_grid_size(cluster_grid_size == 0 ? 2000 : cluster_grid_size) //Initialise cluster_grid_size to 2000 if the input grid size is invalid (e.g. no infill).
{
this->startPoint = start_point;
}
/**
*
*/
*
*/
void LineOrderOptimizer::optimize()
{
int gridSize = 5000; // the size of the cells in the hash grid.
BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
bool picked[polygons.size()];
memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// find closest point to initial starting point within each polygon +initialize picked
if (lines.empty()) //Nothing to do. Terminate early.
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
PolygonRef poly = polygons[i_polygon];
for(unsigned int i_point=0; i_point<poly.size(); i_point++) /// get closest point from polygon
{
float dist = vSize2f(poly[i_point] - startPoint);
if (dist < bestDist)
{
best = i_point;
bestDist = dist;
}
}
polyStart.push_back(best);
assert(poly.size() == 2);
line_bucket_grid.insert(poly[0], i_polygon);
line_bucket_grid.insert(poly[1], i_polygon);
return;
}
//Since polyOrder must be filled with indices, an index in the polygons vector represents each line.
std::vector<Cluster> line_clusters = cluster();
Point incommingPerpundicularNormal(0, 0);
Point prev_point = startPoint;
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++) /// actual path order optimizer
//Define how the TSP solver should use its elements.
std::function<std::vector<std::pair<Point, Point>> (size_t)> get_orientations = [&](size_t cluster_index)->std::vector<std::pair<Point, Point>> //How to get the possible orientations of a cluster.
{
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
for(unsigned int i_close_line_polygon : line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
std::vector<std::pair<Point, Point>> result;
const size_t first_line_index = line_clusters[cluster_index][0]; //The first line in the current cluster.
const size_t last_line_index = line_clusters[cluster_index].back(); //The last line in the current cluster.
const PolygonRef first_line = lines[first_line_index];
const PolygonRef last_line = lines[last_line_index];
const Point start_normal = first_line[polyStart[first_line_index]]; //Start of the path, not mirrored.
const Point end_normal = last_line[(polyStart[last_line_index] + last_line.size() - 1) % last_line.size()]; //End of the path, not mirrored.
result.push_back(std::pair<Point, Point>(start_normal, end_normal));
result.push_back(std::pair<Point, Point>(end_normal, start_normal)); //Can also insert in reverse!
if (line_clusters[cluster_index].size() > 1u) //If the cluster has one line, mirroring the line is equal to reversing the path. Otherwise, we must also include mirrored options.
{
if (picked[i_close_line_polygon] || polygons[i_close_line_polygon].size() < 1)
continue;
checkIfLineIsBest(i_close_line_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
const Point start_mirrored = first_line[(polyStart[first_line_index] + first_line.size() - 1) % first_line.size()]; //Start of the path, mirrored.
const Point end_mirrored = first_line[(polyStart[first_line_index] + first_line.size() - 1) % first_line.size()]; //End of the path, mirrored.
result.push_back(std::pair<Point, Point>(start_mirrored, end_mirrored));
result.push_back(std::pair<Point, Point>(end_mirrored, start_mirrored));
}
return result;
};
TravellingSalesman<size_t> tspsolver(get_orientations); //Solves the macro TSP problem of ordering the clusters.
std::vector<size_t> cluster_orientations;
std::vector<size_t> unoptimised(line_clusters.size());
std::iota(unoptimised.begin(), unoptimised.end(), 0);
std::vector<size_t> optimised = tspsolver.findPath(unoptimised, cluster_orientations, &startPoint); //Approximate the shortest path with the TSP solver.
if (best == -1) /// if single-line-polygon hasn't been found yet
//Actually put the paths in their correct order for the output.
polyOrder.reserve(lines.size());
for (size_t cluster_index = 0; cluster_index < optimised.size(); cluster_index++)
{
Cluster cluster = line_clusters[optimised[cluster_index]];
//Determine in what orientation we should place the cluster depending on cluster_orientations[cluster_index].
size_t orientation = cluster_orientations[cluster_index];
if (cluster.size() == 1) //Singleton clusters have only 2 possible orientations.
{
for(unsigned int i_polygon=0 ; i_polygon<polygons.size() ; i_polygon++)
polyOrder.push_back(static_cast<int>(cluster[0]));
if (orientation >= 1)
{
if (picked[i_polygon] || polygons[i_polygon].size() < 1) /// skip single-point-polygons
continue;
assert(polygons[i_polygon].size() == 2);
checkIfLineIsBest(i_polygon, best, bestDist, prev_point, incommingPerpundicularNormal);
polyStart[cluster[0]] = 1 - polyStart[cluster[0]];
}
}
if (best > -1) /// should always be true; we should have been able to identify the best next polygon
else //Larger clusters have 4 possible orientations.
{
assert(polygons[best].size() == 2);
int endIdx = polyStart[best] * -1 + 1; /// 1 -> 0 , 0 -> 1
prev_point = polygons[best][endIdx];
incommingPerpundicularNormal = crossZ(normal(polygons[best][endIdx] - polygons[best][polyStart[best]], 1000));
picked[best] = true;
polyOrder.push_back(best);
}
else
logError("Failed to find next closest line.\n");
}
prev_point = startPoint;
for(unsigned int n=0; n<polyOrder.size(); n++) /// decide final starting points in each polygon
{
int nr = polyOrder[n];
PolygonRef poly = polygons[nr];
int best = -1;
float bestDist = std::numeric_limits<float>::infinity();
bool orientation = poly.orientation();
for(unsigned int i=0;i<poly.size(); i++)
{
float dist = vSize2f(polygons[nr][i] - prev_point);
Point n0 = normal(poly[(i+poly.size()-1)%poly.size()] - poly[i], 2000);
Point n1 = normal(poly[i] - poly[(i + 1) % poly.size()], 2000);
float dot_score = dot(n0, n1) - dot(crossZ(n0), n1);
if (orientation)
dot_score = -dot_score;
if (dist + dot_score < bestDist)
if ((orientation & 1) == 0) //Not reversed.
{
best = i;
bestDist = dist + dot_score;
for (size_t polygon_index = 0; polygon_index < cluster.size(); polygon_index++)
{
polyOrder.push_back(static_cast<int>(cluster[polygon_index]));
}
}
else //Reversed.
{
for (size_t polygon_index = cluster.size(); polygon_index-- > 0; ) //Insert the lines in backward direction.
{
polyOrder.push_back(static_cast<int>(cluster[polygon_index]));
}
}
if (orientation >= 2u) //Mirrored.
{
for (size_t polygon_index : cluster) //Mirror each line in the cluster.
{
polyStart[polygon_index] = 1 - polyStart[polygon_index];
}
}
}
polyStart[nr] = best;
assert(poly.size() == 2);
prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1
}
}
inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal)
std::vector<std::vector<size_t>> LineOrderOptimizer::cluster()
{
{ /// check distance to first point on line (0)
float dist = vSize2f(polygons[i_line_polygon][0] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][1] - polygons[i_line_polygon][0], 1000))) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
polyStart.resize(lines.size()); //Polystart should always contain an entry for all polygons.
BucketGrid2D<size_t> grid(cluster_grid_size);
for (size_t polygon_index = 0; polygon_index < lines.size(); polygon_index++) //First put every endpoint of all lines in the grid.
{
grid.insert(lines[polygon_index][0], polygon_index);
grid.insert(lines[polygon_index].back(), polygon_index);
}
std::vector<Cluster> clusters;
bool picked[lines.size()]; //For each polygon, whether it is already in a cluster.
memset(picked, 0, lines.size()); //Initialise to false.
for (size_t polygon_index = 0; polygon_index < lines.size(); polygon_index++) //Find clusters with nearest neighbour-ish search.
{
if (picked[polygon_index]) //Already in a cluster.
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 0;
continue;
}
clusters.push_back(Cluster()); //Make a new cluster for this line.
clusters.back().push_back(polygon_index);
polyStart[polygon_index] = 0; //Choose one possible mirroring of the lines. This determines the start point of each line. The mirror of a cluster is also checked by the TSP solver (but the combination of directions of each individual line is determined here below).
picked[polygon_index] = true;
size_t current_polygon = polygon_index; //We'll do a walk to the nearest valid neighbour. A neighbour is valid if it is not picked yet and if both its endpoints are near.
size_t best_polygon = current_polygon;
while (best_polygon != static_cast<size_t>(-1)) //Keep going until there is no valid neighbour.
{
const PolygonRef current_line = lines[current_polygon];
Point current_start = current_line[polyStart[current_polygon]]; //Start and end point of the current polygon. These are used to find the distance to the next polygon.
Point current_end = current_line[(polyStart[current_polygon] + current_line.size() - 1) % current_line.size()];
best_polygon = static_cast<size_t>(-1);
unsigned long long best_distance = cluster_grid_size * cluster_grid_size + 1; //grid_size squared since vSize2 gives squared distance.
size_t best_start;
for (size_t neighbour : grid.findNearbyObjects(current_line[0]))
{
if (picked[neighbour]) //Don't use neighbours that are already in another cluster.
{
continue;
}
tryCluster(neighbour, current_start, current_end, &best_polygon, &best_distance, &best_start);
}
if (best_polygon != static_cast<size_t>(-1)) //We found one.
{
current_polygon = best_polygon;
clusters.back().push_back(best_polygon);
polyStart[best_polygon] = best_start;
picked[best_polygon] = true;
}
}
}
{ /// check distance to second point on line (1)
float dist = vSize2f(polygons[i_line_polygon][1] - prev_point);
dist += abs(dot(incommingPerpundicularNormal, normal(polygons[i_line_polygon][0] - polygons[i_line_polygon][1], 1000) )) * 0.0001f; /// penalize sharp corners
if (dist < bestDist)
return clusters;
}
void LineOrderOptimizer::tryCluster(size_t line, const Point current_start, const Point current_end, size_t* best_polygon, unsigned long long* best_distance, size_t* best_start)
{
//Input checking.
if (!best_polygon || !best_distance || !best_start) //Output parameter missing.
{
return;
}
const unsigned long long distance_start_start = vSize2(current_start - lines[line][0]);
const unsigned long long distance_start_end = vSize2(current_start - lines[line].back());
const unsigned long long distance_end_start = vSize2(current_end - lines[line][0]);
const unsigned long long distance_end_end = vSize2(current_end - lines[line].back());
if (distance_start_start < cluster_grid_size * cluster_grid_size && distance_end_end < cluster_grid_size * cluster_grid_size) //Two lines are alongside each other.
{
if (distance_end_end < *best_distance) //Best neighbour and orientation so far.
{
best = i_line_polygon;
bestDist = dist;
polyStart[i_line_polygon] = 1;
*best_polygon = line;
*best_distance = distance_end_end;
*best_start = lines[line].size() - 1;
}
}
if (distance_start_end < cluster_grid_size * cluster_grid_size && distance_end_start < cluster_grid_size * cluster_grid_size) //Two lines are alongside each other, but in reverse direction.
{
if (distance_end_start < *best_distance)
{
*best_polygon = line;
*best_distance = distance_end_start;
*best_start = 0;
}
}
}
+65 -9
Ver Arquivo
@@ -56,33 +56,89 @@ private:
*/
class LineOrderOptimizer
{
typedef typename std::vector<size_t> Cluster; //To make it more clear what a cluster is.
public:
/*!
* \brief The size of the grid cells used to cluster lines.
*
* Increase this value to make the optimisation algorithm fall back to
* nearest neighbour more often. Reduce this value to make the optimisation
* algorithm use random insertion on smaller pieces of the input.
*/
const unsigned long long cluster_grid_size;
Point startPoint; //!< The location of the nozzle before starting to print the current layer
std::vector<PolygonRef> polygons; //!< the parts of the layer (in arbitrary order)
std::vector<PolygonRef> lines; //!< 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
LineOrderOptimizer(Point startPoint)
{
this->startPoint = startPoint;
}
/*!
* \brief Constructs the line order optimiser with the specified settings.
*
* \param start_point The starting point from where the paths generated by
* this optimiser must start.
* \param cluster_grid_size The size of the grid cells used to cluster
* lines. Make this bigger and the optimiser will fall back to nearest
* neighbour search more often. Make this smaller and the optimiser will
* use random insertion more often.
*/
LineOrderOptimizer(const Point& start_point, unsigned long long cluster_grid_size);
void addPolygon(PolygonRef polygon)
{
this->polygons.push_back(polygon);
this->lines.push_back(polygon);
}
void addPolygons(Polygons& polygons)
{
for(unsigned int i=0;i<polygons.size(); i++)
this->polygons.push_back(polygons[i]);
for(unsigned int i = 0; i < polygons.size(); i++)
{
this->lines.push_back(polygons[i]);
}
}
void optimize(); //!< sets #polyStart and #polyOrder
private:
void checkIfLineIsBest(unsigned int i_line_polygon, int& best, float& bestDist, Point& prev_point, Point& incommingPerpundicularNormal);
/*!
* \brief Clusters the polygons in groups such that the start and end of the
* polygons in each group are close together.
*
* This performs a simple nearest-neighbour traversal through all lines. An
* arbitrary line is chosen as starting point for a cluster, and iteratively
* the nearest neighbouring line will get added to that cluster. A line is
* only neighbouring if both of its endpoints are nearby the endpoints of
* the previous line. This way you get logical groups of lines that should
* always be in sequence, with fairly low computational cost.
*
* \return Clusters of polygons, where each cluster is represented with a
* vector of indices pointing to positions in \link polygons.
*/
std::vector<Cluster> cluster();
/*!
* \brief Tries to insert the specified line in the currently processed
* cluster.
*
* The distance of the line to the current line (represented by
* \p current_start and \p current_end) is measured, and compared to the
* distance of the best candidate to insert so far. If it is shorter, then
* the new line is stored via \p best_polygon as the best line to add to the
* cluster.
*
* \param line The index of the new line to try to add to the cluster.
* \param current_start The start point of the previous line that was added.
* \param current_end The end point of the previous line that was added.
* \param best_polygon The line that is the best candidate to insert so far.
* This parameter may be changed by this function if the new line is better.
* \param best_distance The distance of the best candidate to insert so far.
* This parameter may be changed by this function if the new line is better.
* \param best_start The starting vertex index of the best candidate to
* insert so far. This parameter may be changed by this function if the new
* line is better.
*/
void tryCluster(size_t line, const Point current_start, const Point current_end, size_t* best_polygon, unsigned long long* best_distance, size_t* best_start);
};
}//namespace cura
+5
Ver Arquivo
@@ -236,6 +236,11 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
{
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
if (!conf) //Setting could not be found.
{
logWarning("Trying to override unknown setting %s.", setting.c_str());
continue;
}
_loadSettingValues(conf, override_iterator, false);
}
}
+2 -2
Ver Arquivo
@@ -134,7 +134,7 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
}
}
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count)
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count)
{
SliceLayer& layer = storage.layers[layerNr];
@@ -144,7 +144,7 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
{
continue; // the last wall is not present, the part should only get inter preimeter gaps, but no infill.
}
Polygons infill = part.insets.back().offset(-extrusionWidth / 2 - infill_skin_overlap);
Polygons infill = part.insets.back().offset(-innermost_wall_extrusion_width / 2 - infill_skin_overlap);
for(SliceLayerPart& part2 : layer.parts)
{
+2 -2
Ver Arquivo
@@ -67,11 +67,11 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
*
* \param layerNr The index of the layer for which to generate the infill
* \param part The part where the insets (input) are stored and where the infill (output) is stored.
* \param extrusionWidth width of the wall lines
* \param innermost_wall_extrusion_width width of the innermost wall lines
* \param infill_skin_overlap overlap distance between infill and skin
* \param wall_line_count The number of walls, i.e. the number of the wall from which to offset.
*/
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap, int wall_line_count);
void generateInfill(int layerNr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int infill_skin_overlap, int wall_line_count);
/*!
* \brief Combines the infill of multiple layers for a specified mesh.
+6 -93
Ver Arquivo
@@ -59,13 +59,13 @@ void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls)
SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
retraction_config_per_extruder(initializeRetractionConfigs()),
travel_config(&retraction_config, "MOVE"),
travel_config(&retraction_config, PrintFeatureType::MoveCombing),
skirt_config(initializeSkirtConfigs()),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], "SUPPORT"),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_infill_extruder_nr")], "SUPPORT"),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], "SKIN"),
raft_base_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_interface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
raft_surface_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("adhesion_extruder_nr")], PrintFeatureType::Support),
support_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_extruder_nr")], PrintFeatureType::Support),
support_roof_config(&retraction_config_per_extruder[this->meshgroup->getSettingAsIndex("support_roof_extruder_nr")], PrintFeatureType::Skin),
max_object_height_second_to_last_extruder(-1)
{
}
@@ -161,93 +161,6 @@ Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool inc
}
std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr)
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
if (layer_nr < 0)
{
ret[getSettingAsIndex("adhesion_extruder_nr")] = true; // raft
}
else
{
if (layer_nr == 0)
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield
// support
if (support.supportLayers[layer_nr].supportAreas.size() > 0)
{
if (layer_nr == 0)
{
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
}
else
{
ret[getSettingAsIndex("support_extruder_nr")] = true;
}
}
if (support.supportLayers[layer_nr].roofs.size() > 0)
{
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
}
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
int extr_nr = mesh.getSettingAsIndex("extruder_nr");
if (layer.parts.size() > 0)
{
ret[extr_nr] = true;
}
}
}
return ret;
}
std::vector< bool > SliceDataStorage::getExtrudersUsed()
{
std::vector<bool> ret;
ret.resize(meshgroup->getExtruderCount(), false);
ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
{ // process brim/skirt
for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
{
if (skirt[extr_nr].size() > 0)
{
ret[extr_nr] = true;
continue;
}
}
}
// TODO: ooze shield, draft shield ..?
// support
// support is presupposed to be present...
ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
ret[getSettingAsIndex("support_extruder_nr")] = true;
ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
// all meshes are presupposed to actually have content
for (SliceMeshStorage& mesh : meshes)
{
ret[mesh.getSettingAsIndex("extruder_nr")] = true;
}
return ret;
}
+3 -33
Ver Arquivo
@@ -24,19 +24,6 @@ public:
std::vector<Polygons> insets; //!< The skin can have perimeters so that the skin lines always start at a perimeter instead of in the middle of an infill cell.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
/*!
* A ReinforcementWall is like an insulated wall behind the outer walls.
* It consists of an area with (generally more dense) infill and perimeters on the inside.
* On the outside it has the outer walls, or the inner walls of another ReinforcementWall.
*/
class ReinforcementWall
{
public:
Polygons wall_reinforcement_area; //!< The infill of the reinforced wall
std::vector<Polygons> wall_reinforcement_axtra_walls; //!< The extra walls on the inside of the reinforcement infill
};
/*!
The SliceLayerPart is a single enclosed printable area for a single layer. (Also known as islands)
It's filled during the FffProcessor.processSliceData(.), where each step uses data from the previous steps.
@@ -50,7 +37,6 @@ public:
std::vector<Polygons> insets; //!< The insets are generated with: an offset of (index * line_width + line_width/2) compared to the outline. The insets are also known as perimeters, and printed inside out.
std::vector<SkinPart> skin_parts; //!< The skin parts which are filled for 100% with lines and/or insets.
std::vector<Polygons> infill_area; //!< The infill_area are the areas which need to be filled with sparse (0-99%) infill. The infill_area is an array to support thicker layers of sparse infill. infill_area[n] is infill_area of (n+1) layers thick.
std::vector<ReinforcementWall> reinforcement_walls; //!< The reinforcement walls for this part. Order: from outter to inner reinforcement wall.
Polygons perimeterGaps; //!< The gaps introduced by avoidOverlappingPerimeters which would otherwise be overlapping perimeters.
};
@@ -131,14 +117,13 @@ public:
GCodePathConfig insetX_config;
GCodePathConfig skin_config;
std::vector<GCodePathConfig> infill_config;
GCodePathConfig wall_reinforcement_config;
SliceMeshStorage(SettingsBaseVirtual* settings)
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, "WALL-OUTER"), insetX_config(&retraction_config, "WALL-INNER"), skin_config(&retraction_config, "SKIN"), wall_reinforcement_config(&retraction_config, "SUPPORT")
: SettingsMessenger(settings), layer_nr_max_filled_layer(0), inset0_config(&retraction_config, PrintFeatureType::OuterWall), insetX_config(&retraction_config, PrintFeatureType::InnerWall), skin_config(&retraction_config, PrintFeatureType::Skin)
{
infill_config.reserve(MAX_INFILL_COMBINE);
for(int n=0; n<MAX_INFILL_COMBINE; n++)
infill_config.emplace_back(&retraction_config, "FILL");
infill_config.emplace_back(&retraction_config, PrintFeatureType::Infill);
}
};
@@ -188,7 +173,7 @@ public:
for (int extruder = 0; extruder < meshgroup->getExtruderCount(); extruder++)
{
RetractionConfig* extruder_retraction_config = &retraction_config_per_extruder[extruder];
skirt_config.emplace_back(extruder_retraction_config, "SKIRT");
skirt_config.emplace_back(extruder_retraction_config, PrintFeatureType::Skirt);
}
return ret;
}
@@ -227,21 +212,6 @@ public:
* \param include_helper_parts whether to include support and prime tower
*/
Polygons getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts);
/*!
* Get the extruder numbers of all extruders used in a given layer.
*
* \param layer_nr the index of the layer for which to get the extruders used (negative layer numbers indicate the raft)
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed(int layer_nr);
/*!
* Get the extruders used.
*
* \return a vector of bools indicating whether the extruder with corresponding index is used in this layer.
*/
std::vector<bool> getExtrudersUsed();
};
}//namespace cura
+7 -7
Ver Arquivo
@@ -35,7 +35,7 @@ Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_thi
return joined;
}
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
{
// initialization of supportAreasPerLayer
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
@@ -46,11 +46,11 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
if (mesh.getSettingBoolean("support_roof_enable"))
{
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"), commandSocket);
generateSupportRoofs(storage, supportAreas, layer_count, mesh.getSettingInMicrons("layer_height"), mesh.getSettingInMicrons("support_roof_height"));
}
else
{
@@ -80,7 +80,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
*
* for support buildplate only: purge all support not connected to buildplate
*/
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket)
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
@@ -114,7 +114,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
int layerThickness = mesh.getSettingInMicrons("layer_height");
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
@@ -252,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int m
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
}
// do stuff for when support on buildplate only
@@ -409,7 +409,7 @@ void AreaSupport::handleWallStruts(
}
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket)
void AreaSupport::generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height)
{
int roof_layer_count = support_roof_height / layerThickness;
+3 -6
Ver Arquivo
@@ -15,9 +15,8 @@ public:
* Generate the support areas and support roof areas for all models.
* \param storage data storage containing the input layer outline data and containing the output support storage per layer
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count, CommandSocket* commandSocket);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count);
private:
/*!
@@ -28,9 +27,8 @@ private:
* \param storage data storage containing the input layer outline data
* \param mesh_idx The index of the object for which to generate support areas
* \param layer_count total number of layers
* \param commandSocket Socket over which to report the progress
*/
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas, CommandSocket* commandSocket);
static void generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas);
@@ -39,11 +37,10 @@ private:
*
* \param storage Output storage: support area + support roof area output
* \param supportAreas The basic support areas for the current mesh
* \param commandSocket Socket over which to report the progress
* \param layerThickness The layer height
* \param support_roof_height The thickness of the hammock in z directiontt
*/
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height, CommandSocket* commandSocket);
static void generateSupportRoofs(SliceDataStorage& storage, std::vector<Polygons>& supportAreas, unsigned int layer_count, int layerThickness, int support_roof_height);
/*!
* Join current support layer with the support of the layer above, (make support conical) and perform smoothing etc operations.
+37
Ver Arquivo
@@ -30,6 +30,43 @@ public:
{
calculate(polys);
}
/*!
* \brief Creates an axis aligned bounding box around the specified polygon.
*
* The bounding box will fit snugly around the polygon.
*
* \param polygon The polygon to create a bounding box for.
*/
AABB(PolygonRef& polygon)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
for(Point point : polygon)
{
include(point);
}
}
/*!
* \brief Creates an axis aligned bounding box around the specified set of
* polygons.
*
* The bounding box will fit around all polygons.
*
* \param polygons A vector of polygons that should be included in the
* bounding box.
*/
AABB(std::vector<PolygonRef>& polygons)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
for(PolygonRef polygon : polygons)
{
for(Point point : polygon)
{
include(point);
}
}
}
void calculate(Polygons& polys)
{
-5
Ver Arquivo
@@ -205,11 +205,6 @@ public:
// if (! emplaced.second)
// logError("Error! BucketGrid2D couldn't insert object!");
};
};
}//namespace cura
+39 -1
Ver Arquivo
@@ -140,6 +140,44 @@ public:
}
}
/*!
* \brief Draws the specified list of polygons on the canvas.
*
* Each polygon is drawn in sequence.
*
* \param polygons A vector of polygons to draw.
* \param colour The colour of the fill of the polygons.
* \param outline_colour The colour of the outline of the polygons.
*/
void writeAreas(const std::vector<PolygonRef>& polygons, Color colour = Color::GRAY, Color outline_colour = Color::BLACK)
{
for(PolygonRef polygon : polygons)
{
writeAreas(polygon,colour,outline_colour);
}
}
/*!
* \brief Draws the specified polygon on the canvas.
*
* The polygon is always closed and has an outline and fill colour that you
* can specify.
*
* \param polygon The polygon to draw on the canvas.
* \param colour The colour of the polygon.
* \param outline_colour The colour of the outline of the polygon.
*/
void writeAreas(PolygonRef polygon, Color colour = Color::GRAY, Color outline_colour = Color::BLACK)
{
fprintf(out, "<polygon points=\"");
for(Point point : polygon)
{
Point transformed = transform(point);
fprintf(out, "%lli,%lli ", transformed.X, transformed.Y);
}
fprintf(out, "\" fill=\"%s\" stroke=\"%s\" stroke-width=\"4\" />\n", toString(colour).c_str(),toString(outline_colour).c_str());
}
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
{
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
@@ -209,7 +247,7 @@ public:
{
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\" stroke=\"%s\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
}
/*!
+405
Ver Arquivo
@@ -0,0 +1,405 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef TRAVELLINGSALESMAN_H
#define TRAVELLINGSALESMAN_H
#include "intpoint.h" //For the Point type.
#include <algorithm> //For std::shuffle to randomly shuffle the array.
#include <list> //For std::list, which is used to insert stuff in the result in constant time.
#include <random> //For the RNG. Note: CuraEngine should be deterministic! Always use a FIXED SEED!
namespace cura
{
/*!
* \brief Struct that holds all information of one element of the path.
*
* It needs to know the actual element in the path, but also where the element's
* own path starts and ends.
*
* \tparam E The type of element data stored in this waypoint. Note that these
* are copied into the waypoint on construction and out of the waypoint right
* before deletion.
*/
template<class E> struct Waypoint
{
/*!
* \brief Constructs a new waypoint with the specified possible start and
* end points and the specified element.
*
* \param orientations The possible start and end points of the waypoints
* for each orientation the element could be placed in.
* \param element The element that's to be bound to this waypoint.
*/
Waypoint(std::vector<std::pair<Point, Point>> orientations, E element) : orientation_indices(orientations), element(element)
{
}
/*!
* \brief The possible orientations in which the waypoint could be placed in
* the path.
*
* This defines in what direction or way the element in this waypoint should
* be traversed in the final path. The Travelling Salesman solution only
* requires the start and end point of this traversal in order to piece the
* waypoint into the path.
*/
std::vector<std::pair<Point, Point>> orientation_indices;
/*!
* \brief The actual element this waypoint holds.
*/
E element;
/*!
* \brief The optimal orientation of this waypoint in the final path.
*
* This is computed during the <em>TravellingSalesman::findPath</em>
* function. It indicates an index in \link orientations that provides the
* shortest path.
*/
size_t best_orientation_index;
};
/*!
* \brief A class of functions implementing solutions of Travelling Salesman.
*
* Various variants can be implemented here, such as the shortest path past a
* set of points or of lines.
*
* \tparam E The type of elements that must be ordered by this instance of
* <em>TravellingSalesman</em>. Note that each element is copied twice in a run
* of \link findPath, so if this type is difficult to copy, provide pointers to
* the elements instead.
*/
template<class E> class TravellingSalesman
{
typedef typename std::list<Waypoint<E>*>::iterator WaypointListIterator; //To help the compiler with templates in templates.
public:
/*!
* \brief Constructs an instance of Travelling Salesman.
*
* \param get_orientations A function to get possible orientations for
* elements in the path. Each orientation defines a possible way that the
* element could be inserted in the path. To do that it must provide a
* start point and an end point for each orientation.
*/
TravellingSalesman(std::function<std::vector<std::pair<Point, Point>>(E)> get_orientations) : get_orientations(get_orientations)
{
//Do nothing. All parameters are already copied to fields.
}
/*!
* \brief Destroys the instance, releasing all memory used by it.
*/
virtual ~TravellingSalesman();
/*!
* \brief Computes a short path along all specified elements.
*
* A short path is computed that includes all specified elements, but not
* always the shortest path. Finding the shortest path is known as the
* Travelling Salesman Problem, and this is an NP-complete problem. The
* solution returned by this function is just a heuristic approximation.
*
* The approximation will try to insert random elements at the best location
* in the current path, thereby incrementally constructing a good path. Each
* element can be inserted in multiple possible orientations, defined by the
* <em>get_orientations</em> function.
*
* \param elements The elements past which the path must run.
* \param element_orientations Output parameter to indicate for each element
* in which orientation it must be placed to minimise the travel time. The
* resulting integers correspond to the index of the options given by the
* <em>get_orientations</em> constructor parameter.
* \param starting_point A fixed starting point of the path, if any. If this
* is <em>nullptr</em>, the path may start at the start or end point of any
* element, depending on which the heuristic deems shortest.
* \return A vector of elements, in an order that would make a short path.
*/
std::vector<E> findPath(std::vector<E> elements, std::vector<size_t>& element_orientations, Point* starting_point = nullptr);
protected:
/*!
* \brief Function to use to get the possible orientations of an element.
*
* Each orientation has a start point and an end point, in that order.
*/
std::function<std::vector<std::pair<Point, Point>>(E)> get_orientations;
private:
/*!
* \brief Puts all elements in waypoints, caching their endpoints.
*
* The <em>get_start</em> and <em>get_end</em> functions are called on each
* element. The results are stored along with the element in a waypoint and
* a pointer to the waypoint is added to the resulting vector.
*
* Note that this creates a waypoint for each element on the heap. These
* waypoints need to be deleted when the algorithm is done. This is why this
* function must always stay private.
*
* \param elements The elements to put into waypoints.
* \return A vector of waypoints with the specified elements in them.
*/
std::vector<Waypoint<E>*> fillWaypoints(std::vector<E> elements);
/*!
* \brief Tries to insert a waypoint at the first position in the list.
*
* It will try to insert the waypoint at all possible orientations. If it
* finds an orientation with an insertion distance that is less than the
* best distance, it will update the \p best_distance, \p best_orientation
* and \p best_insert variables with the parameters of this insertion.
*
* \param waypoint The waypoint to insert in the list.
* \param starting_point The starting point to insert the waypoint after, if
* any.
* \param first_element The first element of the list to insert the waypoint
* before.
* \param best_distance The current best distance of inserting the waypoint.
* This parameter is changed if a better insertion is found.
* \param best_orientation The current best orientation of inserting the
* waypoint. This parameter is changed if a better insertion is found.
* \param best_insert The current best place to insert the waypoint. This
* parameter is changed if a better insertion is found.
*/
inline void tryInsertFirst(Waypoint<E>* waypoint, Point* starting_point, WaypointListIterator first_element, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert);
/*!
* \brief Tries to insert a waypoint at the last position in the list.
*
* It will try to insert the waypoint at all possible orientations. If it
* finds an orientation with an insertion distance that is less than the
* best distance, it will update the \p best_distance, \p best_orientation
* and \p best_insert variables with the parameters of this insertion.
*
* \param waypoint The waypoint to insert in the list.
* \param last_element The last element of the list to insert the waypoint
* after.
* \param The WaypointListIterator to use to indicate that it should insert
* the new waypoint after the last element of the list, if it should find
* that as a new best position.
* \param best_distance The current best distance of inserting the waypoint.
* This parameter is changed if a better insertion is found.
* \param best_orientation The current best orientation of inserting the
* waypoint. This parameter is changed if a better insertion is found.
* \param best_insert The current best place to insert the waypoint. This
* parameter is changed if a better insertion is found.
*/
inline void tryInsertLast(Waypoint<E>* waypoint, WaypointListIterator last_element, WaypointListIterator after_insert, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert);
/*!
* \brief Tries to insert a waypoint at a position in the centre of the
* list.
*
* It will try to insert the waypoint at all possible orientations. If it
* finds an orientation with an insertion distance that is less than the
* best distance, it will update the \p best_distance, \p best_orientation
* and \p best_insert variables with the parameters of this insertion.
*
* \param waypoint The waypoint to insert in the list.
* \param before_insert The element after which to insert the new waypoint.
* \param after_insert The element before which to insert the new waypoint.
* \param best_distance The current best distance of inserting the waypoint.
* This parameter is changed if a better insertion is found.
* \param best_orientation The current best orientation of inserting the
* waypoint. This parameter is changed if a better insertion is found.
* \param best_insert The current best place to insert the waypoint. This
* parameter is changed if a better insertion is found.
*/
inline void tryInsertMiddle(Waypoint<E>* waypoint, WaypointListIterator before_insert, WaypointListIterator after_insert, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert);
};
////BELOW FOLLOWS THE IMPLEMENTATION.////
template<class E> TravellingSalesman<E>::~TravellingSalesman()
{
//Do nothing.
}
template<class E> std::vector<E> TravellingSalesman<E>::findPath(std::vector<E> elements, std::vector<size_t>& element_orientations, Point* starting_point)
{
/* This approximation algorithm of TSP implements the random insertion
* heuristic. Random insertion has in tests proven to be almost as good as
* Christofides (111% of the optimal path length rather than 110% on random
* graphs) but is much faster to compute. */
if (elements.empty())
{
return std::vector<E>();
}
auto rng = std::default_random_engine(0xDECAFF); //Always use a fixed seed! Wouldn't want it to be nondeterministic.
std::vector<Waypoint<E>*> shuffled = fillWaypoints(elements);
std::shuffle(shuffled.begin(), shuffled.end(), rng); //"Randomly" shuffles the waypoints.
std::list<Waypoint<E>*> result;
if (!starting_point) //If there is no starting point, just insert the initial element.
{
shuffled[0]->best_orientation_index = 0; //Choose an arbitrary orientation for the first element.
result.push_back(shuffled[0]); //Due to the check at the start, we know that shuffled always contains at least 1 element.
}
else //If there is a starting point, insert the initial element after it.
{
int64_t best_distance = std::numeric_limits<int64_t>::max(); //Change in travel distance to insert the waypoint. Minimise this distance by varying the orientation.
size_t best_orientation_index; //In what orientation to insert the element.
for (size_t orientation_index = 0; orientation_index < shuffled[0]->orientation_indices.size(); orientation_index++)
{
int64_t distance = vSize(*starting_point - shuffled[0]->orientation_indices[orientation_index].first); //Distance from the starting point to the start point of this element.
if (distance < best_distance)
{
best_distance = distance;
best_orientation_index = orientation_index;
}
}
shuffled[0]->best_orientation_index = best_orientation_index;
result.push_back(shuffled[0]);
}
//Now randomly insert the rest of the points.
for (size_t next_to_insert = 1; next_to_insert < shuffled.size(); next_to_insert++)
{
Waypoint<E>* waypoint = shuffled[next_to_insert];
int64_t best_distance = std::numeric_limits<int64_t>::max(); //Change in travel distance to insert the waypoint. Minimise this distance by varying the insertion point and orientation.
WaypointListIterator best_insert; //Where to insert the element. It will be inserted before this element. If it's nullptr, insert at the very front.
size_t best_orientation_index; //In what orientation to insert the element.
//First try inserting before the first element.
tryInsertFirst(waypoint, starting_point, result.begin(), &best_distance, &best_orientation_index, &best_insert);
//Try inserting at the other positions.
for (WaypointListIterator before_insert = result.begin(); before_insert != result.end(); before_insert++)
{
WaypointListIterator after_insert = before_insert;
after_insert++; //Get the element after the current element.
if (after_insert == result.end()) //There is no next element. We're inserting at the end of the path.
{
tryInsertLast(waypoint, before_insert, after_insert, &best_distance, &best_orientation_index, &best_insert);
}
else //There is a next element. We're inserting somewhere in the middle.
{
tryInsertMiddle(waypoint, before_insert, after_insert, &best_distance, &best_orientation_index, &best_insert);
}
}
//Actually insert the waypoint at the best position we found.
waypoint->best_orientation_index = best_orientation_index;
if (best_insert == result.end()) //We must insert at the very end.
{
result.push_back(waypoint);
}
else //We must insert before best_insert.
{
result.insert(best_insert, waypoint);
}
}
//Now that we've inserted all points, linearise them into one vector.
std::vector<E> result_vector;
result_vector.reserve(elements.size());
element_orientations.clear(); //Prepare the element_orientations vector for storing in which orientation each element should be placed.
element_orientations.reserve(elements.size());
for (Waypoint<E>* waypoint : result)
{
result_vector.push_back(waypoint->element);
element_orientations.push_back(waypoint->best_orientation_index);
delete waypoint; //Free the waypoint from memory. It is no longer needed from here on, since we copied the element in it to the output.
}
return result_vector;
}
template<class E> std::vector<Waypoint<E>*> TravellingSalesman<E>::fillWaypoints(std::vector<E> elements)
{
std::vector<Waypoint<E>*> result;
result.reserve(elements.size());
for (E element : elements) //Put every element in a waypoint.
{
Waypoint<E>* waypoint = new Waypoint<E>(get_orientations(element), element); //Yes, this must be deleted when the algorithm is done!
result.push_back(waypoint);
}
return result;
}
template<class E> inline void TravellingSalesman<E>::tryInsertFirst(Waypoint<E>* waypoint, Point* starting_point, WaypointListIterator first_element, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert)
{
if (!waypoint or !best_distance or !best_orientation_index or !best_insert) //Input checking.
{
return;
}
for (size_t orientation = 0; orientation < waypoint->orientation_indices.size(); orientation++)
{
int64_t before_distance = 0;
if (starting_point) //If there is a starting point, we're inserting between the first point and the starting point.
{
before_distance = vSize(*starting_point - waypoint->orientation_indices[orientation].first);
}
const Point end_of_this = waypoint->orientation_indices[orientation].second;
const Point start_of_first = (*first_element)->orientation_indices[(*first_element)->best_orientation_index].first;
const int64_t after_distance = vSize(end_of_this - start_of_first); //From the end of this element to the start of the first element.
const int64_t distance = before_distance + after_distance;
if (distance < *best_distance)
{
*best_distance = distance;
*best_insert = first_element;
*best_orientation_index = orientation;
}
}
}
template<class E> inline void TravellingSalesman<E>::tryInsertLast(Waypoint<E>* waypoint, WaypointListIterator last_element, WaypointListIterator after_insert, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert)
{
if (!waypoint or !best_distance or !best_orientation_index or !best_insert) //Input checking.
{
return;
}
for (size_t orientation = 0; orientation < waypoint->orientation_indices.size(); orientation++)
{
const Point end_of_last = (*last_element)->orientation_indices[(*last_element)->best_orientation_index].second;
const Point start_of_this = waypoint->orientation_indices[orientation].first;
const int64_t distance = vSize(end_of_last - start_of_this); //From the end of the last element to the start of this element.
if (distance < *best_distance)
{
*best_distance = distance;
*best_insert = after_insert;
*best_orientation_index = orientation;
}
}
}
template<class E> inline void TravellingSalesman<E>::tryInsertMiddle(Waypoint<E>* waypoint, WaypointListIterator before_insert, WaypointListIterator after_insert, int64_t* best_distance, size_t* best_orientation_index, WaypointListIterator* best_insert)
{
if (!waypoint or !best_distance or !best_orientation_index or !best_insert) //Input checking.
{
return;
}
for (size_t orientation = 0; orientation < waypoint->orientation_indices.size(); orientation++)
{
const Point end_of_before = (*before_insert)->orientation_indices[(*before_insert)->best_orientation_index].second;
const Point start_of_after = (*after_insert)->orientation_indices[(*after_insert)->best_orientation_index].first;
const Point start_of_this = waypoint->orientation_indices[orientation].first;
const Point end_of_this = waypoint->orientation_indices[orientation].second;
const int64_t removed_distance = vSize(end_of_before - start_of_after); //Distance of the original move that we'll remove.
const int64_t before_distance = vSize(end_of_before - start_of_this); //From end of previous element to start of this element.
const int64_t after_distance = vSize(end_of_this - start_of_after); //From end of this element to start of next element.
const int64_t distance = before_distance + after_distance - removed_distance;
if (distance < *best_distance)
{
*best_distance = distance;
*best_insert = after_insert;
*best_orientation_index = orientation;
}
}
}
}
#endif /* TRAVELLINGSALESMAN_H */
+21 -12
Ver Arquivo
@@ -146,32 +146,41 @@ public:
if (ax_size < 0)
{// b is 'before' segment ac
if (b_is_beyond_ac)
{
*b_is_beyond_ac = -1;
}
if (b_is_beyond_ac) { *b_is_beyond_ac = -1; }
return vSize2(ab);
}
if (ax_size > ac_size)
{// b is 'after' segment ac
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 1;
}
if (b_is_beyond_ac) { *b_is_beyond_ac = 1; }
return vSize2(b - c);
}
if (b_is_beyond_ac)
{
*b_is_beyond_ac = 0;
}
if (b_is_beyond_ac) { *b_is_beyond_ac = 0; }
Point ax = ac * ax_size / ac_size;
Point bx = ab - ax;
return vSize2(bx);
// return vSize2(ab) - ax_size*ax_size; // less accurate
}
/*!
* Checks whether the minimal distance between two line segments is at most \p max_dist
* The first line semgent is given by end points \p a and \p b, the second by \p c and \p d.
*
* \param a One end point of the first line segment
* \param b Another end point of the first line segment
* \param c One end point of the second line segment
* \param d Another end point of the second line segment
* \param max_dist The maximal distance between the two line segments for which this function will return true.
*/
static bool lineSegmentsAreCloserThan(const Point& a, const Point& b, const Point& c, const Point& d, int64_t max_dist)
{
int64_t max_dist2 = max_dist * max_dist;
return getDist2FromLineSegment(a, c, b) <= max_dist2
|| getDist2FromLineSegment(a, d, b) <= max_dist2
|| getDist2FromLineSegment(c, a, d) <= max_dist2
|| getDist2FromLineSegment(c, b, d) <= max_dist2;
}
};
-14
Ver Arquivo
@@ -21,20 +21,6 @@
namespace cura {
enum PolygonType
{
NoneType,
Inset0Type,
InsetXType,
SkinType,
SupportType,
SkirtType,
InfillType,
SupportInfillType,
MoveCombingType,
MoveRetractionType
};
class PartsView;
+2 -2
Ver Arquivo
@@ -66,10 +66,10 @@ public:
* \param polygons The polygons onto which to move the point
* \param from The point to move.
* \param distance The distance by which to move the point.
* \param maxDist2 The squared maximal allowed distance from the point to the nearest polygon.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \return The index to the polygon onto which we have moved the point.
*/
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max());
static unsigned int moveInside(Polygons& polygons, Point& from, int distance = 0, int64_t max_dist2 = std::numeric_limits<int64_t>::max());
/*!
* Find the two points in two polygons with the smallest distance.
+4 -4
Ver Arquivo
@@ -25,9 +25,9 @@ void GCodePlannerTest::setUp()
fan_speed_layer_time_settings.cool_fan_speed_min = 0;
fan_speed_layer_time_settings.cool_fan_speed_max = 1;
fan_speed_layer_time_settings.cool_min_speed = 0.5;
// Command Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
// socket storage nr height position extruder time settings combing offset avoid distance
gCodePlanner = new GCodePlanner(nullptr, *storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
// Slice layer z layer last current fan speed and layer retraction comb travel travel avoid
// storage nr height position extruder time settings combing offset avoid distance
gCodePlanner = new GCodePlanner(*storage, 0, 0, 0.1, Point(0,0), 0, fan_speed_layer_time_settings, false, 100, false, 50 );
}
void GCodePlannerTest::tearDown()
@@ -46,7 +46,7 @@ void GCodePlannerTest::computeNaiveTimeEstimatesRetractionTest()
GCodeExport gcode;
GCodePathConfig configuration = storage->travel_config;
gCodePlanner->addExtrusionMove(Point(0,0),&configuration,1.0f); //Need to have at least one path to have a configuration.
gCodePlanner->addExtrusionMove(Point(0, 0), &configuration, SpaceFillType::Lines, 1.0f); //Need to have at least one path to have a configuration.
TimeMaterialEstimates before_retract = gCodePlanner->computeNaiveTimeEstimates();
gCodePlanner->writeRetraction(gcode,(unsigned int)0,(unsigned int)0); //Make a retract.
TimeMaterialEstimates after_retract = gCodePlanner->computeNaiveTimeEstimates();
+91
Ver Arquivo
@@ -0,0 +1,91 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "TravellingSalesmanTest.h"
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(TravellingSalesmanTest);
void TravellingSalesmanTest::setUp()
{
tsp_points = new TravellingSalesman<Point>([&](Point point) -> std::vector<std::pair<Point, Point>> { return { std::make_pair(point, point) }; }); //For points, just return the point itself as both start and end points.
tsp_paths = new TravellingSalesman<std::vector<Point>>( //For paths, return the first and last element of the path.
[&](std::vector<Point> path) -> std::vector<std::pair<Point, Point>>
{
if (path.empty())
{
return { std::make_pair(Point(0, 0), Point(0, 0)) };
}
std::vector<std::pair<Point, Point>> result;
result.push_back(std::make_pair(path[0], path.back()));
result.push_back(std::make_pair(path.back(), path[0]));
return result;
}
);
}
void TravellingSalesmanTest::tearDown()
{
delete tsp_points;
delete tsp_paths;
}
void TravellingSalesmanTest::twoPointsTest()
{
std::vector<Point> points;
points.push_back(Point(0,0));
points.push_back(Point(100,0));
std :: vector<size_t> orientations;
std :: vector<Point> result = tsp_points -> findPath(points, orientations);
bijective(points,result); //Assert that all points in the two vectors are equal.
}
void TravellingSalesmanTest::fivePointsTest()
{
std::vector<Point> points;
points.push_back(Point(-100,0));
points.push_back(Point(0,-100));
points.push_back(Point(0,100));
points.push_back(Point(100,0));
points.push_back(Point(0,0));
std :: vector<size_t> orientations;
std :: vector<Point> result = tsp_points -> findPath(points, orientations);
bijective(points,result); //Assert that all points in the two vectors are equal.
}
void TravellingSalesmanTest::bijective(std::vector<Point> first,std::vector<Point> second)
{
CPPUNIT_ASSERT_MESSAGE("The resulting path does not have the same length as the provided point set.",first.size() == second.size());
std::vector<bool> matched; //Indicates which of the elements of the second vector are already matched.
matched.reserve(second.size());
for(size_t i = 0;i < matched.size();i++) //Initialise all matched to false.
{
matched.push_back(false);
}
for(Point first_point : first)
{
for(size_t second_point_index = 0;second_point_index < second.size();second_point_index++)
{
if(first_point == second[second_point_index])
{
matched[second_point_index] = true;
goto CONTINUE_FIRST;
}
}
CPPUNIT_FAIL("A point in the original path was not included in the result.");
CONTINUE_FIRST: ; //Continue with the outer loop.
}
for(bool match : matched) //Check if there are any unmatched points left.
{
CPPUNIT_ASSERT_MESSAGE("A point in the result did not come from the original path.",match);
}
}
}
+73
Ver Arquivo
@@ -0,0 +1,73 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef TRAVELLINGSALESMANTEST_H
#define TRAVELLINGSALESMANTEST_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <../src/utils/TravellingSalesman.h>
namespace cura
{
class TravellingSalesmanTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(TravellingSalesmanTest);
CPPUNIT_TEST(twoPointsTest);
CPPUNIT_TEST(fivePointsTest);
CPPUNIT_TEST_SUITE_END();
public:
/*!
* \brief Sets up the test suite to prepare for testing.
*
* In this case, an instance of <em>TravellingSalesman</em> is produced with
* pre-defined functions for getting the start and end points of a point and
* another instance with paths.
*/
void setUp();
/*!
* \brief Tears down the test suite when testing is done.
*
* The two instances of TravellingSalesman are deleted.
*/
void tearDown();
//The test cases.
void twoPointsTest();
void fivePointsTest();
private:
/*!
* \brief A TSP solver that approximates the shortest path between a set of
* points.
*/
TravellingSalesman<Point>* tsp_points;
/*!
* \brief A TSP solver that approximates the shortest path that includes a
* set of paths.
*/
TravellingSalesman<std::vector<Point>>* tsp_paths;
/*!
* \brief Asserts that the two sets implied by the provided vectors are
* bijective.
*
* They are bijective if and only if all points in the first vector are also
* in the second vector and vice versa.
*
* \param first The first vector to check whether it is bijective with the
* second vector.
* \param second The second vector to check whether it is bijective with the
* first vector.
*/
void bijective(std::vector<Point> first,std::vector<Point> second);
};
}
#endif /* TRAVELLINGSALESMANTEST_H */
+21 -16
Ver Arquivo
@@ -64,12 +64,6 @@ class TestResults():
self._testsuites.append(suite)
return suite
def getFailureCount(self):
result = 0
for testsuite in self._testsuites:
result += testsuite.getFailureCount()
return result
## Save the test results to the file given in the filename.
def saveXML(self, filename):
xml = ElementTree.Element("testsuites")
@@ -254,9 +248,23 @@ class EngineTest():
def getResults(self):
return self._test_results
def main(engine, model_path):
filenames = sorted(os.listdir(model_path), key=lambda filename: os.stat(os.path.join(model_path, filename)).st_size)
filenames = list(filter(lambda filename: filename.lower().endswith(".stl"), filenames))
for filename in filenames:
print("Slicing: %s (%d/%d)" % (filename, filenames.index(filename), len(filenames)))
t = time.time()
p = subprocess.Popen([engine, "-vv", os.path.join(model_path, filename)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.wait() != 0:
print ("Engine failed to report success on test object: %s" % (filename))
print(stderr.decode("utf-8", "replace").split("\n")[-5:])
sys.exit(1)
else:
print("Slicing took: %f" % (time.time() - t))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CuraEngine testing script")
parser.add_argument("--simple", action="store_true", help="Only run the single test, exit")
parser.add_argument("json", type=str, help="Machine JSON file to use")
parser.add_argument("engine", type=str, help="Engine executable")
parser.add_argument("models", type=str, nargs="+", help="List of models to use for testing")
@@ -264,13 +272,10 @@ if __name__ == "__main__":
et = EngineTest(args.json, args.engine, args.models)
if et.testDefaults() == 0:
if not args.simple:
et.testSingleChanges()
if et.testSingleRandom() == 0:
et.testDualRandom()
if et.testAllRandom(10) == 0:
et.testAllRandom(100)
et.testSingleChanges()
if et.testSingleRandom() == 0:
et.testDualRandom()
if et.testAllRandom(10) == 0:
et.testAllRandom(100)
et.getResults().saveXML("output.xml")
if args.simple:
if et.getResults().getFailureCount() > 0:
sys.exit(1)