Comparar commits

..

119 Commits

Autor SHA1 Mensagem Data
Arjen Hiemstra a119be9021 Add support for setting CuraEngine version from CMake 2015-11-12 14:53:59 +01:00
Ghostkeeper 68602df6ee Spelling fix
This would prevent the g-code flavour from being recognised in RepRap machines.
2015-11-09 13:52:40 +01:00
Tim Kuipers 92f5311aaf failsafe: extruder train defaults to global settings if it is not specified 2015-11-06 17:30:15 +01:00
Tim Kuipers 4f9f55ab06 lil: removed debug code 2015-11-06 17:22:45 +01:00
Tim Kuipers 2bfdb2d612 bugfix: outside combing was skipped sometimes 2015-11-06 17:10:31 +01:00
Tim Kuipers 026f82540f bugfix: outside combing was skipped sometimes 2015-11-06 16:53:33 +01:00
Tim Kuipers d87072f761 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-06 15:44:51 +01:00
Tim Kuipers f89a747836 bugfix: outside combing was skipped sometimes 2015-11-06 15:44:42 +01:00
Ghostkeeper 767803d2bf Fix more indenting
I think this is on me. I merged this code once.
2015-11-06 11:10:55 +01:00
Ghostkeeper a808e9e8ed Fix indenting
At one place there was an accidental extra four spaces. At another place the alignment of a comment was off since it was aligned to a comment above it which had moved to the left.
2015-11-06 11:10:55 +01:00
Tim Kuipers 087efefc16 bugfix: new speed wasn't set; fixes CURA-396 2015-11-06 10:42:59 +01:00
Tim Kuipers 09afe2264a bugfix: new speed wasn't set; fixes CURA-396 2015-11-06 10:40:49 +01:00
Tim Kuipers 5ede92fb1b bugfix: gcode flavor prepended for UM2 was incorrect 2015-11-05 21:00:29 +01:00
Ghostkeeper 321ef643a4 Use static cast instead of c-style cast
The static cast has more predictable behaviour across compilers.
2015-11-05 17:53:17 +01:00
Ghostkeeper c43c876cbc Offset PolyCrossings in combing after iteration of basicpath
The offset could sometimes make the basicpath loop get stuck.
2015-11-05 17:53:17 +01:00
Ghostkeeper 3d596f1765 Report error if SVG file couldn't be opened for writing
If for some reason the opening of the file failed, an error is given that indicates where the file was opened. It still continues writing because the nature of this class requires it to fail fast rather than continue silently, but it should be easier to debug now. If silent fail behaviour is desired for this class then we could decide on that later. It involves checking for if(out) at every write function.
2015-11-05 17:53:17 +01:00
Ghostkeeper ff747fa88f SVG filewriter to use const points
This way they can be directly filled in as points into a function call without making a variable for them and passing that variable by reference.
2015-11-05 17:53:17 +01:00
Tim Kuipers 41a1db1e55 feature: always show gcode flavor 2015-11-05 17:48:21 +01:00
Tim Kuipers 0717594a15 bugfix: walls were visualized when wall_line_count==0 2015-11-05 12:05:42 +01:00
Ghostkeeper c72b05463a Combing path-polygon intersection only offset along path
This is slightly more efficient to compute, though it allows for combing closer to the polygon than the specified combing distance if the edge approaches parallel with the desired combing path.

Contributes to issue CURA-286.
2015-11-04 14:04:50 +01:00
Ghostkeeper eb1acd02a2 Fixed combing offset bug
A point was being moved inside the polygon to obtain waypoints for combing through the inside of the polygon. Then it was being moved outside the polygon with a larger offset, but the second move used the inside point of the first move, causing the resulting offset to be a bit too small. This keeps a copy of the original waypoint before moving it inside, which it uses only for computing the outside waypoint.

Contributes to issue CURA-286.
2015-11-04 14:04:50 +01:00
Tim Kuipers 63eda27a50 no more error when no extruder trains are present 2015-11-04 11:17:25 +01:00
Tim Kuipers 5b1eeecf76 fix for support roof triangles infill 2015-11-04 11:16:50 +01:00
Tim Kuipers 41ede0727b Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-04 11:02:32 +01:00
Ghostkeeper 7ffec2e617 Remove unnecessary parallel line collision detection
Now that a collision is also triggered when the line crosses an endpoint of another line, the detection of collisions when the two lines are parallel is no longer needed.

Contributes to issue CURA-286.
2015-11-03 17:40:46 +01:00
Ghostkeeper 57ebcf0163 combineInfillLayers to stop if combining 1 or 0 layers
Combining 1 layer has no effect. Combining 0 layers is invalid. Therefore, if this function is made to combine 1 or 0 layers, it doesn't combine anything, and immediately returns.
2015-11-03 16:48:23 +01:00
Tim Kuipers ad29876617 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-11-03 09:17:42 +01:00
Tim Kuipers 07481a548c bugfix: settings overrides weren't working
Conflicts:
	src/settingRegistry.cpp
2015-11-03 09:17:32 +01:00
Ghostkeeper 7d70768178 Tell CppUnit to output XML
The standard XmlOutputter seems to output JUnit-style XML.
2015-11-02 15:08:28 +01:00
Ghostkeeper 9c201e3396 Remove old try for return code
Directly returning the boolean didn't work.
2015-11-02 14:50:57 +01:00
Ghostkeeper f00c091ed1 Add actual tests for getDist2FromLineSegmentTest
It tests various types of line segments with points in various places. The tests are currently already failing...
2015-11-02 14:37:03 +01:00
Ghostkeeper 4574e0917e getDist2FromLineSegment pass by const reference
This allows for calling the function with constexpr types.
2015-11-02 14:32:57 +01:00
Ghostkeeper 4ae1f5e6bf Succeeded CppUnit integration
If you add -DBUILD_TESTS=true to CMake, it will add a test target for each test file. These can be executed separately as executable file, or they can be ran all at once with 'make test'.
2015-11-02 13:17:11 +01:00
Ghostkeeper ae02a91a5d Fix license
Yeah I've done that more often... Sorry.
2015-11-02 09:26:13 +01:00
Ghostkeeper 0781a8f4ef Initial setup of CppUnit in CMake
I've setup a basic test but it doesn't actually do anything.
2015-10-30 17:41:48 +01:00
Ghostkeeper c6e201e307 Don't combine infill layers if infill is empty
If the infill density is 0 and it has to combine layers, it would try to access layers below the first layer that don't exist. Instead it will now not combine at all, saving some processing power too.

Contributes to issue Cura-285.
2015-10-30 12:34:31 +01:00
Tim Kuipers 1d073600e4 Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-10-29 11:36:59 +01:00
Tim Kuipers 8f6cb82421 merge of raft bugfixes 2015-10-29 11:29:43 +01:00
Ghostkeeper 1023102e3a Log an error when layer height is not positive
In addition to returning false, sliceModel now also logs an error when the layer height is invalid.
2015-10-28 16:38:27 +01:00
Ghostkeeper 0ac334c849 Log an error when slicing failed
In the command socket, when slicing failed, it would just ignore the failure and continue. This is fine, but let's at least log an error when that happens.
2015-10-28 16:34:59 +01:00
Ghostkeeper 696e0132f4 Break when layer height is 0
After discussing this with several colleagues, this seems to be the desired behaviour.
2015-10-28 16:19:06 +01:00
Ghostkeeper fb284397c2 Only assert for insane g-code values in debug mode
A preprocessor definition is added if CMAKE_BUILD_TYPE == DEBUG. So for release builds, set CMAKE_BUILD_TYPE properly to RELEASE and CuraEngine will no longer stop at the assertion.
2015-10-28 15:17:33 +01:00
Nicanor Romero Venier 71e486d007 Accepts 'help' as a known command 2015-10-27 10:25:17 +01:00
Ghostkeeper eb2ae0d699 Correctly handle case of no layers
If there are no layers, the engine used to crash with an assertion error. Instead, it now returns an empty slice, which is correct behaviour. Fixes issue CURA-323.
2015-10-26 12:11:27 +01:00
Ghostkeeper bcee6c7cc2 No negative initial layer thickness
If this setting is zero or negative, make it the minimum layer thickness: 1 micron. Also fixed a mistake in the comment of the similar check for the normal layer thickness setting.
2015-10-26 11:52:32 +01:00
Ghostkeeper 3a28f5d547 No negative layer thickness
If this setting is zero or negative, make it the minimum layer thickness: 1 micron.
2015-10-26 11:47:53 +01:00
Ghostkeeper 4b11b5946e Remove infill_sparse_combine setting
In discussion, we deemed infill_sparse_thickness more clear to the user and infill_sparse_combine superfluous, since it is really an intermediary value used by the engine to round the thickness up. So now it is actually computed in the engine. Accompanying this commit will be a commit to Cura to remove the setting from the .json file.
2015-10-23 12:14:06 +02:00
Ghostkeeper d558e24b43 Re-do minimum combine_infill_layers check
This was accidentally removed in the merge...
2015-10-23 11:43:51 +02:00
Ghostkeeper 5d6f45a71d Merge branch 'master' of http://github.com/Ultimaker/CuraEngine
Changed files:
 * src/skin.cpp
2015-10-23 11:41:29 +02:00
Ghostkeeper d827cc0f92 Never combine 0 layers
Infill_combine_layers of 0 is an invalid value. So perform this sanity check to make sure that infill_combine_layers is always at least 1.
2015-10-23 11:25:06 +02:00
Ghostkeeper 263eb370e9 Handle edge case of parallel line intersection
If a line was parallel with an edge of the polygon, the line should be marked as intersecting that edge to properly register the scanline crossing. This handles that edge case for most cases by adding one intersection. In some cases it is plausible that two intersections should be registered. For those cases this procedure is still wrong, since it is computationally expensive to detect them.
2015-10-22 13:34:13 +02:00
Ghostkeeper c0d22dcf7b Move middle_from and middle_to inside as well
When combing needs additional waypoints (middle_from and middle_to), these waypoints weren't offset away from the edge towards the inside of the mesh. This caused all sorts of mistakes with edge cases. This prevents these edge cases.
2015-10-22 13:29:57 +02:00
Ghostkeeper 01d0a3b8d6 Add variant of writeAreas for point vector
Rather than having to supply a polygon, you can now supply a vector of points too. This helps if you want to visualise a custom polygon. Rather than having to clone a polygon some way, clear it and fill it with your own points, you now don't have to create a polygon container at all.
2015-10-22 11:29:59 +02:00
Ghostkeeper 1dd7313332 Fix moveInside corner cases
When the specified point is closest to a vertex of the polygon rather than an edge, the computed inward direction was wrong. I think this was a typo.
2015-10-22 11:15:59 +02:00
Ghostkeeper 7a7d1ebd77 Edge cases in moveInside
If we're moving points inside, points on the edge also have their direction of motion altered. This effectively means that the corner case happens more often.
2015-10-22 11:11:20 +02:00
Ghostkeeper 3dfb84b9a1 Added include(Point) for 2D bounding boxes too
This functionality already existed for 3D axis aligned bounding boxes. It makes it easier to construct a bounding box when no explicit polygon is available. To facilitate this, the bounding box is properly constructed with high minimum value too.
2015-10-21 12:28:03 +02:00
Ghostkeeper 80896e3f4d Fix order of parameter initialisation
This caused a compiler warning.
2015-10-21 12:16:49 +02:00
Ghostkeeper e8cb1028e4 Add check against divide by zero
If two consecutive points are exactly equal, a divide by zero would happen here. This check prevents the divide in that case and skips the second of the points then.
2015-10-20 13:34:32 +02:00
Ghostkeeper d4adbb97bf Merge master of http://github.com/Ultimaker/CuraEngine
Changed files:
 * src/comb.cpp
 * src/comb.h
2015-10-19 17:06:09 +02:00
Ghostkeeper c8488f6d37 Add writeDashedLine
It draws a dashed line on the canvas. This helps if there are multiple lines on top of each other.
2015-10-19 16:45:16 +02:00
Ghostkeeper 7524df2868 Add yellow colour to SVG
I needed an extra colour for debugging. Why not yellow?
2015-10-19 16:43:55 +02:00
Ghostkeeper 28ac5dc2c9 Switch X to be the horizontal dimension
This was the wrong way around in everything except my writeLines function. X is commonly the horizontal dimension (even in the SVG language itself).
2015-10-19 13:58:25 +02:00
Ghostkeeper f5db2ee8b4 Merge master to 15.10
I need the new writeLines in SVG debugging. I know it's not really a bugfix, but SVG is not used in actual releases, so it would not break anything.
2015-10-19 11:46:09 +02:00
Tim Kuipers 8081237298 merge 2015-10-15 11:54:33 +02:00
Tim Kuipers 61915fc322 bugfix: raft is now uses combing, instead of retracting everywhere 2015-10-14 10:57:01 +02:00
Tim Kuipers aa1998dbd8 halfway bugfix: combing during raft 2015-10-14 10:56:36 +02:00
Hajo Nils Krabbenhöft 859a106ec2 rework getLayerSecondWalls() to make sure it doesn't completely remove isles with less than 0.8mm radius (= offset_from_outlines = 2*nozzle_size) 2015-10-14 10:54:34 +02:00
Tim Kuipers ff38534644 bugfix: raftmargin was ignored 2015-10-14 10:45:56 +02:00
Tim Kuipers 2eb81d216f manual merge for raft z 2015-10-14 10:45:31 +02:00
Ghostkeeper 2785659fe6 Move layer loop into combineInfillLayers
The combineInfillLayers function now loops over all layers and combines them. The function therefore no longer needs to be called for every layer.
2015-10-14 10:07:31 +02:00
Ghostkeeper 4bc86ad192 Compute combined infill layers only on modulo-0 layers
For instance, if the sparse_infill_combine setting is 3, it will only be computed for layers 0, 3, 6, etc. This prevents different parts of infill from landing on different layers, causing infill to break into multiple layers.
2015-10-14 10:07:22 +02:00
Tim Kuipers a7b5e05505 bugfix: support roofs sendPolygons now of type SupportType 2015-10-14 10:03:49 +02:00
Tim Kuipers 0fd130cfd3 bugfix: support roof config and support roof sendPolygons(.) 2015-10-14 10:03:33 +02:00
Tim Kuipers ccae7d611e bugfix: timeEstimate recalculate trapezoids failed 2015-10-14 10:02:45 +02:00
Arjen Hiemstra 5dcce74efe Increment layer count for each object that should be printed as a single object
This makes the ordering for one-at-a-time mode more clear

CURA-222 #Start-Review
2015-10-13 17:54:25 +02:00
Hajo Nils Krabbenhöft f78c3ed4da thicker lines for retraction moves 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 79b16e406a GCodePlanner will initialize command socket on GCodeExport, which will then send combing and retraction paths to GUI 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 8219f32d96 also add new poly types to cpp backend 2015-10-08 15:30:07 +02:00
Hajo Nils Krabbenhöft 05cfe62927 add new types to protobuf 2015-10-08 15:30:07 +02:00
Tim Kuipers dc87dda346 Merge pull request #255 from nicanor-romero/15.10
Explicitly write a \n when finding a new line in a comment
2015-10-06 14:17:52 +02:00
Nicanor Romero Venier f249796f3d Explicitly write a \n when finding a new line in a comment
This way the start_gcode and end_gcode that includes '\n's will not
have their commands executed since they now appear as commented out.
2015-10-06 13:05:00 +02:00
Tim Kuipers 3a8a5844d4 moved file 'new settings master' 2015-10-01 15:08:55 +02:00
Tim Kuipers 7b5bb1e367 write 'end of gcode' comment at end of gcode 2015-10-01 11:18:00 +02:00
Tim Kuipers 017dc5be4c bugfix: profile string caused bugs on UMO and when USB printing 2015-10-01 11:14:18 +02:00
Tim Kuipers 60c9e83da9 bugfix: double skin areas 2015-09-30 16:18:26 +02:00
Tim Kuipers 51927938b0 bugfix: visualization used support areas instead of lines 2015-09-30 12:31:03 +02:00
Tim Kuipers d4b9f7c0bd Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-30 09:43:58 +02:00
Tim Kuipers 0b78099587 bugfix: coasting speeds were 100 times too much (%) 2015-09-30 09:43:46 +02:00
Arjen Hiemstra a61d5385ea Use size_t instead of "long unsigned int" for storing vector sizes
std::vector<>::size() returns a size_t. size_t is a long uint on 64-bit
but a normal uint on 32-bit. So compilation on 32-bit fails because
std::max cannot do template deduction correctly.
2015-09-29 17:52:02 +02:00
Tim Kuipers 24c836f87a Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-29 16:14:50 +02:00
Tim Kuipers 8a2ca62305 bugfix: retractions on any move smaller than the retraction_min_travel when combing was disabled 2015-09-29 16:14:40 +02:00
Arjen Hiemstra b0ae307006 Fix check for "touching buildplate" option of support type
CURA-168 #done
2015-09-29 16:09:06 +02:00
Hajo Nils Krabbenhöft 29dd62345a use int for zhop 2015-09-29 15:49:34 +02:00
Hajo Nils Krabbenhöft b3f555c78f store current z-hop height and keep applying that on multi-step travel 2015-09-29 15:49:25 +02:00
Tim Kuipers 0b2f64c6a7 bugfix: total_layers count 2015-09-29 14:44:50 +02:00
Tim Kuipers d501fe67d5 refactor: totalLayers -> total_layers 2015-09-29 14:41:12 +02:00
Tim Kuipers 7180e77699 refactor: fixed enum FillPerimeterGapMode to match other enums 2015-09-29 11:04:04 +02:00
Olliver Schinagl 9c36fdf513 GCodeExport: (re-)Add layer_count tag
Commit 1070d36 broke the build by enabeling code that was not kept up to
date, this patch fixes it properly by adding the
writeLayerCountComment() method.

Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-29 11:03:35 +02:00
Tim Kuipers bc8a167e7b refactor: fixed enum FillPerimeterGapMode to match other enums 2015-09-29 11:03:32 +02:00
Olliver Schinagl bd90dd060f Re-nable layer count using a consistent tag name
Signed-off-by: Olliver Schinagl <o.schinagl@ultimaker.com>
2015-09-28 17:37:03 +02:00
Arjen Hiemstra 5ac62a96a8 Also fix another call for Fill Perimeter Gaps
Contributes to Ultimaker/Cura#411
2015-09-22 12:38:53 +02:00
Arjen Hiemstra 7010689448 Fix "Fill Perimeter Gaps" to properly check values
Since the enum values were changed to normalised values this check was
wrong. Additonally, now properly use an Enum for the values since that
is a cleaner approach.

Fixes Ultimaker/Cura#411
2015-09-22 12:37:03 +02:00
Jaime van Kessel 869975be6b Merge pull request #247 from fxtentacle/15.10-retract-when-no-combing
always retract when combing is disabled
2015-09-22 09:57:23 +02:00
Hajo Nils Krabbenhöft da3bf8da32 always retract when combing is disabled 2015-09-22 09:53:11 +02:00
Arjen Hiemstra 360e3d7e24 Fix one-at-a-time mode so it does not stop after printing 1 object
CURA-162:review
CURA-190:review
2015-09-18 12:09:08 +02:00
Tim Kuipers c267492a48 bugfix: raft viz 2015-09-10 13:20:44 +02:00
Tim Kuipers 52258e2d3a bugfix: send polygon info after fuzzy skin 2015-09-10 11:44:43 +02:00
Tim Kuipers 92c6edca86 bugfix: send polygon info after fuzzy skin 2015-09-10 11:40:26 +02:00
Tim Kuipers 9c26c820a9 bugfix: skin line distance was infill line width 2015-09-10 11:20:40 +02:00
Tim Kuipers b7993441ff bugfix: support progress was still wrong per object 2015-09-10 10:22:06 +02:00
Tim Kuipers 10ebc96323 bugfix: no bottom/top layers resulted in a lingle layer 2015-09-09 17:06:08 +02:00
Tim Kuipers 13af50f34d Merge branch '15.10' of https://github.com/Ultimaker/CuraEngine into 15.10 2015-09-09 16:52:25 +02:00
Tim Kuipers 0563c6eacc Merge branch 'bugfix_per_object_support' into 15.10 2015-09-09 15:26:54 +02:00
Arjen Hiemstra 6f95a78f3c Set correct RPATH for CuraEngine 2015-09-08 17:29:25 +02:00
Tim Kuipers ae79d232f3 bugfixes for skin extrusion width; feature: toggle skin_no_small_gaps_heuristic 2015-09-08 16:25:21 +02:00
Tim Kuipers 11786ada66 lil change fuzzy mode 2015-09-08 12:35:24 +02:00
Tim Kuipers 648bc1f3f3 bugfix: fuzzy skin had too many points; redux can lead to not enough points on poly 2015-09-08 12:35:16 +02:00
Tim Kuipers 84be436eb9 feat: fuzzy skin magic mode 2015-09-08 12:35:06 +02:00
Tim Kuipers e04965187a bugfix: visualize skin lines instead of areas 2015-09-08 10:34:53 +02:00
41 arquivos alterados com 1074 adições e 527 exclusões
+37 -3
Ver Arquivo
@@ -10,6 +10,16 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
# Add a compiler flag to check the output for insane values if we are in debug mode.
if(CMAKE_BUILD_TYPE MATCHES DEBUG)
message("Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
endif()
# Add warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -21,7 +31,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
add_library(clipper STATIC libs/clipper/clipper.cpp)
set(engine_SRCS
set(engine_SRCS # Except main.cpp.
src/bridge.cpp
src/comb.cpp
src/commandSocket.cpp
@@ -33,7 +43,6 @@ set(engine_SRCS
src/infill.cpp
src/inset.cpp
src/layerPart.cpp
src/main.cpp
src/MergeInfillLines.cpp
src/mesh.cpp
src/MeshGroup.cpp
@@ -60,15 +69,40 @@ set(engine_SRCS
src/utils/polygon.cpp
)
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
set(engine_TEST
LinearAlg2DTest
)
# Generating ProtoBuf protocol.
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
# Compiling CuraEngine itself.
add_executable(CuraEngine ${engine_SRCS} src/main.cpp ${engine_PB_SRCS})
target_link_libraries(CuraEngine clipper Arcus)
set_target_properties(CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
if (UNIX)
target_link_libraries(CuraEngine pthread)
endif()
# Compiling the test environment.
if (BUILD_TESTS)
add_library(CuraEngineLibrary ${engine_SRCS} ${engine_PB_SRCS})
if (UNIX)
target_link_libraries(CuraEngineLibrary pthread)
endif()
enable_testing()
foreach (test ${engine_TEST})
add_executable(${test} tests/main.cpp tests/${test}.cpp)
target_link_libraries(${test} CuraEngineLibrary)
target_link_libraries(${test} cppunit)
add_test(${test} ${test})
endforeach()
endif()
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
+2
Ver Arquivo
@@ -62,6 +62,8 @@ message Polygon {
SkirtType = 5;
InfillType = 6;
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
}
Type type = 1;
bytes points = 2;
+123 -65
Ver Arquivo
@@ -16,7 +16,10 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
gcode.preSetup(storage.meshgroup);
gcode.resetTotalPrintTimeAndFilament();
if (meshgroup_number == 1)
{
gcode.resetTotalPrintTimeAndFilament();
}
if (command_socket)
command_socket->beginGCode();
@@ -35,8 +38,13 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
}
meshgroup_number++;
unsigned int total_layers = storage.meshes[0].layers.size();
//gcode.writeComment("Layer count: %d", totalLayers);
size_t total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
total_layers = std::max(total_layers, mesh.layers.size());
}
gcode.writeLayerCountComment(total_layers);
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
if (has_raft)
@@ -61,18 +69,8 @@ void FffGcodeWriter::writeGCode(SliceDataStorage& storage, TimeKeeper& time_keep
if (command_socket)
{
finalize();
command_socket->sendGCodeLayer();
command_socket->endSendSlicedObject();
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
std::ostringstream prefix;
prefix << ";FLAVOR:UltiGCode\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
command_socket->sendGCodePrefix(prefix.str());
}
}
}
@@ -87,10 +85,10 @@ void FffGcodeWriter::setConfigCoasting(SliceDataStorage& storage)
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");
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");
coasting_config.coasting_speed_retract = train->getSettingInPercentage("coasting_speed_retract") / 100.0;
}
}
@@ -190,14 +188,19 @@ void FffGcodeWriter::setConfigInfill(SliceMeshStorage& mesh, int layer_thickness
void FffGcodeWriter::processStartingCode(SliceDataStorage& storage)
{
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
if (!command_socket)
{
if (!command_socket)
std::ostringstream prefix;
prefix << "FLAVOR:" << toString(gcode.getFlavor());
gcode.writeComment(prefix.str().c_str());
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
gcode.writeCode(";FLAVOR:UltiGCode\n;TIME:666\n;MATERIAL:666\n;MATERIAL2:-1\n");
gcode.writeComment("TIME:666");
gcode.writeComment("MATERIAL:666");
gcode.writeComment("MATERIAL2:-1");
}
}
else
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE)
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
@@ -231,75 +234,95 @@ void FffGcodeWriter::processNextMeshGroupCode(SliceDataStorage& storage)
gcode.writeMove(Point(storage.model_min.x, storage.model_min.y), getSettingInMillimetersPerSecond("speed_travel"), 0);
}
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int totalLayers)
void FffGcodeWriter::processRaft(SliceDataStorage& storage, unsigned int total_layers)
{
int extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
bool retraction_combing = false; // the raft isn't added to the parts to avoid
bool retraction_combing = true;
int n_raft_surface_layers = train->getSettingAsCount("raft_surface_layers");
{ // raft base layer
gcode.writeLayerComment(-3);
GCodePathConfig raft_base_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_base_config.setSpeed(getSettingInMillimetersPerSecond("raft_base_speed"));
raft_base_config.setLineWidth(getSettingInMicrons("raft_base_line_width"));
raft_base_config.setLayerHeight(getSettingInMicrons("raft_base_thickness"));
raft_base_config.setFlow(train->getSettingInPercentage("material_flow"));
int layer_nr = -n_raft_surface_layers - 2;
gcode.writeLayerComment(layer_nr);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t z = getSettingInMicrons("raft_base_thickness");
gcode.setZ(z);
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
if (getSettingAsIndex("adhesion_extruder_nr") > 0)
gcode_layer.setExtruder(extruder_nr);
gcode.setZ(getSettingInMicrons("raft_base_thickness"));
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, raft_base_config.getLayerHeight());
gcode_layer.addPolygonsByOptimizer(storage.raftOutline, &raft_base_config);
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_base_line_width"), train->getSettingInMicrons("raft_base_line_spacing"), train->getSettingInPercentage("infill_overlap"), 0);
gcode_layer.addLinesByOptimizer(raftLines, &raft_base_config);
sendPolygons(SupportType, layer_nr, raftLines, raft_base_config.getLineWidth());
gcode.writeFanCommand(train->getSettingInPercentage("raft_base_fan_speed"));
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_base_thickness"));
}
{ // raft interface layer
gcode.writeLayerComment(-2);
GCodePathConfig raft_interface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_interface_config.setSpeed(getSettingInMillimetersPerSecond("raft_interface_speed"));
raft_interface_config.setLineWidth(getSettingInMicrons("raft_interface_line_width"));
raft_interface_config.setLayerHeight(getSettingInMicrons("raft_interface_thickness"));
raft_interface_config.setFlow(train->getSettingInPercentage("material_flow"));
int layer_nr = -n_raft_surface_layers - 1;
gcode.writeLayerComment(layer_nr);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness");
gcode.setZ(z);
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness"));
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, raft_interface_config.getLayerHeight());
Polygons raftLines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raftLines, train->getSettingInMicrons("raft_interface_line_width"), train->getSettingInMicrons("raft_interface_line_spacing"), train->getSettingInPercentage("infill_overlap"), train->getSettingAsCount("raft_surface_layers") > 0 ? 45 : 90);
gcode_layer.addLinesByOptimizer(raftLines, &raft_interface_config);
sendPolygons(SupportType, layer_nr, raftLines, raft_interface_config.getLineWidth());
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
}
for (int raftSurfaceLayer=1; raftSurfaceLayer<=train->getSettingAsCount("raft_surface_layers"); raftSurfaceLayer++)
GCodePathConfig raft_surface_config(&storage.retraction_config_per_extruder[extruder_nr], "SUPPORT");
raft_surface_config.setSpeed(getSettingInMillimetersPerSecond("raft_surface_speed"));
raft_surface_config.setLineWidth(getSettingInMicrons("raft_surface_line_width"));
raft_surface_config.setLayerHeight(getSettingInMicrons("raft_surface_thickness"));
raft_surface_config.setFlow(train->getSettingInPercentage("material_flow"));
for (int raftSurfaceLayer=1; raftSurfaceLayer <= n_raft_surface_layers; raftSurfaceLayer++)
{ // raft surface layers
int layer_nr = -n_raft_surface_layers + raftSurfaceLayer - 1;
gcode.writeLayerComment(-1);
gcode.writeComment("RAFT");
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, 0, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
int64_t z = train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer;
gcode.setZ(z);
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config_per_extruder[extruder_nr], train->getSettingInMillimetersPerSecond("speed_travel"), retraction_combing, layer_nr, train->getSettingInMicrons("machine_nozzle_size"), train->getSettingBoolean("travel_avoid_other_parts"), train->getSettingInMicrons("travel_avoid_distance"));
gcode_layer.setCombing(false);
gcode.setZ(train->getSettingInMicrons("raft_base_thickness") + train->getSettingInMicrons("raft_interface_thickness") + train->getSettingInMicrons("raft_surface_thickness")*raftSurfaceLayer);
if (command_socket)
command_socket->sendLayerInfo(layer_nr, z, raft_surface_config.getLayerHeight());
Polygons raft_lines;
int offset_from_poly_outline = 0;
generateLineInfill(storage.raftOutline, offset_from_poly_outline, raft_lines, train->getSettingInMicrons("raft_surface_line_width"), train->getSettingInMicrons("raft_surface_line_spacing"), train->getSettingInPercentage("infill_overlap"), 90 * raftSurfaceLayer);
gcode_layer.addLinesByOptimizer(raft_lines, &raft_surface_config);
sendPolygons(SupportType, layer_nr, raft_lines, raft_surface_config.getLineWidth());
gcode_layer.writeGCode(false, train->getSettingInMicrons("raft_interface_thickness"));
}
@@ -335,9 +358,8 @@ void FffGcodeWriter::processLayer(SliceDataStorage& storage, unsigned int layer_
gcode.writeLayerComment(layer_nr);
int64_t comb_offset_from_outlines = storage.meshgroup->getExtruderTrain(gcode.getExtruderNr())->getSettingInMicrons("machine_nozzle_size") * 2; // TODO: only used when there is no second wall.
GCodePlanner gcode_layer(gcode, storage, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
int z = storage.meshes[0].layers[layer_nr].printZ;
int64_t z = storage.meshes[0].layers[layer_nr].printZ;
GCodePlanner gcode_layer(command_socket, gcode, storage, z, &storage.retraction_config, getSettingInMillimetersPerSecond("speed_travel"), getSettingBoolean("retraction_combing"), layer_nr, comb_offset_from_outlines, getSettingBoolean("travel_avoid_other_parts"), getSettingInMicrons("travel_avoid_distance"));
gcode.setZ(z);
gcode.resetStartPosition();
@@ -559,30 +581,30 @@ void FffGcodeWriter::addMeshLayerToGCode(SliceDataStorage& storage, SliceMeshSto
int infill_angle = 45;
if (layer_nr & 1)
infill_angle += 90;
int extrusion_width = mesh->infill_config[0].getLineWidth();
int infill_line_width = mesh->infill_config[0].getLineWidth();
int infill_line_distance = mesh->getSettingInMicrons("infill_line_distance");
double infill_overlap = mesh->getSettingInPercentage("infill_overlap");
if (mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
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);
}
processInsets(gcode_layer, mesh, part, layer_nr, z_seam_type);
if (!mesh->getSettingBoolean("infill_before_walls"))
{
processMultiLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
processSingleLayerInfill(gcode_layer, mesh, part, layer_nr, infill_line_distance, infill_overlap, infill_angle, extrusion_width);
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);
}
if (skin_alternate_rotation && ( layer_nr / 2 ) & 1)
infill_angle -= 45;
int64_t skin_overlap = 0;
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_angle, extrusion_width);
processSkin(gcode_layer, mesh, part, layer_nr, skin_overlap, infill_angle, mesh->skin_config.getLineWidth());
//After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter.
if (!mesh->getSettingBoolean("magic_spiralize") || static_cast<int>(layer_nr) < mesh->getSettingAsCount("bottom_layers"))
@@ -681,10 +703,11 @@ void FffGcodeWriter::processInsets(GCodePlanner& gcode_layer, SliceMeshStorage*
void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* mesh, SliceLayerPart& part, unsigned int layer_nr, double infill_overlap, int infill_angle, int extrusion_width)
{
Polygons skin_polygons;
Polygons skin_lines;
for(SkinPart& skin_part : part.skin_parts) // TODO: optimize parts order
{
Polygons skin_polygons;
Polygons skin_lines;
EFillMethod pattern = mesh->getSettingAsFillMethod("top_bottom_pattern");
int bridge = -1;
if (layer_nr > 0)
@@ -703,18 +726,20 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
{
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)
{
inner_skin_outline = &skin_part.insets.back();
offset_from_inner_skin_outline = -extrusion_width/2;
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
generateLineInfill(skin_part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
}
}
}
if (inner_skin_outline == nullptr)
{
inner_skin_outline = &skin_part.outline;
@@ -725,12 +750,16 @@ void FffGcodeWriter::processSkin(GCodePlanner& gcode_layer, SliceMeshStorage* me
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());
}
// handle gaps between perimeters etc.
if (mesh->getSettingString("fill_perimeter_gaps") != "Nowhere")
if (mesh->getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE)
{
generateLineInfill(part.perimeterGaps, 0, skin_lines, extrusion_width, extrusion_width, 0, infill_angle);
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);
}
}
@@ -814,9 +843,14 @@ void FffGcodeWriter::addSupportLinesToGCode(SliceDataStorage& storage, GCodePlan
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());
}
}
@@ -836,7 +870,11 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
setExtruder_addPrime(storage, gcode_layer, layer_nr, roof_extruder_nr);
double fillAngle;
if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height"))
if (pattern == EFillMethod::CONCENTRIC)
{
fillAngle = 0;
}
else if (getSettingInMicrons("support_roof_height") < 2 * getSettingInMicrons("layer_height") || pattern == EFillMethod::TRIANGLES)
{
fillAngle = 90; // perpendicular to support lines
}
@@ -852,8 +890,10 @@ void FffGcodeWriter::addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlan
Polygons support_lines;
infill_comp.generate(support_polygons, support_lines, nullptr);
gcode_layer.addPolygonsByOptimizer(support_polygons, &storage.support_config);
gcode_layer.addLinesByOptimizer(support_lines, &storage.support_config);
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());
}
void FffGcodeWriter::setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr)
@@ -926,12 +966,30 @@ void FffGcodeWriter::processFanSpeedAndMinimalLayerTime(SliceDataStorage& storag
void FffGcodeWriter::finalize()
{
if (command_socket)
{
std::ostringstream prefix;
prefix << ";FLAVOR:" << toString(gcode.getFlavor()) << "\n";
prefix << ";TIME:" << int(gcode.getTotalPrintTime()) << "\n";
if (gcode.getFlavor() == EGCodeFlavor::ULTIGCODE)
{
prefix << ";MATERIAL:" << int(gcode.getTotalFilamentUsed(0)) << "\n";
prefix << ";MATERIAL2:" << int(gcode.getTotalFilamentUsed(1)) << "\n";
}
command_socket->sendGCodePrefix(prefix.str());
}
gcode.finalize(max_object_height, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
gcode.writeComment("End of Gcode");
/*
the profile string below can be executed since the M25 doesn't end the gcode on an UMO and when printing via USB.
gcode.writeCode("M25 ;Stop reading from this point on.");
gcode.writeComment("Cura profile string:");
gcode.writeComment(FffProcessor::getInstance()->getAllLocalSettingsString() + FffProcessor::getInstance()->getProfileString());
*/
}
+4 -4
Ver Arquivo
@@ -125,18 +125,18 @@ private:
/*!
* Add raft gcode.
* \param storage Input: where the slice data is stored.
* \param totalLayers The total number of layers.
* \param total_layers The total number of layers.
*/
void processRaft(SliceDataStorage& storage, unsigned int totalLayers);
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Add a layer to the gcode.
* \param storage Input: where the slice data is stored.
* \param layer_nr The index of the layer to write the gcode of.
* \param totalLayers The total number of layers.
* \param total_layers The total number of layers.
* \param has_raft Whether a raft is used for this print.
*/
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int totalLayers, bool has_raft);
void processLayer(SliceDataStorage& storage, unsigned int layer_nr, unsigned int total_layers, bool has_raft);
/*!
* Interpolate between the initial layer speeds and the eventual speeds.
+79 -158
Ver Arquivo
@@ -1,9 +1,6 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include <random> // for bulging effect?
#include <functional> // for bugling?
#include <cmath> // for bulging?
#include "slicer.h"
#include "utils/gettime.h"
@@ -49,13 +46,28 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
log("Slicing model...\n");
int initial_layer_thickness = meshgroup->getSettingInMicrons("layer_height_0");
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Initial layer height %i is disallowed.",initial_layer_thickness);
return false;
}
int layer_thickness = meshgroup->getSettingInMicrons("layer_height");
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Layer height %i is disallowed.",layer_thickness);
return false;
}
if (meshgroup->getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
{
initial_layer_thickness = layer_thickness;
}
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
int layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if(layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
Progress::messageProgressStage(Progress::Stage::INSET,&timeKeeper,commandSocket); //Continue directly with the inset stage, which will also immediately stop.
return true; //This is NOT an error state!
}
std::vector<Slicer*> slicerList;
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
@@ -79,9 +91,6 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
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);
bulgeWalls(slicerList, meshgroup);
//carveMultipleVolumes(storage.meshes);
generateMultipleVolumesOverlap(slicerList, getSettingInMicrons("multiple_mesh_overlap"));
@@ -99,20 +108,16 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
meshStorage.getSettingInMicrons("layer_height_0")
- initial_slice_z;
if (has_raft)
{
layer.printZ +=
meshStorage.getSettingInMicrons("raft_base_thickness")
+ meshStorage.getSettingInMicrons("raft_interface_thickness")
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("layer_height") //raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap")
- initial_slice_z;
}
else
{
meshStorage.layers[layer_nr].printZ +=
meshStorage.getSettingInMicrons("layer_height_0")
- initial_slice_z;
+ meshStorage.getSettingAsCount("raft_surface_layers") * getSettingInMicrons("raft_surface_thickness") //raft_surface_thickness")
+ meshStorage.getSettingInMicrons("raft_airgap");
}
@@ -136,8 +141,12 @@ bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeepe
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
// const
unsigned int total_layers = storage.meshes.at(0).layers.size();
size_t total_layers = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
}
//layerparts2HTML(storage, "output/output.html");
for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
{
@@ -157,6 +166,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);
AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
@@ -165,6 +175,7 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
}
}
*/
Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
int mesh_max_bottom_layer_count = 0;
@@ -184,10 +195,10 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
}
for(unsigned int layer_number = total_layers-1; layer_number > 0; layer_number--)
unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
for(SliceMeshStorage& mesh : storage.meshes)
{
for(SliceMeshStorage& mesh : storage.meshes)
combineInfillLayers(layer_number, mesh, mesh.getSettingAsCount("infill_sparse_combine"));
combineInfillLayers(mesh,combined_infill_layers);
}
storage.primeTower.computePrimeTowerMax(storage);
@@ -204,7 +215,18 @@ void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper&
{
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzySkin(mesh);
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"));
}
}
}
}
}
@@ -229,20 +251,12 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
{
if (layer->parts[partNr].insets.size() > 0)
{
sendPolygons(Inset0Type, layer_nr, layer->parts[partNr].insets[0], line_width_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);
}
}
}
else
{ // only send polygon data
SliceLayer* layer = &mesh.layers[layer_nr];
for(SliceLayerPart& part : layer->parts)
{
sendPolygons(Inset0Type, layer_nr, part.outline, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
for (PolygonRef polyline : layer->openPolyLines)
@@ -260,10 +274,10 @@ void FffPolygonGenerator::processInsets(SliceDataStorage& storage, unsigned int
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers)
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers)
{
int n_empty_first_layers = 0;
for (unsigned int layer_idx = 0; layer_idx < totalLayers; layer_idx++)
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
bool layer_is_empty = true;
for (SliceMeshStorage& mesh : storage.meshes)
@@ -297,7 +311,7 @@ void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, int
layer.printZ -= n_empty_first_layers * layer_height;
}
}
totalLayers -= n_empty_first_layers;
total_layers -= n_empty_first_layers;
}
}
@@ -307,40 +321,45 @@ void FffPolygonGenerator::processSkins(SliceDataStorage& storage, unsigned int l
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { continue; }
int extrusionWidth = mesh.getSettingInMicrons("wall_line_width_x");
int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width");
int innermost_wall_extrusion_width = mesh.getSettingInMicrons("wall_line_width_x");
int extrusionWidth_infill = mesh.getSettingInMicrons("infill_line_width");
generateSkins(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"), mesh.getSettingBoolean("remove_overlapping_walls_0_enabled"), mesh.getSettingBoolean("remove_overlapping_walls_x_enabled"));
if (mesh.getSettingInMicrons("infill_line_distance") > 0)
{
int infill_skin_overlap = 0;
if (mesh.getSettingInMicrons("infill_line_distance") > mesh.getSettingInMicrons("infill_line_width") + 10)
{
infill_skin_overlap = extrusionWidth / 2;
infill_skin_overlap = skin_extrusion_width / 2;
}
generateInfill(layer_nr, mesh, extrusionWidth_infill, infill_skin_overlap);
if (mesh.getSettingString("fill_perimeter_gaps") == "Skin")
if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::SKIN)
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"));
}
else if (mesh.getSettingString("fill_perimeter_gaps") == "Everywhere")
else if (mesh.getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") == FillPerimeterGapMode::EVERYWHERE)
{
generatePerimeterGaps(layer_nr, mesh, extrusionWidth, 0, 0);
generatePerimeterGaps(layer_nr, mesh, skin_extrusion_width, 0, 0);
}
}
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
bool frontend_can_show_polygon_as_filled_polygon = false;
if (frontend_can_show_polygon_as_filled_polygon)
{
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
SliceLayer& layer = mesh.layers[layer_nr];
for(SliceLayerPart& part : layer.parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, extrusionWidth);
// sendPolygons(InfillType, layer_nr, part.infill_area[0], extrusionWidth_infill); // sends the outline, not the actual infill
for (SkinPart& skin_part : part.skin_parts)
{
sendPolygons(SkinType, layer_nr, skin_part.outline, innermost_wall_extrusion_width);
}
}
}
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int totalLayers)
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
@@ -349,28 +368,28 @@ void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned
int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
}
int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
for(unsigned int layer_nr=0; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius);
}
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
for(unsigned int layer_nr=1; layer_nr<totalLayers; layer_nr++)
for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
}
for(unsigned int layer_nr=totalLayers-1; layer_nr>0; layer_nr--)
for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
{
storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int totalLayers)
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
{
int draft_shield_height = getSettingInMicrons("draft_shield_height");
int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
@@ -387,7 +406,7 @@ void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned
int layer_skip = 500 / layer_height + 1;
Polygons& draft_shield = storage.draft_protection_shield;
for (unsigned int layer_nr = 0; layer_nr < totalLayers && layer_nr < max_screen_layer; layer_nr += layer_skip)
for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
@@ -420,14 +439,19 @@ void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
}
void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
{
if (mesh.getSettingAsCount("wall_line_count") == 0)
{
return;
}
int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
int64_t range_random_point_dist = avg_dist_between_points / 2;
for (SliceLayer& layer : mesh.layers)
for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
{
SliceLayer& layer = mesh.layers[layer_nr];
for (SliceLayerPart& part : layer.parts)
{
Polygons results;
@@ -472,110 +496,7 @@ void FffPolygonGenerator::processFuzzySkin(SliceMeshStorage& mesh)
}
}
skin = results;
}
}
}
void FffPolygonGenerator::bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup)
{
assert(slicerList.size() == meshgroup->meshes.size());
for (unsigned int mesh_idx = 0; mesh_idx < slicerList.size(); mesh_idx++)
{
Slicer* slicer = slicerList[mesh_idx];
Mesh& mesh = meshgroup->meshes[mesh_idx];
if (!mesh.getSettingBoolean("magic_bulge_walls"))
{
// continue; // TODO
}
auto getBulging = [](Point xy, int z)
{
std::hash<int> hash_fn;
int cell_size = MM2INT(0.2);
int cell_dim = 5; // surrounding taken into account
double result = 0.0;
int bulging = MM2INT(10.0);
Point3 middle(xy.X / cell_size, xy.Y / cell_size, z / cell_size);
double total_weight = 0.0;
for (int x = middle.x - cell_dim; x < middle.x + cell_dim; x++)
{
for (int y = middle.y - cell_dim; y < middle.y + cell_dim; y++)
{
for (int z = middle.z - cell_dim; z < middle.z + cell_dim; z++)
{
srand(x ^ (y << 8) ^ (z << 16)); // set seed
int h = rand();
// int h = hash_fn(x ^ (y << 8) ^ (z << 16));
double r = (double(h % 200000 - 100000))/100000.0; // between -1 and 1
double weight = sqrt(1.0 / (1.0 + static_cast<double>(((Point3(xy.X, xy.Y, z) - Point3(x,y,z)* cell_size)).vSize()) * 4));
total_weight += weight;
result += r * weight ;
}
}
}
return static_cast<int>(result / total_weight * bulging);
// return rand() % (bulging*2) - bulging;
};
int64_t avg_dist_between_points = MM2INT(0.5); // mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
int64_t range_random_point_dist = avg_dist_between_points / 2;
int layer_height = mesh.getSettingInMicrons("layer_height");
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SlicerLayer& layer = slicer->layers[layer_nr];
Polygons& outlines = layer.polygonList;
Polygons results;
int z_approx = layer_nr * layer_height;
for (PolygonRef poly : outlines)
{
// generate points in between p0 and p1
PolygonRef result = results.newPoly();
int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
Point* p0 = &poly.back();
for (Point& p1 : poly)
{ // 'a' is the (next) new point between p0 and p1
Point p0p1 = p1 - *p0;
int64_t p0p1_size = vSize(p0p1);
int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
{
Point in_between = *p0 + normal(p0p1, p0pa_dist);
int r = getBulging(in_between, z_approx);
Point perp_to_p0p1 = crossZ(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = in_between + fuzz;
result.add(pa);
dist_last_point = p0pa_dist;
}
dist_left_over = p0p1_size - dist_last_point;
p0 = &p1;
}
while (result.size() < 3 )
{
unsigned int point_idx = poly.size() - 2;
result.add(poly[point_idx]);
if (point_idx == 0) { break; }
point_idx--;
}
if (result.size() < 3)
{
result.clear();
for (Point& p : poly)
result.add(p);
}
}
outlines = results;
sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0"));
}
}
}
+9 -14
Ver Arquivo
@@ -12,8 +12,6 @@
namespace cura
{
class Slicer; // forward declaration
/*!
* Primary stage in Fused Filament Fabrication processing: Polygons are generated.
* The model is sliced and each slice consists of polygons representing the outlines: the boundaries between inside and outside the object.
@@ -91,9 +89,9 @@ private:
* Remove all bottom layers which are empty.
* \param storage Input and Ouput parameter: stores all layers
* \param layer_height The height of each layer
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int totalLayers);
void removeEmptyFirstLayers(SliceDataStorage& storage, int layer_height, unsigned int total_layers);
/*!
* Generate the inset polygons which form the walls.
@@ -105,9 +103,9 @@ private:
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void processOozeShield(SliceDataStorage& storage, unsigned int totalLayers);
void processOozeShield(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Generate the skin areas.
@@ -120,9 +118,9 @@ private:
* Generate the polygons where the draft screen should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void processDraftShield(SliceDataStorage& storage, unsigned int totalLayers);
void processDraftShield(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Generate the skirt/brim/raft areas/insets.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
@@ -132,15 +130,12 @@ private:
/*!
* Special mode: Make the outer wall 'fuzzy'
* Make the outer wall 'fuzzy'
*/
void processFuzzySkin(SliceMeshStorage& mesh);
void processFuzzyWalls(SliceMeshStorage& mesh);
/*!
* Special mode: bulge the outer walls
*/
void bulgeWalls(std::vector< Slicer* > slicerList, MeshGroup* meshgroup);
};
}//namespace cura
+1 -1
Ver Arquivo
@@ -37,7 +37,7 @@ public:
private:
FffPolygonGenerator polygon_generator;
FffGcodeWriter gcode_writer;
CommandSocket* command_socket;
CommandSocket* command_socket; // TODO: replace all refs to command_socket by CommandSocket::getInstance()
bool first_meshgroup;
+2 -2
Ver Arquivo
@@ -104,7 +104,7 @@ void PrimeTower::generateGroundpoly(SliceDataStorage& storage)
storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLayers)
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0
// && storage.getSettingInMicrons("prime_tower_distance") > 0
@@ -113,7 +113,7 @@ void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int totalLaye
generatePaths3(storage);
}
}
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers)
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
{
if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_distance") > 0 && storage.getSettingInMicrons("prime_tower_size") > 0)
+3 -3
Ver Arquivo
@@ -44,10 +44,10 @@ public:
* Generate the area where the prime tower should be.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param totalLayers The total number of layers
* \param total_layers The total number of layers
*/
void generatePaths(SliceDataStorage& storage, unsigned int totalLayers);
void generatePaths_OLD(SliceDataStorage& storage, unsigned int totalLayers);
void generatePaths(SliceDataStorage& storage, unsigned int total_layers);
void generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers);
void computePrimeTowerMax(SliceDataStorage& storage);
+2 -2
Ver Arquivo
@@ -4,8 +4,8 @@
namespace cura
{
enum class EPrintFeature : unsigned int
{
enum class EPrintFeature : unsigned int // unused!!
{ // TODO: use in gcodePathConfigs ?
OUTER_WALL,
INNER_WALLS,
INFILL,
+2 -2
Ver Arquivo
@@ -27,7 +27,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
processSkirt(commandSocket);
unsigned int totalLayers = wireFrame.layers.size();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment("SKIRT");
@@ -71,7 +71,7 @@ void Wireframe2gcode::writeGCode(CommandSocket* commandSocket)
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr, commandSocket);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, totalLayers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers, commandSocket); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_nr];
+100 -51
Ver Arquivo
@@ -5,6 +5,7 @@
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "utils/SVG.h"
namespace cura {
@@ -15,22 +16,41 @@ bool Comb::moveInsideBoundary(Point* p, int distance)
Polygons Comb::getLayerSecondWalls()
{
Polygons layer_walls;
for (SliceMeshStorage& mesh : storage.meshes)
if (layer_nr < 0)
{ // when a raft is present
return storage.raftOutline.offset(MM2INT(0.1));
}
else
{
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
Polygons layer_walls;
for (SliceMeshStorage& mesh : storage.meshes)
{
if (part.insets.size() >= 2)
for (SliceLayerPart& part : mesh.layers[layer_nr].parts)
{
layer_walls.add(part.insets[1]);
}
else
{
layer_walls.add(part.outline.offset(-offset_from_outlines));
// we want the 2nd inner walls
if (part.insets.size() >= 2) {
layer_walls.add(part.insets[1]);
continue;
}
// but we'll also take the inner wall if the 2nd doesn't exist
if (part.insets.size() >= 1) {
layer_walls.add(part.insets[0]);
continue;
}
// and if there is no walls, we'll try to move inside from the outline
Polygons newOutline = part.outline.offset(-offset_from_outlines);
if(newOutline.polygonLength() > 0) {
layer_walls.add(newOutline);
continue;
}
// offset_from_outlines was so large that it completely destroyed our isle,
// so we'll just use the regular outline
layer_walls.add(part.outline);
continue;
}
}
return layer_walls;
}
return layer_walls;
}
// boundary_outside is only computed when it's needed!
@@ -44,7 +64,7 @@ Polygons* Comb::getBoundaryOutside()
return boundary_outside;
}
Comb::Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
Comb::Comb(SliceDataStorage& storage, int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
@@ -64,7 +84,7 @@ Comb::~Comb()
delete boundary_outside;
}
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside)
bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside, bool endInside, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored))
{
@@ -117,13 +137,15 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
{ // normal combing within part
PolygonsPart part = partsView_inside.assemblePart(start_part_idx);
combPaths.emplace_back();
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part, startPoint, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
return true;
}
else
{ // comb inside part to edge (if needed) >> move through air avoiding other parts >> comb inside end part upto the endpoint (if needed)
Point middle_from;
Point middle_to;
Point inside_middle_from;
Point inside_middle_to;
if (startInside && endInside)
{
@@ -131,27 +153,36 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from_cp.location, boundary_inside[end_part_boundary_poly_idx]);
// walkToNearestSmallestConnection(middle_from_cp, middle_to_cp); // TODO: perform this optimization?
middle_from = middle_from_cp.location;
inside_middle_from = middle_from_cp.location;
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored); //Also move the intermediary waypoint inside if it isn't yet.
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else
else if(!startInside && !endInside)
{
if (!startInside && !endInside)
{
middle_from = startPoint;
middle_to = endPoint;
}
else if (!startInside && endInside)
{
middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from, boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
}
else if (startInside && !endInside)
{
middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to, boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
}
middle_from = startPoint;
inside_middle_from = startPoint;
middle_to = endPoint;
inside_middle_to = endPoint;
}
else if(!startInside && endInside)
{
middle_from = startPoint;
inside_middle_from = startPoint;
ClosestPolygonPoint middle_to_cp = PolygonUtils::findClosest(middle_from,boundary_inside[end_part_boundary_poly_idx]);
middle_to = middle_to_cp.location;
inside_middle_to = middle_to_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_to,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
else if(startInside && !endInside)
{
middle_to = endPoint;
inside_middle_to = endPoint;
ClosestPolygonPoint middle_from_cp = PolygonUtils::findClosest(middle_to,boundary_inside[start_part_boundary_poly_idx]);
middle_from = middle_from_cp.location;
inside_middle_from = middle_from_cp.location;
PolygonUtils::moveInside(boundary_inside,inside_middle_from,offset_dist_to_get_from_on_the_polygon_to_outside,max_comb_distance_ignored);
}
if (startInside)
@@ -159,7 +190,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
// start to boundary
PolygonsPart part_begin = partsView_inside.assemblePart(start_part_idx); // comb through the starting part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_begin, startPoint, middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part_begin, startPoint, inside_middle_from, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
// throught air from boundary to boundary
@@ -178,14 +209,14 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
}
combPaths.emplace_back();
combPaths.back().throughAir = true;
if ( vSize(middle_from - middle_to) < vSize(middle_from - from_outside) + vSize(middle_to - to_outside) )
if ( vSize(inside_middle_from - inside_middle_to) < vSize(inside_middle_from - from_outside) + vSize(inside_middle_to - to_outside) )
{ // via outside is a detour
combPaths.back().push_back(middle_from);
combPaths.back().push_back(middle_to);
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
else
{
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(middle, from_outside, to_outside, combPaths.back(), offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
}
else
@@ -193,8 +224,8 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
combPaths.emplace_back();
combPaths.back().throughAir = true;
combPaths.back().cross_boundary = true; // TODO: calculate whether we cross a boundary!
combPaths.back().push_back(middle_from);
combPaths.back().push_back(middle_to);
combPaths.back().push_back(inside_middle_from);
combPaths.back().push_back(inside_middle_to);
}
if (endInside)
@@ -202,7 +233,7 @@ bool Comb::calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool sta
// boundary to end
PolygonsPart part_end = partsView_inside.assemblePart(end_part_idx); // comb through end part only
combPaths.emplace_back();
LinePolygonsCrossings::comb(part_end, middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside);
LinePolygonsCrossings::comb(part_end, inside_middle_to, endPoint, combPaths.back(), -offset_dist_to_get_from_on_the_polygon_to_outside, max_comb_distance_ignored);
}
return true;
@@ -219,18 +250,32 @@ void LinePolygonsCrossings::calcScanlineCrossings()
{
PolyCrossings minMax(poly_idx);
PolygonRef poly = boundary[poly_idx];
Point p0 = transformation_matrix.apply(poly.back());
Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point p1 = transformation_matrix.apply(poly[point_idx]);
if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y))
if((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
{
if(p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
{
p0 = p1;
continue;
}
int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
{
if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; }
if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; }
if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
//Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
{
minMax.min.x = x;
minMax.min.point_idx = point_idx;
}
if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
{
minMax.max.x = x;
minMax.max.point_idx = point_idx;
}
}
}
p0 = p1;
@@ -276,14 +321,14 @@ bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
}
void LinePolygonsCrossings::getCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored)
{
if (shorterThen(endPoint - startPoint, Comb::max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
{
//We're not crossing any boundaries. So skip the comb generation.
combPath.push_back(startPoint);
combPath.push_back(endPoint);
return;
combPath.push_back(endPoint);
return;
}
calcScanlineCrossings();
@@ -308,7 +353,7 @@ void LinePolygonsCrossings::getBasicCombingPath(CombPath& combPath)
void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
PolygonRef poly = boundary[polyCrossings.poly_idx];
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x + dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
if ( ( polyCrossings.max.point_idx - polyCrossings.min.point_idx + poly.size() ) % poly.size()
< poly.size() / 2 )
{ // follow the path in the same direction as the winding order of the boundary polygon
@@ -323,13 +368,13 @@ void LinePolygonsCrossings::getBasicCombingPath(PolyCrossings& polyCrossings, Co
{ // follow the path in the opposite direction of the winding order of the boundary polygon
unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1: polyCrossings.min.point_idx - 1;
unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1: polyCrossings.max.point_idx - 1;
for(unsigned int point_idx = min_idx; point_idx != max_idx; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
{
combPath.push_back(PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside));
}
}
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x, transformed_startPoint.Y)));
combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.max.x - dist_to_move_boundary_point_outside, transformed_startPoint.Y)));
}
@@ -348,10 +393,14 @@ LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlong
}
bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
{
optimized_comb_path.push_back(startPoint);
for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
{
if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
{
continue;
}
Point& current_point = optimized_comb_path.back();
if (PolygonUtils::polygonCollidesWithlineSegment(boundary, current_point, comb_path[point_idx]))
{
+6 -7
Ver Arquivo
@@ -113,7 +113,7 @@ private:
*
* \param combPath Output parameter: the points along the combing path.
*/
void getCombingPath(CombPath& combPath);
void getCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored = MM2INT(1.5));
/*!
* Get the basic combing path, without shortcuts. The path goes straight toward the endPoint and follows the boundary when it hits it, until it passes the scanline again.
@@ -178,10 +178,10 @@ public:
* \param endPoint Where to end the combing move.
* \param combPath Output parameter: the combing path generated.
*/
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside)
static void comb(Polygons& boundary, Point startPoint, Point endPoint, CombPath& combPath, int64_t dist_to_move_boundary_point_outside, int64_t max_comb_distance_ignored = MM2INT(1.5))
{
LinePolygonsCrossings linePolygonsCrossings(boundary, startPoint, endPoint, dist_to_move_boundary_point_outside);
linePolygonsCrossings.getCombingPath(combPath);
linePolygonsCrossings.getCombingPath(combPath, max_comb_distance_ignored);
};
};
@@ -205,14 +205,13 @@ class Comb
friend class LinePolygonsCrossings;
private:
SliceDataStorage& storage; //!< The storage from which to compute the outside boundary, when needed.
unsigned int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
int layer_nr; //!< The layer number for the layer for which to compute the outside boundary, when needed.
int64_t offset_from_outlines; //!< Offset from the boundary of a part to the comb path. (nozzle width / 2)
int64_t max_moveInside_distance2; //!< Maximal distance of a point to the Comb::boundary_inside which is still to be considered inside. (very sharp corners not allowed :S)
int64_t offset_from_outlines_outside; //!< Offset from the boundary of a part to a travel path which avoids it by this distance.
static const int64_t max_moveOutside_distance2 = INT64_MAX; //!< Any point which is not inside should be considered outside.
static const int64_t offset_dist_to_get_from_on_the_polygon_to_outside = 40; //!< in order to prevent on-boundary vs crossing boundary confusions (precision thing)
static const int64_t max_comb_distance_ignored = MM2INT(1.5); //!< If the direct path from start point to end point is shorter than this, go directly without any combing.
static const int64_t offset_extra_start_end = 100; //!< Distance to move start point and end point toward eachother to extra avoid collision with the boundaries.
bool avoid_other_parts; //!< Whether to perform inverse combing a.k.a. avoid parts.
@@ -240,7 +239,7 @@ public:
* \param travel_avoid_other_parts Whether to avoid other layer parts when traveling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
*/
Comb(SliceDataStorage& storage, unsigned int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
Comb(SliceDataStorage& storage, int layer_nr, int64_t offset_from_outlines, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~Comb();
@@ -257,7 +256,7 @@ public:
* \param endInside Whether we want to end up inside the comb boundary
* \return Whether combing has succeeded; otherwise a retraction is needed.
*/
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false);
bool calc(Point startPoint, Point endPoint, CombPaths& combPaths, bool startInside = false, bool endInside = false, int64_t max_comb_distance_ignored = MM2INT(1.5));
/*!
* Move \p p to inside the inner comb boundary with a \p distance from the boundary.
+24 -1
Ver Arquivo
@@ -26,6 +26,8 @@ public:
, object_count(0)
, current_sliced_object(nullptr)
, sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
cura::proto::Layer* getLayerById(int id);
@@ -43,6 +45,11 @@ public:
// Number of sliced objects for this sliced object list
int sliced_objects;
// Number of layers sent to the front end so far
// Used for incrementing the current layer in one at a time mode
int current_layer_count;
int current_layer_offset;
// Ids of the sliced objects
std::vector<int64_t> object_ids;
@@ -80,11 +87,17 @@ void CommandSocket::connect(const std::string& ip, int port)
//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)
{
FffProcessor::getInstance()->processMeshGroup(object.get());
if(!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
}
d->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
sendGCodeLayer();
sendPrintTime();
//TODO: Support all-at-once/one-at-a-time printing
//d->processor->processModel(d->object_to_slice.get());
@@ -261,11 +274,18 @@ void CommandSocket::beginSendSlicedObject()
void CommandSocket::endSendSlicedObject()
{
d->sliced_objects++;
d->current_layer_offset = d->current_layer_count;
std::cout << "End sliced object called. sliced objects " << d->sliced_objects << " object count: " << d->object_count << std::endl;
std::cout << "current layer count" << d->current_layer_count << std::endl;
std::cout << "current layer offset" << d->current_layer_offset << std::endl;
if(d->sliced_objects >= d->object_count)
{
d->socket->sendMessage(d->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;
}
@@ -295,6 +315,8 @@ void CommandSocket::sendGCodePrefix(std::string prefix)
cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
id += current_layer_offset;
auto itr = std::find_if(current_sliced_object->mutable_layers()->begin(), current_sliced_object->mutable_layers()->end(), [id](cura::proto::Layer& l) { return l.id() == id; });
cura::proto::Layer* layer = nullptr;
@@ -306,6 +328,7 @@ cura::proto::Layer* CommandSocket::Private::getLayerById(int id)
{
layer = current_sliced_object->add_layers();
layer->set_id(id);
current_layer_count++;
}
return layer;
+44 -21
Ver Arquivo
@@ -8,7 +8,7 @@
namespace cura {
GCodeExport::GCodeExport()
: output_stream(&std::cout), currentPosition(0,0,0), startPosition(INT32_MIN,INT32_MIN,0)
: output_stream(&std::cout),currentPosition(0,0,0),startPosition(INT32_MIN,INT32_MIN,0),commandSocket(nullptr),layer_nr(0)
{
extrusion_amount = 0;
current_extruder = 0;
@@ -19,7 +19,7 @@ GCodeExport::GCodeExport()
currentSpeed = 1;
retractionPrimeSpeed = 1;
isRetracted = false;
isZHopped = false;
isZHopped = 0;
last_coasted_amount_mm3 = 0;
setFlavor(EGCodeFlavor::REPRAP);
}
@@ -28,6 +28,11 @@ GCodeExport::~GCodeExport()
{
}
void GCodeExport::setCommandSocketAndLayerNr(CommandSocket* commandSocket_, unsigned int layer_nr_) {
commandSocket = commandSocket_;
layer_nr = layer_nr_;
}
void GCodeExport::setOutputStream(std::ostream* stream)
{
output_stream = stream;
@@ -137,10 +142,6 @@ double GCodeExport::getTotalFilamentUsed(int e)
return extruder_attr[e].totalFilament;
}
double GCodeExport::getTotalPrintTime(EPrintFeature print_feature)
{
return total_print_time_per_feature[(unsigned int)print_feature];
}
double GCodeExport::getTotalPrintTime()
{
return totalPrintTime;
@@ -149,10 +150,6 @@ double GCodeExport::getTotalPrintTime()
void GCodeExport::resetTotalPrintTimeAndFilament()
{
totalPrintTime = 0;
for (unsigned int feat_idx = 0; feat_idx < (unsigned int)EPrintFeature::ENUM_COUNT; feat_idx++)
{
total_print_time_per_feature[feat_idx] = 0.0;
}
for(unsigned int e=0; e<MAX_EXTRUDERS; e++)
{
extruder_attr[e].totalFilament = 0.0;
@@ -162,17 +159,25 @@ void GCodeExport::resetTotalPrintTimeAndFilament()
estimateCalculator.reset();
}
void GCodeExport::updateTotalPrintTime(EPrintFeature print_feature)
void GCodeExport::updateTotalPrintTime()
{
double time = estimateCalculator.calculate();
totalPrintTime += time;
total_print_time_per_feature[(unsigned int)print_feature] += time;
totalPrintTime += estimateCalculator.calculate();
estimateCalculator.reset();
}
void GCodeExport::writeComment(std::string comment)
{
*output_stream << ";" << comment << "\n";
*output_stream << ";";
for (unsigned int i = 0; i < comment.length(); i++)
{
if (comment[i] == '\n')
{
*output_stream << "\\n";
}else{
*output_stream << comment[i];
}
}
*output_stream << "\n";
}
void GCodeExport::writeTypeComment(const char* type)
@@ -184,6 +189,11 @@ void GCodeExport::writeLayerComment(int layer_nr)
*output_stream << ";LAYER:" << layer_nr << "\n";
}
void GCodeExport::writeLayerCountComment(int layer_count)
{
*output_stream << ";LAYER_COUNT:" << layer_count << "\n";
}
void GCodeExport::writeLine(const char* line)
{
*output_stream << line << "\n";
@@ -204,7 +214,7 @@ void GCodeExport::resetExtrusionValue()
void GCodeExport::writeDelay(double timeAmount)
{
*output_stream << "G4 P" << int(timeAmount * 1000) << "\n";
totalPrintTime += timeAmount;
estimateCalculator.addTime(timeAmount);
}
void GCodeExport::writeMove(Point p, double speed, double extrusion_mm3_per_mm)
@@ -222,8 +232,10 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
if (currentPosition.x == x && currentPosition.y == y && currentPosition.z == z)
return;
assert(speed*60 < 10000 && speed*60 > 100); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
#ifdef ASSERT_INSANE_OUTPUT
assert(speed < 200 && speed > 1); // normal F values occurring in UM2 gcode (this code should not be compiled for release)
assert((Point3(x,y,z) - currentPosition).vSize() < MM2INT(300)); // no crazy positions (this code should not be compiled for release)
#endif //ASSERT_INSANE_OUTPUT
if (extrusion_mm3_per_mm < 0)
logWarning("Warning! Negative extrusion move!");
@@ -289,7 +301,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
if (isZHopped > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z) << "\n";
isZHopped = false;
isZHopped = 0;
}
extrusion_amount += (is_volumatric) ? last_coasted_amount_mm3 : last_coasted_amount_mm3 / getFilamentArea(current_extruder);
if (isRetracted)
@@ -301,6 +313,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
}
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), 25.0);
}else{
@@ -317,6 +330,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
if (last_coasted_amount_mm3 > 0)
{
*output_stream << "G1 F" << (retractionPrimeSpeed * 60) << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount << "\n";
currentSpeed = retractionPrimeSpeed;
estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusion_amount), currentSpeed);
}
}
@@ -325,6 +339,15 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
*output_stream << "G1";
}else{
*output_stream << "G0";
if (commandSocket) {
// we should send this travel as a non-retraction move
cura::Polygons travelPoly;
PolygonRef travel = travelPoly.newPoly();
travel.add(Point(currentPosition.x, currentPosition.y));
travel.add(Point(x, y));
commandSocket->sendPolygons(isRetracted ? MoveRetractionType : MoveCombingType, layer_nr, travelPoly, isRetracted ? MM2INT(0.2) : MM2INT(0.1));
}
}
if (currentSpeed != speed)
@@ -337,7 +360,7 @@ void GCodeExport::writeMove(int x, int y, int z, double speed, double extrusion_
" X" << INT2MM(gcode_pos.X) <<
" Y" << INT2MM(gcode_pos.Y);
if (z != currentPosition.z)
*output_stream << " Z" << INT2MM(z);
*output_stream << " Z" << INT2MM(z + isZHopped);
if (extrusion_mm3_per_mm > 0.000001)
*output_stream << " " << extruder_attr[current_extruder].extruderCharacter << std::setprecision(5) << extrusion_amount;
*output_stream << "\n";
@@ -380,8 +403,8 @@ void GCodeExport::writeRetraction(RetractionConfig* config, bool force)
}
if (config->zHop > 0)
{
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + config->zHop) << "\n";
isZHopped = true;
isZHopped = config->zHop;
*output_stream << std::setprecision(3) << "G1 Z" << INT2MM(currentPosition.z + isZHopped) << "\n";
}
extrusion_amount_at_previous_n_retractions.push_front(extrusion_amount);
if (int(extrusion_amount_at_previous_n_retractions.size()) == config->retraction_count_max)
+16 -5
Ver Arquivo
@@ -10,7 +10,7 @@
#include "utils/intpoint.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "PrintFeature.h"
#include "commandSocket.h"
namespace cura {
@@ -99,6 +99,11 @@ public:
return line_width;
}
int getLayerHeight()
{
return layer_thickness;
}
private:
void calculateExtrusion()
{
@@ -150,7 +155,7 @@ private:
double currentSpeed;
int zPos;
bool isRetracted;
bool isZHopped;
int isZHopped;
double last_coasted_amount_mm3; //!< The coasted amount of filament to be primed on the first next extrusion. (same type as GCodeExport::extrusion_amount)
double retractionPrimeSpeed;
@@ -159,15 +164,21 @@ private:
EGCodeFlavor flavor;
double totalPrintTime;
double total_print_time_per_feature[(unsigned int)EPrintFeature::ENUM_COUNT];
TimeEstimateCalculator estimateCalculator;
bool is_volumatric;
// for sending jump data
CommandSocket* commandSocket;
unsigned int layer_nr;
public:
GCodeExport();
~GCodeExport();
void setCommandSocketAndLayerNr(CommandSocket* commandSocket, unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
Point getExtruderOffset(int id);
@@ -201,13 +212,13 @@ public:
double getTotalFilamentUsed(int e);
double getTotalPrintTime();
double getTotalPrintTime(EPrintFeature print_feature);
void updateTotalPrintTime(EPrintFeature print_feature = EPrintFeature::UNCLASSIFIED);
void updateTotalPrintTime();
void resetTotalPrintTimeAndFilament();
void writeComment(std::string comment);
void writeTypeComment(const char* type);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
void writeLine(const char* line);
+17 -11
Ver Arquivo
@@ -31,10 +31,12 @@ void GCodePlanner::forceNewPathStart()
paths[paths.size()-1].done = true;
}
GCodePlanner::GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
GCodePlanner::GCodePlanner(CommandSocket* commandSocket, GCodeExport& gcode, SliceDataStorage& storage, int z, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: gcode(gcode), storage(storage)
, z(z)
, travelConfig(retraction_config_travel, "MOVE")
{
gcode.setCommandSocketAndLayerNr(commandSocket, layer_nr);
lastPosition = gcode.getPositionXY();
travelConfig.setSpeed(travelSpeed);
comb = nullptr;
@@ -108,11 +110,14 @@ void GCodePlanner::moveInsideCombBoundary(int distance)
void GCodePlanner::addTravel(Point p)
{
GCodePath* path = nullptr;
bool combed = false;
if (comb != nullptr && lastPosition != Point(0,0))
{
CombPaths combPaths;
if (comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb))
combed = comb->calc(lastPosition, p, combPaths, was_combing, is_going_to_comb);
if (combed)
{
bool retract = combPaths.size() > 1;
{ // check whether we want to retract
@@ -153,17 +158,18 @@ void GCodePlanner::addTravel(Point p)
}
}
}
else
{
path = getLatestPathWithConfig(&travelConfig);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
}
}
was_combing = is_going_to_comb;
}
if(!combed) {
// no combing? always retract!
path = getLatestPathWithConfig(&travelConfig);
if (!shorterThen(lastPosition - p, last_retraction_config->retraction_min_travel_distance))
{
path->retract = true;
}
}
addTravel_simple(p, path);
}
+4 -1
Ver Arquivo
@@ -8,6 +8,7 @@
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
namespace cura
@@ -42,6 +43,8 @@ private:
GCodeExport& gcode;
SliceDataStorage& storage;
int z;
Point lastPosition;
std::vector<GCodePath> paths;
@@ -87,7 +90,7 @@ public:
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
*/
GCodePlanner(GCodeExport& gcode, SliceDataStorage& storage, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
GCodePlanner(CommandSocket* commandSocket, GCodeExport& gcode, SliceDataStorage& storage, int z, RetractionConfig* retraction_config_travel, double travelSpeed, bool retraction_combing, unsigned int layer_nr, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
~GCodePlanner();
void setCombing(bool going_to_comb);
+5
Ver Arquivo
@@ -317,6 +317,11 @@ int main(int argc, char **argv)
{
slice(argc, argv);
}
else if (stringcasecompare(argv[1], "help") == 0)
{
print_usage();
exit(0);
}
else
{
cura::logError("Unknown command: %s\n", argv[1]);
+1 -1
Ver Arquivo
@@ -16,7 +16,7 @@ void generateRaft(SliceDataStorage& storage, int distance)
}
else
{
storage.raftOutline = storage.getLayerOutlines(0, true);
storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance);
}
}
+63 -57
Ver Arquivo
@@ -204,13 +204,14 @@ int SettingRegistry::loadJSONsettingsFromDoc(rapidjson::Document& json_document,
}
}
if (false && json_document.HasMember("overrides"))
if (json_document.HasMember("overrides"))
{
const rapidjson::Value& json_object_container = json_document["overrides"];
for (rapidjson::Value::ConstMemberIterator override_iterator = json_object_container.MemberBegin(); override_iterator != json_object_container.MemberEnd(); ++override_iterator)
{
SettingConfig* conf = getSettingConfig(override_iterator->name.GetString());
_addSettingToContainer(conf, override_iterator, false);
std::string setting = override_iterator->name.GetString();
SettingConfig* conf = getSettingConfig(setting);
_loadSettingValues(conf, override_iterator, false);
}
}
@@ -249,63 +250,68 @@ void SettingRegistry::_addSettingToContainer(SettingContainer* parent, rapidjson
/// Create the new setting config object.
SettingConfig* config = parent->addChild(json_object_it->name.GetString(), label);
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (data.HasMember("default"))
{
const rapidjson::Value& dflt = data["default"];
if (dflt.IsString())
{
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
/// Register the setting in the settings map lookup.
if (warn_duplicates && settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
}
if (add_to_settings)
{
settings[config->getKey()] = config;
}
_loadSettingValues(config, json_object_it, warn_duplicates, add_to_settings);
}
/// When this setting has children, add those children to this setting.
if (data.HasMember("children") && data["children"].IsObject())
void SettingRegistry::_loadSettingValues(SettingConfig* config, rapidjson::GenericValue< rapidjson::UTF8< char > >::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings)
{
const rapidjson::Value& data = json_object_it->value;
/// Fill the setting config object with data we have in the json file.
if (data.HasMember("type") && data["type"].IsString())
{
config->setType(data["type"].GetString());
}
if (data.HasMember("default"))
{
const rapidjson::Value& dflt = data["default"];
if (dflt.IsString())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(parent, setting_iterator, warn_duplicates, add_to_settings);
}
config->setDefault(dflt.GetString());
}
else if (dflt.IsTrue())
{
config->setDefault("true");
}
else if (dflt.IsFalse())
{
config->setDefault("false");
}
else if (dflt.IsNumber())
{
std::ostringstream ss;
ss << dflt.GetDouble();
config->setDefault(ss.str());
} // arrays are ignored because machine_extruder_trains needs to be handled separately
else
{
logError("Unrecognized data type in JSON: %s has type %s\n", json_object_it->name.GetString(), toString(dflt.GetType()).c_str());
}
}
if (data.HasMember("unit") && data["unit"].IsString())
{
config->setUnit(data["unit"].GetString());
}
/// Register the setting in the settings map lookup.
if (warn_duplicates && settingExists(config->getKey()))
{
cura::logError("Duplicate definition of setting: %s a.k.a. \"%s\" was already claimed by \"%s\"\n", config->getKey().c_str(), config->getLabel().c_str(), getSettingConfig(config->getKey())->getLabel().c_str());
}
if (add_to_settings)
{
settings[config->getKey()] = config;
}
/// When this setting has children, add those children to this setting.
if (data.HasMember("children") && data["children"].IsObject())
{
const rapidjson::Value& json_object_container = data["children"];
for (rapidjson::Value::ConstMemberIterator setting_iterator = json_object_container.MemberBegin(); setting_iterator != json_object_container.MemberEnd(); ++setting_iterator)
{
_addSettingToContainer(config, setting_iterator, warn_duplicates, add_to_settings);
}
}
}
+2
Ver Arquivo
@@ -185,6 +185,8 @@ private:
* \param warn_duplicates whether to warn for duplicate definitions
*/
void _addSettingToContainer(SettingContainer* parent, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
void _loadSettingValues(SettingConfig* config, rapidjson::Value::ConstMemberIterator& json_object_it, bool warn_duplicates, bool add_to_settings = true);
};
}//namespace cura
+40 -3
Ver Arquivo
@@ -14,6 +14,26 @@ namespace cura
#define M_PI 3.14159265358979323846
#endif
std::string toString(EGCodeFlavor flavor)
{
switch (flavor)
{
case EGCodeFlavor::BFB:
return "BFB";
case EGCodeFlavor::MACH3:
return "Mach3";
case EGCodeFlavor::MAKERBOT:
return "Makerbot";
case EGCodeFlavor::ULTIGCODE:
return "UltiGCode";
case EGCodeFlavor::REPRAP_VOLUMATRIC:
return "RepRap(Volumetric)";
case EGCodeFlavor::REPRAP:
default:
return "RepRap";
}
}
SettingsBaseVirtual::SettingsBaseVirtual()
: parent(NULL)
{
@@ -92,7 +112,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
if (!machine_extruder_trains)
{
logWarning("Error: no machine_extruder_trains category found in JSON!\n");
// no machine_extruder_trains setting present; just use defaults for each train..
return;
}
@@ -100,7 +120,7 @@ void SettingsBase::setExtruderTrainDefaults(unsigned int extruder_nr)
if (!train)
{
logError("Not enough extruder trains specified in JSON: %i\n", extruder_nr);
// not enough machine_extruder_trains settings present; just use defaults for this train..
return;
}
@@ -229,7 +249,7 @@ ESupportType SettingsBaseVirtual::getSettingAsSupportType(std::string key)
std::string value = getSettingString(key);
if (value == "everywhere")
return ESupportType::EVERYWHERE;
if (value == "touching_buildplate")
if (value == "buildplate")
return ESupportType::PLATFORM_ONLY;
return ESupportType::NONE;
}
@@ -258,5 +278,22 @@ ESurfaceMode SettingsBaseVirtual::getSettingAsSurfaceMode(std::string key)
return ESurfaceMode::NORMAL;
}
FillPerimeterGapMode SettingsBaseVirtual::getSettingAsFillPerimeterGapMode(std::string key)
{
std::string value = getSettingString(key);
if (value == "nowhere")
{
return FillPerimeterGapMode::NOWHERE;
}
if (value == "everywhere")
{
return FillPerimeterGapMode::EVERYWHERE;
}
if (value == "skin")
{
return FillPerimeterGapMode::SKIN;
}
return FillPerimeterGapMode::NOWHERE;
}
}//namespace cura
+13
Ver Arquivo
@@ -75,6 +75,11 @@ enum class EGCodeFlavor
REPRAP_VOLUMATRIC = 5,
};
/*!
* Converts a gcode flavor type to string so that it can be included in the gcode.
*/
std::string toString(EGCodeFlavor flavor);
/*!
* In Cura different infill methods are available.
* This enum defines which fill patterns are available to get a uniform naming troughout the engine.
@@ -124,6 +129,13 @@ enum class ESurfaceMode
BOTH
};
enum class FillPerimeterGapMode
{
NOWHERE,
EVERYWHERE,
SKIN
};
#define MAX_EXTRUDERS 16
//Maximum number of infill layers that can be combined into a single infill extrusion area.
@@ -171,6 +183,7 @@ public:
ESupportType getSettingAsSupportType(std::string key);
EZSeamType getSettingAsZSeamType(std::string key);
ESurfaceMode getSettingAsSurfaceMode(std::string key);
FillPerimeterGapMode getSettingAsFillPerimeterGapMode(std::string key);
};
/*!
+94 -41
Ver Arquivo
@@ -2,15 +2,15 @@
#include "skin.h"
#include "utils/polygonUtils.h"
#define MIN_AREA_SIZE (INT2MM(extrusionWidth) * INT2MM(extrusionWidth))
#define MIN_AREA_SIZE (0.4 * 0.4)
namespace cura
{
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters)
{
generateSkinAreas(layerNr, storage, extrusionWidth, downSkinCount, upSkinCount);
generateSkinAreas(layerNr, storage, innermost_wall_extrusion_width, downSkinCount, upSkinCount, no_small_gaps_heuristic);
SliceLayer* layer = &storage.layers[layerNr];
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
@@ -20,39 +20,71 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
}
}
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount)
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, bool no_small_gaps_heuristic)
{
SliceLayer& layer = storage.layers[layerNr];
SliceLayer& layer = storage.layers[layer_nr];
if (downSkinCount == 0 && upSkinCount == 0)
{
return;
}
for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
{
SliceLayerPart& part = layer.parts[partNr];
Polygons upskin = part.insets.back().offset(-extrusionWidth/2);
Polygons downskin = upskin;
Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2);
Polygons downskin = (downSkinCount == 0)? Polygons() : upskin;
if (upSkinCount == 0) upskin = Polygons();
if (static_cast<int>(layerNr - downSkinCount) >= 0)
{
SliceLayer& layer2 = storage.layers[layerNr - downSkinCount];
for(SliceLayerPart& part2 : layer2.parts)
auto getInsidePolygons = [&part](SliceLayer& layer2)
{
if (part.boundaryBox.hit(part2.boundaryBox))
downskin = downskin.difference(part2.insets.back());
Polygons result;
for(SliceLayerPart& part2 : layer2.parts)
{
if (part.boundaryBox.hit(part2.boundaryBox))
result.add(part2.insets.back());
}
return result;
};
if (no_small_gaps_heuristic)
{
if (static_cast<int>(layer_nr - downSkinCount) >= 0)
{
downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
}
if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size()))
{
upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
}
}
if (static_cast<int>(layerNr + upSkinCount) < static_cast<int>(storage.layers.size()))
else
{
SliceLayer& layer2 = storage.layers[layerNr + upSkinCount];
for(SliceLayerPart& part2 : layer2.parts)
if (layer_nr > 0 && downSkinCount > 0)
{
if (part.boundaryBox.hit(part2.boundaryBox))
upskin = upskin.difference(part2.insets.back());
Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]);
for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr]));
}
downskin = downskin.difference(not_air); // skin overlaps with the walls
}
if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0)
{
Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]);
for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++)
{
not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr]));
}
upskin = upskin.difference(not_air); // skin overlaps with the walls
}
}
Polygons skin = upskin.unionPolygons(downskin);
skin.removeSmallAreas(MIN_AREA_SIZE);
for (PolygonsPart& skin_area_part : skin.splitIntoParts())
@@ -121,31 +153,52 @@ void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth,
}
}
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount)
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount)
{
SliceLayer* layer = &storage.layers[layerNr];
for(int n=1; n<amount; n++)
if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
{
if (layerNr < n)
break;
SliceLayer* layer2 = &storage.layers[layerNr - n];
for(SliceLayerPart& part : layer->parts)
return;
}
if(storage.layers.empty() || storage.layers.size() - 1 < static_cast<size_t>(storage.getSettingAsCount("top_layers")) || storage.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
{
return;
}
/* We need to round down the layer index we start at to the nearest
divisible index. Otherwise we get some parts that have infill at divisible
layers and some at non-divisible layers. Those layers would then miss each
other. */
size_t min_layer = storage.getSettingAsCount("bottom_layers") + amount - 1;
min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
size_t max_layer = storage.layers.size() - 1 - storage.getSettingAsCount("top_layers");
max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
{
SliceLayer* layer = &storage.layers[layer_idx];
for(unsigned int n = 1;n < amount;n++)
{
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
if(layer_idx < n)
{
if (part.boundaryBox.hit(part2.boundaryBox))
{
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
}
break;
}
SliceLayer* layer2 = &storage.layers[layer_idx - n];
for(SliceLayerPart& part : layer->parts)
{
Polygons result;
for(SliceLayerPart& part2 : layer2->parts)
{
if(part.boundaryBox.hit(part2.boundaryBox))
{
Polygons intersection = part.infill_area[n - 1].intersection(part2.infill_area[0]).offset(-200).offset(200);
result.add(intersection);
part.infill_area[n - 1] = part.infill_area[n - 1].difference(intersection);
part2.infill_area[0] = part2.infill_area[0].difference(intersection);
}
}
part.infill_area.push_back(result);
}
part.infill_area.push_back(result);
}
}
}
+17 -3
Ver Arquivo
@@ -26,11 +26,13 @@ void generatePerimeterGaps(int layerNr, SliceMeshStorage& storage, int extrusion
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param innermost_wall_extrusion_width The line width of the inner most wall
* \param insetCount The number of perimeters to surround the skin
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
* \param avoidOverlappingPerimeters_0 Whether to remove the parts of the first perimeters where it have overlap with itself (and store the gaps thus created in the \p storage)
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p storage)
*/
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int insetCount, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, int innermost_wall_extrusion_width, int insetCount, bool no_small_gaps_heuristic, bool avoidOverlappingPerimeters_0, bool avoidOverlappingPerimeters);
/*!
* Generate the skin areas (outlines)
@@ -40,8 +42,9 @@ void generateSkins(int layerNr, SliceMeshStorage& storage, int extrusionWidth, i
* \param extrusionWidth extrusionWidth
* \param downSkinCount The number of layers of bottom skin
* \param upSkinCount The number of layers of top skin
* \param no_small_gaps_heuristic A heuristic which assumes there will be no small gaps between bottom and top skin with a z size smaller than the skin size itself
*/
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount);
void generateSkinAreas(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount, bool no_small_gaps_heuristic);
/*!
* Generate the skin insets.
@@ -63,7 +66,18 @@ void generateSkinInsets(SliceLayerPart* part, int extrusionWidth, int insetCount
* \param infill_skin_overlap overlap distance between infill and skin
*/
void generateInfill(int layerNr, SliceMeshStorage& storage, int extrusionWidth, int infill_skin_overlap);
void combineInfillLayers(int layerNr, SliceMeshStorage& storage, int amount);
/*!
* \brief Combines the infill of multiple layers for a specified mesh.
*
* The infill layers are combined while the thickness of each layer is
* multiplied such that the infill should fill up again to the full height of
* all combined layers.
*
* \param storage The mesh to combine the infill layers of.
* \param amount The number of layers to combine.
*/
void combineInfillLayers(SliceMeshStorage& storage,unsigned int amount);
}//namespace cura
+41 -14
Ver Arquivo
@@ -26,28 +26,55 @@ void SliceLayer::getOutlines(Polygons& result, bool external_polys_only)
}
Polygons SliceDataStorage::getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only)
Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only)
{
Polygons total;
for (SliceMeshStorage& mesh : meshes)
{
SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
if (layer_nr < 0)
{ // when processing raft
if (include_helper_parts)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
if (external_polys_only)
{
std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
Polygons result;
for (PolygonsPart& part : parts)
{
result.add(part.outerPolygon());
}
return result;
}
else
{
return raftOutline;
}
}
else
{
return Polygons();
}
}
if (include_helper_parts)
else
{
if (support.generated)
Polygons total;
for (SliceMeshStorage& mesh : meshes)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
SliceLayer& layer = mesh.layers[layer_nr];
layer.getOutlines(total, external_polys_only);
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
{
total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
}
}
total.add(primeTower.ground_poly);
if (include_helper_parts)
{
if (support.generated)
{
total.add(support.supportLayers[layer_nr].supportAreas);
total.add(support.supportLayers[layer_nr].roofs);
}
total.add(primeTower.ground_poly);
}
return total;
}
return total;
}
+2 -2
Ver Arquivo
@@ -163,11 +163,11 @@ public:
/*!
* Get all outlines within a given layer.
*
* \param layer_nr the index of the layer for which to get the outlines
* \param layer_nr the index of the layer for which to get the outlines (negative layer numbers indicate the raft)
* \param include_helper_parts whether to include support and prime tower
* \param external_polys_only whether to disregard all hole polygons
*/
Polygons getLayerOutlines(unsigned int layer_nr, bool include_helper_parts, bool external_polys_only = false);
Polygons getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only = false);
};
}//namespace cura
+28 -25
Ver Arquivo
@@ -41,11 +41,12 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
storage.support.supportLayers.emplace_back();
for(SliceMeshStorage& mesh : storage.meshes)
for(unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
std::vector<Polygons> supportAreas;
supportAreas.resize(layer_count, Polygons());
generateSupportAreas(storage, &mesh, layer_count, supportAreas, commandSocket);
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas, commandSocket);
if (mesh.getSettingBoolean("support_roof_enable"))
{
@@ -79,43 +80,45 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int l
*
* for support buildplate only: purge all support not connected to buildplate
*/
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, 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, CommandSocket* commandSocket)
{
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
// given settings
ESupportType support_type = object->getSettingAsSupportType("support_type");
ESupportType support_type = mesh.getSettingAsSupportType("support_type");
if (!object->getSettingBoolean("support_enable"))
if (!mesh.getSettingBoolean("support_enable"))
return;
if (support_type == ESupportType::NONE)
return;
double supportAngle = object->getSettingInAngleRadians("support_angle");
double supportAngle = mesh.getSettingInAngleRadians("support_angle");
bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
int supportZDistance = object->getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = object->getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = object->getSettingInMicrons("support_top_distance");
int join_distance = object->getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = object->getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = object->getSettingInMicrons("support_area_smoothing");
int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
int join_distance = mesh.getSettingInMicrons("support_join_distance");
int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing");
int extension_offset = object->getSettingInMicrons("support_offset");
int extension_offset = mesh.getSettingInMicrons("support_offset");
int supportTowerDiameter = object->getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = object->getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = object->getSettingInAngleRadians("support_tower_roof_angle");
int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
//std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
int z_layer_distance_tower = 1; // start tower directly below overhang point
int layerThickness = object->getSettingInMicrons("layer_height");
int extrusionWidth = object->getSettingInMicrons("support_line_width");
int supportXYDistance = object->getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
int layerThickness = mesh.getSettingInMicrons("layer_height");
int extrusionWidth = mesh.getSettingInMicrons("support_line_width");
int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
bool conical_support = object->getSettingBoolean("support_conical_enabled");
double conical_support_angle = object->getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = object->getSettingInMicrons("support_conical_min_width");
bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
// derived settings:
@@ -160,7 +163,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
AreaSupport::detectOverhangPoints(storage, *object, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth);
bool still_in_upper_empty_layers = true;
@@ -173,7 +176,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
Polygons overhang;
{
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
Polygons supportLayer_supportee = object->layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
Polygons supportLayer_supported = supportLayer_supporter.offset(maxDistFromLowerLayer);
@@ -249,7 +252,7 @@ void AreaSupport::generateSupportAreas(SliceDataStorage& storage, SliceMeshStora
still_in_upper_empty_layers = false;
}
Progress::messageProgress(Progress::Stage::SUPPORT, support_layer_count - layer_idx, support_layer_count, commandSocket);
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
}
// do stuff for when support on buildplate only
+2 -2
Ver Arquivo
@@ -26,11 +26,11 @@ private:
* This function also handles small overhang areas (creates towers with larger diameter than just the overhang area) and single walls which could otherwise fall over.
*
* \param storage data storage containing the input layer outline data
* \param object The object for which to generate support areas
* \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, SliceMeshStorage* object, 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, CommandSocket* commandSocket);
+9 -2
Ver Arquivo
@@ -24,8 +24,15 @@ void TimeEstimateCalculator::setPosition(Position newPos)
currentPosition = newPos;
}
void TimeEstimateCalculator::addTime(double time)
{
extra_time += time;
}
void TimeEstimateCalculator::reset()
{
extra_time = 0.0;
blocks.clear();
}
@@ -190,7 +197,7 @@ double TimeEstimateCalculator::calculate()
forward_pass();
recalculate_trapezoids();
double totalTime = 0;
double totalTime = extra_time;
for(unsigned int n=0; n<blocks.size(); n++)
{
Block& block = blocks[n];
@@ -287,7 +294,7 @@ void TimeEstimateCalculator::recalculate_trapezoids()
Block *current;
Block *next = nullptr;
for(unsigned int n=0; n<blocks.size(); n--)
for(unsigned int n=0; n<blocks.size(); n++)
{
current = next;
next = &blocks[n];
+12 -4
Ver Arquivo
@@ -7,10 +7,10 @@
namespace cura
{
/**
The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
Some of this code has been addapted from the Marlin sources.
*/
/*!
* The TimeEstimateCalculator class generates a estimate of printing time calculated with acceleration in mind.
* Some of this code has been adapted from the Marlin sources.
*/
class TimeEstimateCalculator
{
@@ -54,6 +54,8 @@ public:
};
private:
double extra_time;
Position previous_feedrate;
double previous_nominal_feedrate;
@@ -61,8 +63,14 @@ private:
std::vector<Block> blocks;
public:
TimeEstimateCalculator()
: extra_time(0.0)
{
}
void setPosition(Position newPos);
void plan(Position newPos, double feedRate);
void addTime(double time);
void reset();
double calculate();
+18 -6
Ver Arquivo
@@ -18,7 +18,7 @@ public:
Point min, max;
AABB()
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
}
AABB(Point&min, Point& max)
@@ -26,7 +26,7 @@ public:
{
}
AABB(Polygons& polys)
: min(POINT_MIN, POINT_MIN), max(POINT_MIN, POINT_MIN)
: min(POINT_MAX, POINT_MAX), max(POINT_MIN, POINT_MIN)
{
calculate(polys);
}
@@ -39,10 +39,7 @@ public:
{
for(unsigned int j=0; j<polys[i].size(); j++)
{
if (min.X > polys[i][j].X) min.X = polys[i][j].X;
if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y;
if (max.X < polys[i][j].X) max.X = polys[i][j].X;
if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y;
include(polys[i][j]);
}
}
}
@@ -55,6 +52,21 @@ public:
if (min.Y > other.max.Y) return false;
return true;
}
/*!
* \brief Includes the specified point in the bounding box.
*
* The bounding box is expanded if the point is not within the bounding box.
*
* \param point The point to include in the bounding box.
*/
void include(Point point)
{
min.X = std::min(min.X,point.X);
min.Y = std::min(min.Y,point.Y);
max.X = std::max(max.X,point.X);
max.Y = std::max(max.Y,point.Y);
}
};
/*!
+73 -10
Ver Arquivo
@@ -6,6 +6,7 @@
#include "polygon.h"
#include "intpoint.h"
#include "AABB.h"
#include "logoutput.h"
namespace cura {
@@ -18,7 +19,8 @@ public:
GRAY,
RED,
BLUE,
GREEN
GREEN,
YELLOW
};
private:
@@ -33,6 +35,7 @@ private:
case SVG::Color::RED: return "red";
case SVG::Color::BLUE: return "blue";
case SVG::Color::GREEN: return "green";
case SVG::Color::YELLOW: return "yellow";
default: return "black";
}
}
@@ -51,8 +54,12 @@ public:
, scale(std::min(double(canvas_size.X - 20) / aabb_size.X, double(canvas_size.Y - 20) / aabb_size.Y))
{
out = fopen(filename, "w");
if(!out)
{
logError("The file %s could not be opened for writing.",filename);
}
fprintf(out, "<!DOCTYPE html><html><body>\n");
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.Y, canvas_size.X);
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width:%llipx;height:%llipx\">\n", canvas_size.X, canvas_size.Y);
// fprintf(out, "<marker id='MidMarker' viewBox='0 0 10 10' refX='5' refY='5' markerUnits='strokeWidth' markerWidth='10' markerHeight='10' stroke='lightblue' stroke-width='2' fill='none' orient='auto'>");
// fprintf(out, "<path d='M 0 0 L 10 5 M 0 10 L 10 5'/>");
@@ -69,7 +76,7 @@ public:
/*!
* transform a point in real space to canvas space
*/
Point transform(Point& p)
Point transform(const Point& p)
{
return Point((p.X-aabb.min.X)*scale, (p.Y-aabb.min.Y)*scale) + Point(10,10);
}
@@ -123,7 +130,7 @@ public:
for(Point& p : poly)
{
Point fp = transform(p);
fprintf(out, "%lli,%lli ", fp.Y, fp.X);
fprintf(out, "%lli,%lli ", fp.X, fp.Y);
}
if (j == 0)
fprintf(out, "\" style=\"fill:%s;stroke:%s;stroke-width:1\" />\n", toString(color).c_str(), toString(outline_color).c_str());
@@ -133,14 +140,25 @@ public:
}
}
void writePoint(Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
void writeAreas(std::vector<Point> polygon,Color color = Color::GRAY,Color outline_color = Color::BLACK)
{
fprintf(out,"<polygon fill=\"%s\" stroke=\"%s\" stroke-width=\"1\" points=\"",toString(color).c_str(),toString(outline_color).c_str()); //The beginning of the polygon tag.
for(Point& point : polygon) //Add every point to the list of points.
{
Point transformed = transform(point);
fprintf(out,"%lli,%lli ",transformed.X,transformed.Y);
}
fprintf(out,"\" />\n"); //The end of the polygon tag.
}
void writePoint(const Point& p, bool write_coords=false, int size = 5, Color color = Color::BLACK)
{
Point pf = transform(p);
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.Y, pf.X, size, toString(color).c_str(), toString(color).c_str());
fprintf(out, "<circle cx=\"%lli\" cy=\"%lli\" r=\"%d\" stroke=\"%s\" stroke-width=\"1\" fill=\"%s\" />\n",pf.X, pf.Y, size, toString(color).c_str(), toString(color).c_str());
if (write_coords)
{
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.Y, pf.X, p.X, p.Y);
fprintf(out, "<text x=\"%lli\" y=\"%lli\" style=\"font-size: 10;\" fill=\"black\">%lli,%lli</text>\n",pf.X, pf.Y, p.X, p.Y);
}
}
void writePoints(PolygonRef poly, bool write_coords=false, int size = 5, Color color = Color::BLACK)
@@ -159,14 +177,59 @@ public:
}
}
void writeLine(Point& a, Point& b, Color color = Color::BLACK)
/*!
* \brief Draws a polyline on the canvas.
*
* The polyline is the set of line segments between each pair of consecutive
* points in the specified vector.
*
* \param polyline A set of points between which line segments must be
* drawn.
* \param color The colour of the line segments. If this is not specified,
* black will be used.
*/
void writeLines(std::vector<Point> polyline,Color color = Color::BLACK)
{
if(polyline.size() <= 1) //Need at least 2 points.
{
return;
}
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />", a.Y, a.X, b.Y, b.X, toString(color).c_str());
Point transformed = transform(polyline[0]); //Element 0 must exist due to the check above.
fprintf(out,"<path fill=\"none\" stroke=\"%s\" stroke-width=\"1\" d=\"M%lli,%lli",toString(color).c_str(),transformed.X,transformed.Y); //Write the start of the path tag and the first endpoint.
for(size_t point = 1;point < polyline.size();point++)
{
transformed = transform(polyline[point]);
fprintf(out,"L%lli,%lli",transformed.X,transformed.Y); //Write a line segment to the next point.
}
fprintf(out,"\" />\n"); //Write the end of the tag.
}
void writeLine(const Point& a, const Point& b, Color color = Color::BLACK)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out, "<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" style=\"stroke:%s;stroke-width:1\" />\n", fa.X, fa.Y, fb.X, fb.Y, toString(color).c_str());
}
/*!
* \brief Draws a dashed line on the canvas from point A to point B.
*
* This is useful in the case where multiple lines may overlap each other.
*
* \param a The starting endpoint of the line.
* \param b The ending endpoint of the line.
* \param color The stroke colour of the line.
*/
void writeDashedLine(const Point& a,const Point& b,Color color = Color::BLACK)
{
Point fa = transform(a);
Point fb = transform(b);
fprintf(out,"<line x1=\"%lli\" y1=\"%lli\" x2=\"%lli\" y2=\"%lli\" stroke=\"%s\" stroke-width=\"1\" stroke-dasharray=\"5,5\" />\n",fa.X,fa.Y,fb.X,fb.Y,toString(color).c_str());
}
template<typename... Args>
void printf(const char* txt, Args... args)
void printf(const char* txt, Args&&... args)
{
fprintf(out, txt, args...);
}
+1 -1
Ver Arquivo
@@ -108,7 +108,7 @@ public:
* \param c the second point on the line segment
* \param b_is_beyond_ac optional output parameter: whether \p b is closest to the line segment (0), to \p a (-1) or \p b (1)
*/
static int64_t getDist2FromLineSegment(Point& a, Point& b, Point& c, char* b_is_beyond_ac = nullptr)
static int64_t getDist2FromLineSegment(const Point& a, const Point& b, const Point& c, char* b_is_beyond_ac = nullptr)
{
/*
* a,
+4 -2
Ver Arquivo
@@ -30,7 +30,9 @@ enum PolygonType
SupportType,
SkirtType,
InfillType,
SupportInfillType
SupportInfillType,
MoveCombingType,
MoveRetractionType
};
@@ -81,7 +83,7 @@ public:
ClipperLib::Path& operator*() { return *polygon; }
template <typename... Args>
void emplace_back(Args... args)
void emplace_back(Args&&... args)
{
polygon->emplace_back(args...);
}
+8 -3
Ver Arquivo
@@ -104,8 +104,13 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
Point ab = b - a;
Point ap = p - a;
int64_t ab_length = vSize(ab);
if(ab_length <= 0) //A = B, i.e. the input polygon had two adjacent points on top of each other.
{
p1 = p2; //Skip only one of the points.
continue;
}
int64_t ax_length = dot(ab, ap) / ab_length;
if (ax_length < 0) // x is projected to before ab
if (ax_length <= 0) // x is projected to before ab
{
if (projected_p_beyond_prev_segment)
{ // case which looks like: > .
@@ -120,7 +125,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
if (distance == 0) { ret = x; }
else
{
Point inward_dir = crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4));
Point inward_dir = crossZ(normal(ab,distance * 4) + normal(p1 - p0,distance * 4));
ret = x + normal(inward_dir, distance); // *4 to retain more precision for the eventual normalization
is_inside = dot(inward_dir, p - x) >= 0;
}
@@ -134,7 +139,7 @@ unsigned int PolygonUtils::moveInside(Polygons& polygons, Point& from, int dista
continue;
}
}
else if (ax_length > ab_length) // x is projected to beyond ab
else if (ax_length >= ab_length) // x is projected to beyond ab
{
projected_p_beyond_prev_segment = true;
p0 = p1;
+95
Ver Arquivo
@@ -0,0 +1,95 @@
//Copyright (c) 2015 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "LinearAlg2DTest.h"
#include <../src/utils/linearAlg2D.h>
namespace cura
{
CPPUNIT_TEST_SUITE_REGISTRATION(LinearAlg2DTest);
void LinearAlg2DTest::setUp()
{
//Do nothing.
}
void LinearAlg2DTest::tearDown()
{
//Do nothing.
}
void LinearAlg2DTest::getDist2FromLineSegmentTest()
{
char is_beyond;
//Horizontal line.
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,3),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,0),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Points beyond the horizontal line.
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,0),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,0),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,3),Point(100,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Vertical line.
CPPUNIT_ASSERT_EQUAL(int64_t(25), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(5,25),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,25),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Points beyond the vertical line.
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,200),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(10000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,-100),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(2), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-1,-1),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,0),Point(0,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//45 degrees diagonal line.
CPPUNIT_ASSERT_EQUAL(int64_t(50), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(30,20),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(25,25),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Points beyond the diagonal line.
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,200),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(20000),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-100),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(18), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(3,-3),Point(100,100),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Arbitrary degree diagonal line.
CPPUNIT_ASSERT_EQUAL(int64_t(320), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(20,30),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(40,20),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Points beyond the diagonal line.
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(200,100),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(12500),LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-100,-50),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(9), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-3,0),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(-1), is_beyond);
CPPUNIT_ASSERT_EQUAL(int64_t(20), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(-2,4),Point(100,50),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
//Zero-distance line.
CPPUNIT_ASSERT_EQUAL(int64_t(100), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(10,0),&is_beyond));
CPPUNIT_ASSERT(is_beyond != 0); //It's beyond, but which direction it is beyond in doesn't matter.
CPPUNIT_ASSERT_EQUAL(int64_t(0), LinearAlg2D::getDist2FromLineSegment(Point(0,0),Point(0,0),Point(0,0),&is_beyond));
CPPUNIT_ASSERT_EQUAL(char(0), is_beyond);
}
}
+46
Ver Arquivo
@@ -0,0 +1,46 @@
//Copyright (c) 2015 Ultimaker B.V.
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
#ifndef LINEARALG2DTEST_H
#define LINEARALG2DTEST_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
namespace cura
{
class LinearAlg2DTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(LinearAlg2DTest);
CPPUNIT_TEST(getDist2FromLineSegmentTest);
CPPUNIT_TEST_SUITE_END();
public:
/*!
* \brief Sets up the test suite to prepare for testing.
*
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
* needs to be created here.
*/
void setUp();
/*!
* \brief Tears down the test suite when testing is done.
*
* Since <em>LinearAlg2DTest</em> only has static functions, no instance
* exists that needs to be destroyed.
*/
void tearDown();
/*!
* \brief Tests the LinearAlg2D#getDist2FromLineSegment function.
*/
void getDist2FromLineSegmentTest();
};
}
#endif //LINEARALG2DTEST_H
+25
Ver Arquivo
@@ -0,0 +1,25 @@
//Copyright (c) 2015 Ultimaker B.V.
//UltiScanTastic is released under the terms of the AGPLv3 or higher.
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/XmlOutputter.h>
/*!
* \brief Runs the test cases.
*/
int main(int argc,char** argv)
{
CppUnit::TextUi::TestRunner runner;
//Set the output type to be JUnit-style XML.
std::ofstream output("output.xml");
CppUnit::XmlOutputter* outputter = new CppUnit::XmlOutputter(&runner.result(), output);
runner.setOutputter(outputter);
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest()); //Makes a test suite from all test cases that are registered with CPPUNIT_TEST_SUITE_REGISTRATION().
bool success = runner.run("", false); //Run the tests!
return success ? 0 : 1;
}