Comparar commits

..

8 Commits

Autor SHA1 Mensagem Data
Tim Kuipers d36934e35b bugfix: sliceDataStorage.meshes reallocation due to size change 2015-08-07 15:32:19 +02:00
Tim Kuipers 41f7258e25 bugfix: retraction count max 2015-08-05 14:30:57 +02:00
Tim Kuipers 24cb4fbb36 bugfix for combined infill lines 2015-07-28 14:00:51 +02:00
Tim Kuipers 6fa022013d bugfix: infill overlap for 100% infill 2015-07-22 15:30:45 +02:00
Tim Kuipers 9274846970 merge 2015-07-22 13:03:54 +02:00
Tim Kuipers 9050f8da53 cleanup debug shite last bugfix 2015-07-20 14:37:59 +02:00
Tim Kuipers f7039848c2 bugfix: remove degenerate verts: verts connected to overlapping line segments 2015-07-20 14:18:14 +02:00
Arjen Hiemstra d4e27d8bbb Force using clang libc++ on MacOSX to prevent issues with linking 2015-07-16 15:58:00 +02:00
210 arquivos alterados com 8702 adições e 30571 exclusões
+13 -38
Ver Arquivo
@@ -1,50 +1,25 @@
*.tar.bz2
*.tar.gz
*.7z
*.zip
.DS_Store
*~
NUL
*.gcode
## Building result.
build/*
*.pyc
*.zip
*.exe
*.a
*.o
CuraEngine
.idea
.DS_Store
_bin
_obj
## CMake files
cmake_install.cmake
CMakeCache.txt
CMakeFiles/
CPackSourceConfig.cmake
# Visual Studio files generated by CMake
*.vcxproj
*.vcxproj.filters
CuraEngine.sln
# Makefile generated by CMake
Makefile
## IDE project files.
*.depend
*.o
.*.swp
*.gcode
CuraEngine
build/*
*~
NUL
CuraEngine.layout
CuraEngine.cbp
*kdev*
*.kate-swp
nbproject/*
.idea
*.depend
.*.swp
## Documentation.
documentation/html/*
documentation/latex/*
## Test results.
tests/output.xml
callgrind.out.*
*kdev*
*.kate-swp
+17 -137
Ver Arquivo
@@ -2,14 +2,7 @@ project(CuraEngine)
cmake_minimum_required(VERSION 2.8.12)
option (ENABLE_ARCUS
"Enable support for ARCUS" ON)
if (ENABLE_ARCUS)
message(STATUS "Building with Arcus")
find_package(Arcus REQUIRED)
add_definitions(-DARCUS)
endif ()
find_package(Arcus REQUIRED)
if(NOT ${CMAKE_VERSION} VERSION_LESS 3.1)
set(CMAKE_CXX_STANDARD 11)
@@ -17,175 +10,62 @@ else()
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CURA_ENGINE_VERSION "master" CACHE STRING "Version name of Cura")
option(BUILD_TESTS OFF)
# Add a compiler flag to check the output for insane values if we are in debug mode.
if(CMAKE_BUILD_TYPE MATCHES DEBUG OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "Building debug release of CuraEngine.")
add_definitions(-DASSERT_INSANE_OUTPUT)
add_definitions(-DUSE_CPU_TIME)
add_definitions(-DDEBUG)
endif()
# Add warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
if(NOT APPLE AND NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++")
endif()
option (ENABLE_OPENMP
"Use OpenMP for parallel code" ON)
if (ENABLE_OPENMP)
FIND_PACKAGE( OpenMP )
if( OPENMP_FOUND )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}" )
endif()
elseif(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR} libs)
add_library(clipper STATIC libs/clipper/clipper.cpp)
set(engine_SRCS # Except main.cpp.
set(engine_SRCS
src/bridge.cpp
src/comb.cpp
src/commandSocket.cpp
src/ConicalOverhang.cpp
src/ExtruderTrain.cpp
src/FffGcodeWriter.cpp
src/FffPolygonGenerator.cpp
src/FffProcessor.cpp
src/gcodeExport.cpp
src/GCodePathConfig.cpp
src/gcodePlanner.cpp
src/infill.cpp
src/WallsComputation.cpp
src/inset.cpp
src/layerPart.cpp
src/LayerPlanBuffer.cpp
src/MergeInfillLines.cpp
src/main.cpp
src/mesh.cpp
src/MeshGroup.cpp
src/multiVolumes.cpp
src/pathOrderOptimizer.cpp
src/Preheat.cpp
src/PrimeTower.cpp
src/polygonOptimizer.cpp
src/raft.cpp
src/settingRegistry.cpp
src/settings.cpp
src/skin.cpp
src/SkirtBrim.cpp
src/sliceDataStorage.cpp
src/skirt.cpp
src/slicer.cpp
src/support.cpp
src/timeEstimate.cpp
src/WallsComputation.cpp
src/wallOverlap.cpp
src/Weaver.cpp
src/Wireframe2gcode.cpp
src/multithreadOpenMP.cpp
src/infill/NoZigZagConnectorProcessor.cpp
src/infill/ZigzagConnectorProcessorConnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorDisconnectedEndPieces.cpp
src/infill/ZigzagConnectorProcessorEndPieces.cpp
src/infill/ZigzagConnectorProcessorNoEndPieces.cpp
src/infill/SubDivCube.cpp
src/modelFile/modelFile.cpp
src/pathPlanning/Comb.cpp
src/pathPlanning/GCodePath.cpp
src/pathPlanning/LinePolygonsCrossings.cpp
src/pathPlanning/NozzleTempInsert.cpp
src/pathPlanning/TimeMaterialEstimates.cpp
src/progress/Progress.cpp
src/progress/ProgressStageEstimator.cpp
src/settings/SettingConfig.cpp
src/settings/SettingContainer.cpp
src/settings/SettingRegistry.cpp
src/settings/settings.cpp
src/utils/AABB.cpp
src/utils/AABB3D.cpp
src/utils/Date.cpp
src/utils/gettime.cpp
src/utils/LinearAlg2D.cpp
src/utils/ListPolyIt.cpp
src/utils/logoutput.cpp
src/utils/PolygonProximityLinker.cpp
src/utils/polygonUtils.cpp
src/utils/polygon.cpp
src/utils/ProximityPointLink.cpp
)
# List of tests. For each test there must be a file tests/${NAME}.cpp and a file tests/${NAME}.h.
set(engine_TEST
GCodePlannerTest
)
set(engine_TEST_INFILL
)
set(engine_TEST_UTILS
SparseGridTest
LinearAlg2DTest
PolygonUtilsTest
PolygonTest
StringTest
)
# Generating ProtoBuf protocol
if (ENABLE_ARCUS)
protobuf_generate_cpp(engine_PB_SRCS engine_PB_HEADERS Cura.proto)
endif ()
# Compiling CuraEngine itself.
add_library(_CuraEngine ${engine_SRCS} ${engine_PB_SRCS}) #First compile all of CuraEngine as library, allowing this to be re-used for tests.
target_link_libraries(_CuraEngine clipper)
if (ENABLE_ARCUS)
target_link_libraries(_CuraEngine Arcus)
endif ()
add_executable(CuraEngine ${engine_SRCS} ${engine_PB_SRCS})
target_link_libraries(CuraEngine clipper Arcus)
set_target_properties(_CuraEngine PROPERTIES COMPILE_DEFINITIONS "VERSION=\"${CURA_ENGINE_VERSION}\"")
add_executable(Test src/test.cpp)
target_link_libraries(Test clipper)
if (UNIX)
target_link_libraries(_CuraEngine pthread)
endif()
add_executable(CuraEngine src/main.cpp) #Then compile main.cpp as separate executable, and link the library to it.
target_link_libraries(CuraEngine _CuraEngine)
# Compiling the test environment.
if (BUILD_TESTS)
message(STATUS "Building tests...")
enable_testing()
foreach (test ${engine_TEST})
add_executable(${test} tests/main.cpp tests/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_INFILL})
add_executable(${test} tests/main.cpp tests/infill/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
foreach (test ${engine_TEST_UTILS})
add_executable(${test} tests/main.cpp tests/utils/${test}.cpp)
target_link_libraries(${test} _CuraEngine cppunit)
add_test(${test} ${test})
endforeach()
target_link_libraries(CuraEngine pthread)
endif()
add_custom_command(TARGET CuraEngine POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources $<TARGET_FILE_DIR:CuraEngine>)
# Installing CuraEngine.
include(GNUInstallDirs)
install(TARGETS CuraEngine DESTINATION ${CMAKE_INSTALL_BINDIR})
include(CPackConfig.cmake)
-20
Ver Arquivo
@@ -1,20 +0,0 @@
set(CPACK_PACKAGE_VENDOR "Ultimaker")
set(CPACK_PACKAGE_CONTACT "Arjen Hiemstra <a.hiemstra@ultimaker.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cura Engine")
set(CPACK_PACKAGE_VERSION "15.05.90")
set(CPACK_GENERATOR "DEB")
if(NOT DEFINED CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
execute_process(COMMAND dpkg --print-architecture OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
set(DEB_DEPENDS
"arcus (>= 15.05.90)"
"protobuf (>= 3.0.0)"
"libstdc++6 (>= 4.9.0)"
"libgcc1 (>= 4.9.0)"
)
string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS})
include(CPack)
-14
Ver Arquivo
@@ -1,14 +0,0 @@
Changelog CuraEngine
====================
- Feature: infill_wipe_dist. Add a travel move after an infill line in order to let it stick better to the walls.
- Feature: Draft Protection Screen. A shell similar to the ooze shield providing protection from gusts of wind and acting similar to a heated chamber
Release 15.06.01
-----
- [Not documented]
+35 -73
Ver Arquivo
@@ -1,29 +1,13 @@
syntax = "proto3";
package cura.proto;
package Cura;
message ObjectList
{
// typeid 1
message ObjectList {
repeated Object objects = 1;
repeated Setting settings = 2; // meshgroup settings (for one-at-a-time printing)
}
message Slice
{
repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another
SettingList global_settings = 2; // The global settings used for the whole print job
repeated Extruder extruders = 3; // The settings sent to each extruder object
repeated SettingExtruder limit_to_extruder = 4; // From which stack the setting would inherit if not defined per object
}
message Extruder
{
int32 id = 1;
SettingList settings = 2;
}
message Object
{
message Object {
int64 id = 1;
bytes vertices = 2; //An array of 3 floats.
bytes normals = 3; //An array of 3 floats.
@@ -31,17 +15,29 @@ message Object
repeated Setting settings = 5; // Setting override per object, overruling the global settings.
}
message Progress
{
// typeid 3
message Progress {
float amount = 1;
}
// typeid 2
message SlicedObjectList {
repeated SlicedObject objects = 1;
}
message SlicedObject {
int64 id = 1;
repeated Layer layers = 2;
}
message Layer {
int32 id = 1;
float height = 2; // Z position
float thickness = 3; // height of a single layer
repeated Polygon polygons = 4; // layer data
float height = 2;
float thickness = 3;
repeated Polygon polygons = 4;
}
message Polygon {
@@ -54,71 +50,37 @@ message Polygon {
SkirtType = 5;
InfillType = 6;
SupportInfillType = 7;
MoveCombingType = 8;
MoveRetractionType = 9;
SupportInterfaceType = 10;
}
Type type = 1; // Type of move
bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used)
float line_width = 3; // The width of the line being laid down
Type type = 1;
bytes points = 2;
float line_width = 3;
}
message LayerOptimized {
int32 id = 1;
float height = 2; // Z position
float thickness = 3; // height of a single layer
repeated PathSegment path_segment = 4; // layer data
}
message PathSegment {
int32 extruder = 1; // The extruder used for this path segment
enum PointType {
Point2D = 0;
Point3D = 1;
}
PointType point_type = 2;
bytes points = 3; // The points defining the line segments, bytes of float[2/3] array of length N+1
bytes line_type = 4; // Type of line segment as an unsigned char array of length 1 or N, where N is the number of line segments in this path
bytes line_width = 5; // The widths of the line segments as bytes of a float array of length 1 or N
}
// typeid 4
message GCodeLayer {
int64 id = 1;
bytes data = 2;
}
message PrintTimeMaterialEstimates { // The print time for the whole print and material estimates for the extruder
float time = 1; // Total time estimate
repeated MaterialEstimates materialEstimates = 2; // materialEstimates data
}
message MaterialEstimates {
// typeid 5
message ObjectPrintTime {
int64 id = 1;
float material_amount = 2; // material used in the extruder
float time = 2;
float material_amount = 3;
}
// typeid 6
message SettingList {
repeated Setting settings = 1;
}
message Setting {
string name = 1; // Internal key to signify a setting
string name = 1;
bytes value = 2; // The value of the setting
}
message SettingExtruder {
string name = 1; //The setting key.
int32 extruder = 2; //From which extruder stack the setting should inherit.
bytes value = 2;
}
// typeid 7
message GCodePrefix {
bytes data = 2; //Header string to be prepended before the rest of the g-code sent from the engine.
}
message SlicingFinished {
bytes data = 2;
}
+4 -5
Ver Arquivo
@@ -58,8 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
#LEAVE THIS DIRECTORY EMPTY IF THIS DOCUMENTATION NEEDS TO BE PUBLISHED AUTOMATICALLY TO GITHUB PAGES!
OUTPUT_DIRECTORY = docs
OUTPUT_DIRECTORY = documentation
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@@ -178,7 +177,7 @@ JAVADOC_AUTOBRIEF = NO
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
@@ -832,7 +831,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = docs/assets
IMAGE_PATH = documentation/assets
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -2050,7 +2049,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO
# The default value is: NO.
HAVE_DOT = YES
HAVE_DOT = NO
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
+21 -30
Ver Arquivo
@@ -3,9 +3,9 @@ CuraEngine
The CuraEngine is a C++ console application for 3D printing GCode generation. It has been made as a better and faster alternative to the old Skeinforge engine.
The CuraEngine is pure C++ and uses Clipper from http://www.angusj.com/delphi/clipper.php
Furthermore it depends on libArcus by Ultimaker, which can be found at http://github.com/Ultimaker/libArcus
There are no external dependences and Clipper is included in the source code without modifications.
This is just a console application for GCode generation. For a full graphical application look at https://github.com/Ultimaker/Cura which is the graphical frontend for CuraEngine.
This is just a console application for GCode generation. For a full graphical application look at https://github.com/daid/Cura which is the graphical frontend for CuraEngine.
The CuraEngine can be used seperately or in other applications. Feel free to add it to your application. But please take note of the License.
@@ -19,52 +19,43 @@ But in general it boils down to: You need to share the source of any CuraEngine
How to Install
==============
1. Clone the repository from https://github.com/Ultimaker/CuraEngine.git (the URL at the right hand side of this page).
2. Install Protobuf >= 3.0.0 (see below)
2. Install Protobuf (see below)
3. Install libArcus (see https://github.com/Ultimaker/libArcus)
In order to compile CuraEngine, either use CMake or start a project in your preferred IDE.
CMake compilation:
1. Navigate to the CuraEngine directory and execute the following commands
2. ```$ mkdir build && cd build```
3. ```$ cmake ..```
4. ```$ make```
2. $ mkdir build && cd build
3. $ cmake ..
4. $ make
Project files generation:
1. Navigate to the CuraEngine directory and execute the following commands
2. ```cmake . -G "CodeBlocks - Unix Makefiles"```
2. cmake . -G "CodeBlocks - Unix Makefiles"
3. (for a list of supported IDE's see http://www.cmake.org/Wiki/CMake_Generator_Specific_Information#Code::Blocks_Generator)
Installing Protobuf
-------------------
1. Be sure to have libtool installed.
2. Download protobuf from https://github.com/google/protobuf/releases (download ZIP and unZIP at desired location, or clone the repo). The protocol buffer is used for communication between the CuraEngine and the GUI.
3. Run ```autogen.sh``` from the protobuf directory:
```$ ./autogen.sh```
4. ```$ ./configure```
5. ```$ make```
6. ```# make install```
(Please note the ```#```. It indicates the need of superuser, as known as root, priviliges.)
7. (In case the shared library cannot be loaded, you can try ```sudo ldconfig``` on Linux systems)
2. Download protobuf from https://github.com/google/protobuf/ (download ZIP and unZIP at desired location, or clone the repo) The protocol buffer is used for communication between the CuraEngine and the GUI.
3. Before installing protobuf, change autogen.sh : comment line 18 to line 38 using '#'s. This removes the dependency on gtest-1.7.0.
4. Run autogen.sh from the protobuf directory:
$ ./autogen.sh
5. $ ./configure
6. $ make
7. $ make install # Requires superused priviliges.
8. (In case the shared library cannot be loaded, you can try "sudo ldconfig" on Linux systems)
Running
=======
Other than running CuraEngine from a frontend, such as Ultimaker/Cura, one can run CuraEngine from the command line.
For that one needs a settings JSON file, which can be found in the Ultimaker/Cura repository.
Note that the structure of the json files has changed since 2.1. In the corresponding branch of the Cura repository you can find how the json files used to be structured.
Other than running CuraEngine from a frontend, such as PluggableCura, one can run CuraEngine from the command line.
An example run for an UM2 machine looks as follows:
* Navigate to the CuraEngine directory and execute the following
```
./build/CuraEngine slice -v -j ../Cura/resources/definitions/dual_extrusion_printer.def.json -o "output/test.gcode" -e1 -s infill_line_distance=0 -e0 -l "/model_1.stl" -e1 -l "fully_filled_model.stl"
```
Run `CuraEngine help` for a general description of how to use the CuraEngine tool.
[Set the environment variable](https://help.ubuntu.com/community/EnvironmentVariables) CURA_ENGINE_SEARCH_PATH to the appropriate paths, delimited by a colon e.g.
```
CURA_ENGINE_SEARCH_PATH=/path/to/Cura/resources/definitions:/user/defined/path
./build/CuraEngine -v -j fdmprinter.json -s machine_start_gcode=";FLAVOR:UltiGCode
;TIME:10000000
;MATERIAL:2000
;MATERIAL2:0" -s mesh_position_x=115.0 -s mesh_position_y=112.5 -s mesh_position_z=0 -s material_diameter=1.128 -o "output/test.gcode" "/path/to/model.stl"
```
Internals
@@ -128,4 +119,4 @@ The GCode generation is quite a large bit of code. As a lot is going on here. Im
* PathOrderOptimizer: This piece of code needs to solve a TravelingSalesmanProblem. Given a list of polygons/lines it tries to find the best order in which to print them. It currently does this by finding the closest next polygon to print.
* Infill: This code generates a group of lines from an area. This is the code that generates the actuall infill pattern. There is also a concentric infill function, which is currently not used.
* Comb: The combing code is the code that tries to avoid holes when moving the head around without printing. This code also detects when it fails. The final GCode generator uses the combing code while generating the final GCode. So they interact closely.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
* GCodeExport: The GCode export is a 2 step process. First it collects all the paths for a layer that it needs to print, this includes all moves, prints, extrusion widths. And then it generates the final GCode. This is the only piece of code that has knowledge about GCode keywords and syntax;meshmdhfdhfdhf to generate a different flavor of GCode it will be the only piece that needs adjustment. All volumatric calculations also happen here.
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 70 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 20 KiB

+1
Ver Arquivo
@@ -0,0 +1 @@
html/index.html

Antes

Largura:  |  Altura:  |  Tamanho: 18 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 18 KiB

+126
Ver Arquivo
@@ -0,0 +1,126 @@
Code Conventions
=======
Note that the code convention described here have not all yet been fully implemented.
Bracketing and indenting
-----
~~~~~~~~~~~~~~~{.cpp}
if (condition) // brackets always on new lines
{ // allways a bracket after an if, for, while, etc.
// indent always with 4 spaces, never with tabs
}
else // else on new line
{
// more code
}
~~~~~~~~~~~~~~~
Naming conventions
------
* variables: lower_case_with_underscores
* functions: loweCamelCase
* classes: UpperCamelCase
* macros: UPPER_CASE_WITH_UNDERSCORES
~~~~~~~~~~~~~~~{.cpp}
#define UPPER_CASE_MACRO 1
class UpperCamelCase
{
private:
MemberVariableObject with_underscores;
public:
MemberVariableObject with_underscores;
public:
UpperCamelCase();
~UpperCamelCase();
// start with input variable(s) and end with output variable(s)
void lowerCamelCaseFunctions(ParamObject& also_with_underscores)
{
LocalObject under_scores;
}
private:
void putFunctionsAndVariablesInSeperatePublicPrivateBlocks();
};
~~~~~~~~~~~~~~~
Ordering
----
~~~~~~~~~~~~~~~{.cpp}
class Example
{
// start with input variable(s) and end with output parameter(s)
void function1(ParamObject& input_variable, int setting_parameter, ParamObject2& return_parameter)
{
function2();
function3();
}
// place functions called solely by one other function below it chronologically
void function2();
void function3();
};
~~~~~~~~~~~~~~~
Documentation
----
We use [Doxygen](www.doxygen.org/) to generate documentation. Try to keep your documentation in doxygen style.
Here's a small example:
~~~~~~~~~~~~~~~{.cpp}
/ *!
* Doxygen style comments!
*
* \param param1 explanation may refer to another \p param2
* /
void function(int param1, int param2)
{
// non-doxygen style comments on implementation details
}
int member; //!< inline doxygen comment on the entry to the left
~~~~~~~~~~~~~~~
Files
--------
For a file Foo.h (UpperCamelCase):
~~~~~~~~~~~~~~~{.cpp}
#ifndef FOO_H
#define FOO_H
// [content]
#endif//FOO_H
~~~~~~~~~~~~~~~
Other
----
~~~~~~~~~~~~~~~{.cpp}
#include <all>
#include <includes>
#include <on>
#include <top>
#include <first_system_includes>
#include <then_library_includes>
#include "finally_local_includes"
enum class EnumExample
{
ELEM0 = 0,
ELEM1 = 1
}
~~~~~~~~~~~~~~~
Illegal syntax
----
~~~~~~~~~~~~~~~{.cpp}
void function()
{
if (condition)
single_line_outside_code_block(); // always use braces!
}; // unneccesary semicolon after function definition is not allowed
~~~~~~~~~~~~~~~
@@ -4,7 +4,6 @@ Glossary
Term/Synonyms | Meaning
--- | ---
Extruder Train | The whole of a feeder, bowden tube and a nozzle
Island/Part | isolated/unconnected part in 2D slice
Inset | perimeter, the perimeters which are laid down around the infill
Slicing | The act of extracting the contours of the object at a certain height (not the whole process which would also include gcode generation etc.)
+1 -1
Ver Arquivo
@@ -7,4 +7,4 @@ This is the documentation for CuraEngine, the back-end slicer of Cura.
[Glossary](documentation/glossary.md)
[Code Conventions](https://github.com/Ultimaker/Meta/blob/master/code_conventions.md)
[Code Conventions](documentation/code_conventions.md)
+1325
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+21 -19
Ver Arquivo
@@ -1,24 +1,26 @@
The Clipper Library (including Delphi, C++ & C# source code, other accompanying
code, examples and documentation), hereafter called "the Software", has been
released under the following license, terms and conditions:
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the Software covered by this license to use, reproduce,
display, distribute, execute, and transmit the Software, and to prepare
derivative works of the Software, and to permit third-parties to whom the
Software is furnished to do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
The copyright notices in the Software and this entire statement, including the
above license grant, this restriction and the following disclaimer, must be
included in all copies of the Software, in whole or in part, and all derivative
works of the Software, unless such copies or derivative works are solely in the
form of machine-executable object code generated by a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+3 -34
Ver Arquivo
@@ -1,39 +1,8 @@
=====================================================================
Clipper Change Log
=====================================================================
v6.2.1 (31 October 2014) Rev 482
* Bugfix in ClipperOffset.Execute where the Polytree.IsHole property
was returning incorrect values with negative offsets
* Very minor improvement to join rounding in ClipperOffset
* Fixed CPP OpenGL demo.
v6.2.0 (17 October 2014) Rev 477
* Numerous minor bugfixes, too many to list.
(See revisions 454-475 in Sourceforge Repository)
* The ZFillFunction (custom callback function) has had its parameters
changed.
* Curves demo removed (temporarily).
* Deprecated functions have been removed.
v6.1.5 (26 February 2014) Rev 460
* Improved the joining of output polygons sharing a common edge
when those common edges are horizontal.
* Fixed a bug in ClipperOffset.AddPath() which would produce
incorrect solutions when open paths were added before closed paths.
* Minor code tidy and performance improvement
v6.1.4 (6 February 2014)
* Fixed bugs in MinkowskiSum
* Fixed minor bug when using Clipper.ForceSimplify.
* Modified use_xyz callback so that all 4 vertices around an
intersection point are now passed to the callback function.
v6.1.3a (22 January 2014) Rev 453
* Fixed buggy PointInPolygon function (C++ and C# only).
Note this bug only affected the newly exported function, the
internal PointInPolygon function used by Clipper was OK.
v6.1.3 (19 January 2014) Rev 452
v6.1.3 (19 January 2014)
* Fixed potential endless loop condition when adding open
paths to Clipper.
* Fixed missing implementation of SimplifyPolygon function
@@ -44,11 +13,11 @@ v6.1.3 (19 January 2014) Rev 452
* Overloaded MinkowskiSum function to accommodate multi-contour
paths.
v6.1.2 (15 December 2013) Rev 444
v6.1.2 (15 December 2013)
* Fixed broken C++ header file.
* Minor improvement to joining polygons.
v6.1.1 (13 December 2013) Rev 441
v6.1.1 (13 December 2013)
* Fixed a couple of bugs affecting open paths that could
raise unhandled exceptions.
+535 -389
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+36 -33
Ver Arquivo
@@ -1,8 +1,8 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.2.1 *
* Date : 31 October 2014 *
* Version : 6.1.3a *
* Date : 22 January 2014 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2014 *
* *
@@ -34,7 +34,7 @@
#ifndef clipper_hpp
#define clipper_hpp
#define CLIPPER_VERSION "6.2.0"
#define CLIPPER_VERSION "6.1.3"
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
//improve performance but coordinate values are limited to the range +/- 46340
@@ -44,10 +44,11 @@
//#define use_xyz
//use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines
//#define use_lines
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
//use_deprecated: Enables support for the obsolete OffsetPaths() function
//which has been replace with the ClipperOffset class.
#define use_deprecated
#include <vector>
#include <set>
@@ -56,7 +57,6 @@
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
namespace ClipperLib {
@@ -69,16 +69,11 @@ enum PolyType { ptSubject, ptClip };
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
#ifdef use_int32
typedef int cInt;
static cInt const loRange = 0x7FFF;
static cInt const hiRange = 0x7FFF;
typedef int cInt;
typedef unsigned int cUInt;
#else
typedef signed long long cInt;
static cInt const loRange = 0x3FFFFFFF;
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
typedef signed long long long64; //used by Int128 class
typedef unsigned long long ulong64;
typedef signed long long cInt;
typedef unsigned long long cUInt;
#endif
struct IntPoint {
@@ -122,12 +117,15 @@ struct DoublePoint
//------------------------------------------------------------------------------
#ifdef use_xyz
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
#ifdef use_deprecated
enum EndType_ {etClosed, etButt = 2, etSquare, etRound};
#endif
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
@@ -136,7 +134,6 @@ class PolyNode
{
public:
PolyNode();
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
@@ -171,6 +168,11 @@ bool Orientation(const Path &poly);
double Area(const Path &poly);
int PointInPolygon(const IntPoint &pt, const Path &path);
#ifdef use_deprecated
void OffsetPaths(const Paths &in_polys, Paths &out_polys,
double delta, JoinType jointype, EndType_ endtype, double limit = 0);
#endif
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
@@ -181,7 +183,8 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths,
Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
@@ -199,7 +202,7 @@ enum EdgeSide { esLeft = 1, esRight = 2};
//forward declarations (for stuff used internally) ...
struct TEdge;
struct IntersectNode;
struct LocalMinimum;
struct LocalMinima;
struct Scanbeam;
struct OutPt;
struct OutRec;
@@ -210,6 +213,7 @@ typedef std::vector < TEdge* > EdgeList;
typedef std::vector < Join* > JoinList;
typedef std::vector < IntersectNode* > IntersectList;
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
@@ -232,14 +236,12 @@ protected:
void PopLocalMinima();
virtual void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
void InsertLocalMinima(LocalMinima *newLm);
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
typedef std::vector<LocalMinimum> MinimaList;
MinimaList::iterator m_CurrentLM;
MinimaList m_MinimaList;
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
bool m_PreserveCollinear;
@@ -266,7 +268,7 @@ public:
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz
void ZFillFunction(ZFillCallback zFillFunc);
void ZFillFunction(TZFillCallback zFillFunc);
#endif
protected:
void Reset();
@@ -277,8 +279,7 @@ private:
JoinList m_GhostJoins;
IntersectList m_IntersectList;
ClipType m_ClipType;
typedef std::priority_queue<cInt> ScanbeamList;
ScanbeamList m_Scanbeam;
std::set< cInt, std::greater<cInt> > m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
bool m_ExecuteLocked;
@@ -288,7 +289,7 @@ private:
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef use_xyz
ZFillCallback m_ZFill; //custom callback
TZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
@@ -307,19 +308,21 @@ private:
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
void ProcessHorizontals(bool IsTopOfScanbeam);
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, bool protect = false);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllOutRecs();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
bool ProcessIntersections(const cInt botY, const cInt topY);
void BuildIntersectList(const cInt botY, const cInt topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
@@ -341,7 +344,7 @@ private:
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef use_xyz
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
void SetZ(IntPoint& pt, TEdge& e);
#endif
};
//------------------------------------------------------------------------------
-57
Ver Arquivo
@@ -1,57 +0,0 @@
Tencent is pleased to support the open source community by making RapidJSON available.
Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License.
If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON.
A copy of the MIT License is included in this file.
Other dependencies and licenses:
Open Source Software Licensed Under the BSD License:
--------------------------------------------------------------------
The msinttypes r29
Copyright (c) 2006-2013 Alexander Chemeris
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Open Source Software Licensed Under the JSON License:
--------------------------------------------------------------------
json.org
Copyright (c) 2002 JSON.org
All Rights Reserved.
JSON_checker
Copyright (c) 2002 JSON.org
All Rights Reserved.
Terms of the JSON License:
---------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Terms of the MIT License:
--------------------------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-203
Ver Arquivo
@@ -1,203 +0,0 @@
This file is a conversion from the ENGINE settings of 15.04 to the ENGINE setting of 2.0
This is NOT a conversion on the frontend internal setting names (15.04 has the dictionary of doom)
autoCenter ? ==> center_object OR machine_center_is_zero ??
coolHeadLift ==> cool_lift_head
downSkinCount ==> bottom_layers
enableCombing ==> retraction_combing
enableOozeShield ==> ooze_shield_enabled
endCode ==> machine_end_gcode
extruderOffset[MAX_EXTRUDERS] = (machine_nozzle_offset_x, machine_nozzle_offset_y)
extrusionWidth ==> infill_line_width, skirt_line_width, support_line_width
fanFullOnLayerNr ==> cool_fan_full_layer
fanSpeedMax ==> cool_fan_speed_max
fanSpeedMin ==> cool_fan_speed_min
filamentDiameter ==> material_diameter
filamentFlow ==> material_flow
fixHorrible ==> meshfix_union_all AND/OR meshfix_union_all_remove_holes AND/OR meshfix_extensive_stitching AND/OR magic_mesh_surface_mode
gcodeFlavor ==> machine_gcode_flavor
infillOverlap ==> infill_overlap
infillPattern ==> infill_pattern
infillSpeed ==> speed_infill
initialLayerSpeed ==> speed_layer_0
initialLayerThickness ==> layer_height_0
initialSpeedupLayers ==> speed_slowdown_layers
inset0Speed ==> speed_wall_0
insetCount ==> wall_line_count
insetXSpeed ==> speed_wall_x
layer0extrusionWidth [ Doesn't exist anymore ]
layerThickness ==> layer_height
matrix [ Doesn't exist anymore ]
minimalExtrusionBeforeRetraction
minimalFeedrate ==> cool_min_speed
minimalLayerTime ==> cool_min_layer_time
moveSpeed ==> speed_travel
multiVolumeOverlap ==> multiple_mesh_overlap
nozzleSize ==> machine_nozzle_size
objectPosition ==> mesh_position_x, mesh_position_y, mesh_position_z
objectSink [ Doesn't exist in CuraEngine anymore ]
perimeterBeforeInfill = not(infill_before_walls)
postSwitchExtruderCode ==> machine_extruder_start_code
preSwitchExtruderCode ==> machine_extruder_end_code
printSpeed ==> speed_prime_tower, speed_support_lines, speed_support_roof, skirt_speed
raftAirGap ==> raft_airgap
raftAirGapLayer0 ?!?!?
raftBaseLinewidth ==> raft_base_line_width
raftBaseSpeed ==> raft_interface_speed, raft_base_speed
raftBaseThickness ==> raft_base_thickness
raftFanSpeed ==> raft_base_fan_speed, raft_interface_fan_speed, raft_surface_fan_speed
raftInterfaceLineSpacing==> raft_interface_line_spacing
raftInterfaceLinewidth ==> raft_interface_line_width
raftInterfaceThickness ==> raft_interface_thickness
raftLineSpacing ==> raft_base_line_spacing
raftMargin ==> raft_margin
raftSurfaceLayers ==> raft_surface_layers
raftSurfaceLineSpacing ==> raft_surface_line_spacing
raftSurfaceLinewidth ==> raft_surface_line_width
raftSurfaceSpeed ==> raft_surface_speed
raftSurfaceThickness ==> raft_surface_thickness
retractionAmount ==> retraction_amount (set retraction_enable = true)
retractionAmountExtruderSwitch ==> switch_extruder_retraction_amount
retractionAmountPrime ==> retraction_extra_prime_amount
retractionMinimalDistance ==> retraction_extrusion_window ( set retraction_count_max = 1 )
retractionSpeed ==> retraction_retract_speed (, retraction_prime_speed ?), switch_extruder_retraction_speed
retractionZHop ==> retraction_hop
simpleMode ??!
skinSpeed ==> speed_topbottom
skirtDistance ==> skirt_gap
skirtLineCount ==> brim_line_count, skirt_line_count
skirtMinLength ==> skirt_minimal_length
sparseInfillLineDistance ==> infill_line_distance
spiralizeMode ==> magic_spiralize
startCode ==> machine_start_gcode
supportAngle ==> support_angle, support_enable=true if support_angle>0
supportEverywhere ==> support_type
supportExtruder ==> support_extruder_nr, support_extruder_nr_layer_0
supportLineDistance ==> support_line_distance
supportType ==> support_pattern
supportXYDistance ==> support_xy_distance
supportZDistance ==> support_z_distance
upSkinCount ==> top_layers
wipeTowerSize ==> prime_tower_size
NEW:
adhesion_extruder_nr
adhesion_type
alternate_extra_perimeter
coasting_enable
coasting_min_volume_move
coasting_min_volume_retract
coasting_speed_move
coasting_speed_retract
coasting_volume_move
coasting_volume_retract
cool_min_layer_time_fan_speed_max
draft_shield_dist
draft_shield_height
extruder_nr
fill_perimeter_gaps
infill_sparse_thickness
infill_wipe_dist
machine_depth
machine_extruder_count
machine_extruder_end_pos_abs
machine_extruder_end_pos_x
machine_extruder_end_pos_y
machine_extruder_start_pos_abs
machine_extruder_start_pos_x
machine_extruder_start_pos_y
machine_heated_bed
machine_nozzle_cool_down_speed
machine_nozzle_expansion_angle
machine_nozzle_head_distance
machine_nozzle_heat_up_speed
machine_nozzle_tip_outer_diameter
machine_print_temp_wait
machine_use_extruder_offset_to_offset_coords
machine_width
magic_fuzzy_skin_enabled
magic_fuzzy_skin_point_dist
magic_fuzzy_skin_thickness
material_bed_temperature
material_bed_temp_prepend
material_bed_temp_wait
material_extrusion_cool_down_speed
material_flow_dependent_temperature
material_flow_temp_graph
material_print_temperature
material_print_temp_prepend
material_print_temp_wait
material_standby_temperature
meshfix_keep_open_polygons
ooze_shield_angle
ooze_shield_dist
prime_tower_dir_outward
prime_tower_distance
prime_tower_flow
prime_tower_line_width
prime_tower_position_x
prime_tower_position_y
prime_tower_wipe_enabled
remove_overlapping_walls_0_enabled
remove_overlapping_walls_x_enabled
retraction_min_travel
skin_alternate_rotation
skin_line_width
skin_no_small_gaps_heuristic
skin_outline_count
support_area_smoothing
support_bottom_distance
support_bottom_stair_step_height
support_conical_angle
support_conical_enabled
support_conical_min_width
support_connect_zigzags
support_join_distance
support_minimal_diameter
support_offset
support_roof_enable
support_roof_extruder_nr
support_roof_height
support_roof_line_distance
support_roof_line_width
support_roof_pattern
support_top_distance
support_tower_diameter
support_tower_roof_angle
switch_extruder_prime_speed
top_bottom_pattern
travel_avoid_distance
travel_avoid_other_parts
travel_compensate_overlapping_walls_enabled
wall_line_width_0
wall_line_width_x
wireframe_bottom_delay
wireframe_drag_along
wireframe_enabled
wireframe_fall_down
wireframe_flat_delay
wireframe_flow_connection
wireframe_flow_flat
wireframe_height
wireframe_nozzle_clearance
wireframe_printspeed_bottom
wireframe_printspeed_down
wireframe_printspeed_flat
wireframe_printspeed_up
wireframe_roof_drag_along
wireframe_roof_fall_down
wireframe_roof_inset
wireframe_roof_outer_delay
wireframe_straight_before_down
wireframe_strategy
wireframe_top_delay
wireframe_top_jump
wireframe_up_half_speed
xy_offset
z_seam_type
-19
Ver Arquivo
@@ -1,19 +0,0 @@
find engine setting literals
cd ~/Development/CuraEngine/output/reflection/
~/bin/substitute.pl y 'while(/getSetting\w+\("(\w+)"\)/gsm) { print "$1\n"; }' ../../src/ | sort | uniq > engineSettingLiterals.txt
run setting inheritance reflection
cd ~/Development/CuraEngine
./build/CuraEngine analyse ../Cura/resources/definitions/fdmprinter.def.json meta/refl_ff.gv output/reflection/engineSettingLiterals.txt -piew
dot meta/refl_ff.gv -Tpng > meta/rafl_ff_dotted.png
green block = used in engine
red edge = inherit function only
black edge = parent-child relation
-130
Ver Arquivo
@@ -1,130 +0,0 @@
[ problem ]
gantry_height introduced by Jaime while there already was machine_nozzle_gantry_distance
[ RENAMES ]
raft_base_linewidth > raft_base_line_width
raft_interface_linewidth > raft_interface_line_width
fill_overlap > infill_overlap
fill_pattern > infill_pattern
fill_sparse_combine > infill_sparse_combine
fill_sparse_density > infill_sparse_density
fill_sparse_thickness > infill_sparse_thickness
support_fill_rate > support_infill_rate
[ SPLITS ]
raft_line_spacing > raft_base_line_spacing (, raft_interface_line_spacing, raft_surface_line_spacing)
wall_overlap_avoid_enabled > remove_overlapping_walls_enabled (, remove_overlapping_walls_0_enabled, remove_overlapping_walls_x_enabled)
retraction_minimal_extrusion > retraction_extrusion_window (+ retraction_count_max = 1)
magic_mesh_surface_mode : false >> "Normal"
magic_mesh_surface_mode : true >> "Surface"
[ NEW ]
alternate_extra_perimeter
coasting_enable
coasting_min_volume
coasting_min_volume_move
coasting_min_volume_retract
coasting_speed
coasting_speed_move
coasting_speed_retract
coasting_volume
coasting_volume_move
coasting_volume_retract
fill_perimeter_gaps
draft_shield_dist
draft_shield_enabled
draft_shield_height
draft_shield_height_limitation
infill_wipe_dist
line_width (was wall_line_width)
machine_extruder_count
machine_head_polygon
machine_head_with_fans_polygon
machine_heat_zone_length
magic_mesh_surface_mode
meshfix_extensive_stitching
meshfix_keep_open_polygons
meshfix_union_all
meshfix_union_all_remove_holes
print_sequence
raft_base_line_spacing (from raft_line_spacing)
raft_base_line_width
raft_fan_speed
raft_interface_fan_speed
raft_interface_line_spacing
raft_interface_speed
raft_speed
raft_surface_fan_speed
raft_surface_line_spacing
raft_surface_line_width
raft_surface_speed
raft_surface_thickness
remove_overlapping_walls_0_enabled
remove_overlapping_walls_enabled
remove_overlapping_walls_x_enabled
retraction_count_max
retraction_extrusion_window (from retraction_minimal_extrusion)
retraction_extra_prime_amount
skin_alternate_rotation
speed_support_lines
speed_support_roof
support_conical_angle
support_conical_enabled
support_conical_min_width
support_offset
support_roof_enable
support_roof_height
support_roof_line_width
travel_avoid_distance
travel_avoid_other_parts
travel_compensate_overlapping_walls_enabled
z_seam_type
[ DUAL EXTRUSION ]
extruder_nr
machine_use_extruder_offset_to_offset_coords
machine_nozzle_offset_x
machine_nozzle_offset_y
machine_extruder_start_code
machine_extruder_start_pos_abs
machine_extruder_start_pos_x
machine_extruder_start_pos_y
machine_extruder_end_pos_abs
machine_extruder_end_pos_x
machine_extruder_end_pos_y
machine_extruder_end_code
prime_tower_enable
prime_tower_size
prime_tower_position_x
prime_tower_position_y
prime_tower_flow
prime_tower_wipe_enabled
ooze_shield_enabled
ooze_shield_angle
ooze_shield_dist
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 284 KiB

-35
Ver Arquivo
@@ -1,35 +0,0 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#include "ConicalOverhang.h"
namespace cura {
void ConicalOverhang::apply(Slicer* slicer, double angle, int layer_thickness)
{
double tanAngle = tan(angle); // the XY-component of the angle
int max_dist_from_lower_layer = tanAngle * layer_thickness; // max dist which can be bridged
for (unsigned int layer_nr = slicer->layers.size() - 2; static_cast<int>(layer_nr) >= 0; layer_nr--)
{
SlicerLayer& layer = slicer->layers[layer_nr];
SlicerLayer& layer_above = slicer->layers[layer_nr + 1];
if (std::abs(max_dist_from_lower_layer) < 5)
{ // magically nothing happens when max_dist_from_lower_layer == 0
// below magic code solves that
int safe_dist = 20;
Polygons diff = layer_above.polygons.difference(layer.polygons.offset(-safe_dist));
layer.polygons = layer.polygons.unionPolygons(diff);
layer.polygons = layer.polygons.smooth(safe_dist);
layer.polygons.simplify(safe_dist, safe_dist * safe_dist / 4);
// somehow layer.polygons get really jagged lines with a lot of vertices
// without the above steps slicing goes really slow
}
else
{
layer.polygons = layer.polygons.unionPolygons(layer_above.polygons.offset(-max_dist_from_lower_layer));
}
}
}
}//namespace cura
-30
Ver Arquivo
@@ -1,30 +0,0 @@
/** Copyright (C) 2016 Tim Kuipers - Released under terms of the AGPLv3 License */
#ifndef CONICAL_OVERHANG_H
#define CONICAL_OVERHANG_H
#include "slicer.h"
namespace cura {
/*!
* A class for changing the geometry of a model such that it is printable without support -
* Or at least with at least support as possible
*/
class ConicalOverhang
{
public:
/*!
* Change the slice data such that the model becomes more printable
*
* \param[in,out] slicer The slice data
* \param angle The maximum angle which can be printed without generating support (or at least generating least support)
* \param layer_thickness The general layer thickness
*/
static void apply(Slicer* slicer, double angle, int layer_thickness);
};
}//namespace cura
#endif // CONICAL_OVERHANG_H
-27
Ver Arquivo
@@ -1,27 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ExtruderTrain.h"
namespace cura
{
int ExtruderTrain::getExtruderNr()
{
return extruder_nr;
}
ExtruderTrain::ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr)
: SettingsBase(settings)
, extruder_nr(extruder_nr)
{
}
bool ExtruderTrain::getIsUsed() const
{
return is_used;
}
void ExtruderTrain::setIsUsed(bool used)
{
is_used = used;
}
}//namespace cura
-25
Ver Arquivo
@@ -1,25 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef EXTRUDER_TRAIN_H
#define EXTRUDER_TRAIN_H
#include "settings/settings.h"
namespace cura
{
class ExtruderTrain : public SettingsBase
{
int extruder_nr;
bool is_used = false; //!< whether this extruder train is (probably) used during printing the current meshgroup
public:
int getExtruderNr();
bool getIsUsed() const; //!< return whether this extruder train is (probably) used during printing the current meshgroup
void setIsUsed(bool used); //!< set whether this extruder train is (probably) used during printing the current meshgroup
ExtruderTrain(SettingsBaseVirtual* settings, int extruder_nr);
};
}//namespace cura
#endif // EXTRUDER_TRAIN_H
-23
Ver Arquivo
@@ -1,23 +0,0 @@
#ifndef FAN_SPEED_LAYER_TIME_H
#define FAN_SPEED_LAYER_TIME_H
#include "settings/settings.h"
namespace cura
{
struct FanSpeedLayerTimeSettings
{
public:
double cool_min_layer_time;
double cool_min_layer_time_fan_speed_max;
double cool_fan_speed_0;
double cool_fan_speed_min;
double cool_fan_speed_max;
double cool_min_speed;
int cool_fan_full_layer;
};
} // namespace cura
#endif // FAN_SPEED_LAYER_TIME_H
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-446
Ver Arquivo
@@ -1,446 +0,0 @@
#ifndef GCODE_WRITER_H
#define GCODE_WRITER_H
#include <fstream>
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/NoCopy.h"
#include "utils/polygonUtils.h"
#include "sliceDataStorage.h"
#include "raft.h"
#include "infill.h"
#include "bridge.h"
#include "pathOrderOptimizer.h"
#include "gcodePlanner.h"
#include "gcodeExport.h"
#include "commandSocket.h"
#include "PrimeTower.h"
#include "FanSpeedLayerTime.h"
#include "PrintFeature.h"
#include "LayerPlanBuffer.h"
namespace cura
{
/*!
* Secondary stage in Fused Filament Fabrication processing: The generated polygons are used in the gcode generation.
* Some polygons in the SliceDataStorage signify areas which are to be filled with parallel lines,
* while other polygons signify the contours which should be printed.
*
* The main function of this class is FffGcodeWriter::writeGCode().
*/
class FffGcodeWriter : public SettingsMessenger, NoCopy
{
friend class FffProcessor; // cause WireFrame2Gcode uses the member [gcode] (TODO)
private:
int max_object_height; //!< The maximal height of all previously sliced meshgroups, used to avoid collision when moving to the next meshgroup to print.
/*
* Buffer for all layer plans (of type GCodePlanner)
*
* The layer plans are buffered so that we can start heating up a nozzle several layers before it needs to be used.
* Another reason is to perform Auto Temperature.
*/
LayerPlanBuffer layer_plan_buffer;
/*!
* The class holding the current state of the gcode being written.
*
* It holds information such as the last written position etc.
*/
GCodeExport gcode;
/*!
* The gcode file to write to when using CuraEngine as command line tool.
*/
std::ofstream output_file;
/*!
* Whether the skirt or brim polygons have been processed into planned paths
* for each extruder train.
*/
bool skirt_brim_is_processed[MAX_EXTRUDERS];
std::vector<std::vector<unsigned int>> mesh_order_per_extruder; //!< For each extruder, the cyclic order of the meshes (the first element is not the starting element per se)
/*!
* For each extruder whether priming has already been planned
*/
bool extruder_prime_is_planned[MAX_EXTRUDERS];
std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings_per_extruder; //!< The settings used relating to minimal layer time and fan speeds. Configured for each extruder.
Point last_position_planned; //!< The position of the head before planning the next layer
int current_extruder_planned; //!< The extruder train in use before planning the next layer
bool is_inside_mesh_layer_part; //!< Whether the last position was inside a layer part (used in combing)
public:
FffGcodeWriter(SettingsBase* settings_)
: SettingsMessenger(settings_)
, max_object_height(0)
, layer_plan_buffer(this, gcode)
, extruder_prime_is_planned {} // initialize all values in array with [false]
, last_position_planned(no_point)
, current_extruder_planned(0) // changed somewhere early in FffGcodeWriter::writeGCode
, is_inside_mesh_layer_part(false)
{
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
output_file.open(filename);
if (output_file.is_open())
{
gcode.setOutputStream(&output_file);
return true;
}
return false;
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
gcode.setOutputStream(stream);
}
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode.getTotalPrintTime();
}
/*!
* Write all the gcode for the current meshgroup.
* This is the primary function of this class.
*
* \param[in] storage The data storage from which to get the polygons to print and the areas to fill.
* \param timeKeeper The stop watch to see how long it takes for each of the stages in the slicing process.
*/
void writeGCode(SliceDataStorage& storage, TimeKeeper& timeKeeper);
private:
/*!
* Set the FffGcodeWriter::fan_speed_layer_time_settings by retrieving all settings from the global/per-meshgroup settings.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigFanSpeedLayerTime(SliceDataStorage& storage);
/*!
* Create and set the SliceDataStorage::coasting_config for each extruder.
*
* \param[out] storage The data storage to which to save the configuration
*/
void setConfigCoasting(SliceDataStorage& storage);
/*!
* Set the retraction config globally, per extruder and per mesh.
*
* \param[out] storage The data storage to which to save the configurations
*/
void setConfigRetraction(SliceDataStorage& storage);
/*!
* Initialize the GcodePathConfig config parameters which don't change over
* all layers, for each feature.
*
* The features are: skirt or brim, support and for each mesh: outer wall,
* inner walls, skin, infill (and combined infill).
*
* \param[out] storage The data storage to which to save the configurations.
*/
void initConfigs(SliceDataStorage& storage);
/*!
* Get the extruder with which to start the print.
*
* Generally this is the adhesion_extruder_nr, but in case the platform adhesion type is none,
* the extruder with lowest number which is used on the first layer is used as initial extruder.
*
* \param[in] storage where to get settings from.
*/
unsigned int getStartExtruder(SliceDataStorage& storage);
/*!
* Set temperatures and perform initial priming.
*
* Write a stub header if CuraEngine is in command line tool mode. (Cause writing the header afterwards would entail moving all gcode down.)
*
* \param[in] storage where the slice data is stored.
* \param[in] start_extruder_nr The extruder with which to start the print.
*/
void processStartingCode(SliceDataStorage& storage, const unsigned int start_extruder_nr);
/*!
* Move up and over the already printed meshgroups to print the next meshgroup.
*
* \param[in] storage where the slice data is stored.
*/
void processNextMeshGroupCode(SliceDataStorage& storage);
/*!
* Add raft layer plans onto the FffGcodeWriter::layer_plan_buffer
*
* \param[in,out] storage where the slice data is stored.
* \param total_layers The total number of layers.
*/
void processRaft(SliceDataStorage& storage, unsigned int total_layers);
/*!
* Convert the polygon data of a layer into a layer plan on the FffGcodeWriter::layer_plan_buffer
*
* In case of negative layer numbers, create layers only containing the data from
* the helper parts (support etc) to fill up the gap between the raft and the model.
*
* \param[in] storage where the slice data is stored.
* \param layer_nr The index of the layer to write the gcode of.
* \param total_layers The total number of layers.
*/
void processLayer(SliceDataStorage& storage, int layer_nr, unsigned int total_layers);
/*!
* Whether the extruders need to be primed separately just before they are used.
*
* \return whether the extruders need to be primed separately just before they are used
*/
bool getExtrudersNeedPrimeDuringFirstLayer();
/*!
* Plan priming of all used extruders which haven't been primed yet
* \param[in] storage where the slice data is stored.
* \param layer_plan The initial planning of the g-code of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void ensureAllExtrudersArePrimed(SliceDataStorage& storage, GCodePlanner& layer_plan, const int layer_nr);
/*!
* Add the skirt or the brim to the layer plan \p gcodeLayer.
*
* \param Storage where the slice data is stored.
* \param gcodeLayer The initial planning of the g-code of the layer.
* \param extruder_nr The extruder train for which to process the skirt or
* brim.
*/
void processSkirtBrim(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int extruder_nr);
/*!
* Adds the ooze shield to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processOozeShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Adds the draft protection screen to the layer plan \p gcodeLayer.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*/
void processDraftShield(SliceDataStorage& storage, GCodePlanner& gcodeLayer, unsigned int layer_nr);
/*!
* Calculate in which order to plan the extruders
*
* \param[in] storage where the slice data is stored.
* \param current_extruder The current extruder with which we last printed
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<int> calculateExtruderOrder(SliceDataStorage& storage, int current_extruder);
/*!
* Calculate in which order to plan the meshes of a specific extruder
*
* \param[in] storage where the slice data is stored.
* \param extruder_nr The extruder for which to determine the order
* \return A vector of pairs of extruder numbers coupled with the mesh indices ordered on print order for that extruder.
*/
std::vector<unsigned int> calculateMeshOrder(SliceDataStorage& storage, int extruder_nr);
/*!
* Add a single layer from a single mesh-volume to the layer plan \p gcodeLayer in mesh surface mode.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshLayerToGCode_meshSurfaceMode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the open polylines from a single layer from a single mesh-volume to the layer plan \p gcodeLayer for mesh the surface modes.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshOpenPolyLinesToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add a single layer from a single mesh-volume to the layer plan \p gcode_layer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param gcode_layer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshLayerToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add a single part from a given layer of a mesh-volume to the layer plan \p gcode_layer.
*
* \param[in] storage where the slice data is stored.
* \param mesh The mesh to add to the layer plan \p gcode_layer.
* \param part The part to add
* \param gcode_layer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
*
*/
void addMeshPartToGCode(SliceDataStorage& storage, const SliceMeshStorage* mesh, const SliceLayerPart& part, GCodePlanner& gcode_layer, int layer_nr);
/*!
* Add thicker (multiple layers) sparse infill for a given part in a layer plan.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processMultiLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Add normal sparse infill for a given part in a layer.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param infill_line_distance The distance between the infill lines
* \param infill_overlap The distance by which the infill overlaps with the wall insets.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processSingleLayerInfill(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, int infill_line_distance, int infill_overlap, int fillAngle);
/*!
* Generate the insets for the walls of a given layer part.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param z_seam_type dir3ective for where to start the outer paerimeter of a part
* \param z_seam_pos The location near where to start the outer inset in case \p z_seam_type is 'back'
*/
void processInsets(GCodePlanner& gcodeLayer, const SliceMeshStorage* mesh, const SliceLayerPart& part, unsigned int layer_nr, EZSeamType z_seam_type, Point z_seam_pos);
/*!
* Add the gcode of the top/bottom skin of the given part and of the perimeter gaps.
*
* Perimter gaps are generated for skin outlines and printed while the skin fill of the skin part is printed.
* Perimeter gaps between the walls are added to the gcode afterwards.
*
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param mesh The mesh for which to add to the layer plan \p gcodeLayer.
* \param part The part for which to create gcode
* \param layer_nr The current layer number.
* \param skin_overlap The distance by which the skin overlaps with the wall insets and the distance by which the perimeter gaps overlap with adjacent print features.
* \param fillAngle The angle in the XY plane at which the infill is generated.
*/
void processSkinAndPerimeterGaps(cura::GCodePlanner& gcode_layer, const cura::SliceMeshStorage* mesh, const cura::SliceLayerPart& part, unsigned int layer_nr, int skin_overlap, int infill_angle);
/*!
* Add the support to the layer plan \p gcodeLayer of the current layer for all support parts with the given \p extruder_nr.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support was added to the layer plan
*/
bool addSupportToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int extruder_nr);
/*!
* Add the support lines/walls to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support infill was added to the layer plan
*/
bool addSupportInfillToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Add the support skins to the layer plan \p gcodeLayer of the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \return whether any support skin was added to the layer plan
*/
bool addSupportRoofsToGCode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr);
/*!
* Change to a new extruder, and add the prime tower instructions if the new extruder is different from the last.
*
* On layer 0 this function adds the skirt for the nozzle it switches to, instead of the prime tower.
*
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param extruder_nr The extruder to which to switch
*/
void setExtruder_addPrime(SliceDataStorage& storage, GCodePlanner& gcode_layer, int layer_nr, int extruder_nr);
/*!
* Add the prime tower gcode for the current layer.
* \param[in] storage where the slice data is stored.
* \param gcodeLayer The initial planning of the gcode of the layer.
* \param layer_nr The index of the layer to write the gcode of.
* \param prev_extruder The current extruder with which we last printed.
*/
void addPrimeTower(SliceDataStorage& storage, GCodePlanner& gcodeLayer, int layer_nr, int prev_extruder);
/*!
* Add the end gcode and set all temperatures to zero.
*/
void finalize();
};
}//namespace cura
#endif // GCODE_WRITER_H
-846
Ver Arquivo
@@ -1,846 +0,0 @@
#include "FffPolygonGenerator.h"
#include <algorithm>
#include <map> // multimap (ordered map allowing duplicate keys)
#include <omp.h>
#include "utils/math.h"
#include "utils/algorithm.h"
#include "slicer.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "MeshGroup.h"
#include "support.h"
#include "multiVolumes.h"
#include "layerPart.h"
#include "WallsComputation.h"
#include "SkirtBrim.h"
#include "skin.h"
#include "infill.h"
#include "raft.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "ConicalOverhang.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
#include "progress/ProgressEstimatorLinear.h"
#include "multithreadOpenMP.h"
namespace cura
{
bool FffPolygonGenerator::generateAreas(SliceDataStorage& storage, MeshGroup* meshgroup, TimeKeeper& timeKeeper)
{
#pragma omp parallel
{
#pragma omp master
{
#ifdef _OPENMP
log("OpenMP enabled, number of threads used: %u\n", omp_get_num_threads());
#else
log("OpenMP disabled\n");
#endif
}
}
if (!sliceModel(meshgroup, timeKeeper, storage))
{
return false;
}
slices2polygons(storage, timeKeeper);
return true;
}
unsigned int FffPolygonGenerator::getDraftShieldLayerCount(const unsigned int total_layers) const
{
if (!getSettingBoolean("draft_shield_enabled"))
{
return 0;
}
switch (getSettingAsDraftShieldHeightLimitation("draft_shield_height_limitation"))
{
default:
case DraftShieldHeightLimitation::FULL:
return total_layers;
case DraftShieldHeightLimitation::LIMITED:
return std::max((coord_t)0, (getSettingInMicrons("draft_shield_height") - getSettingInMicrons("layer_height_0")) / getSettingInMicrons("layer_height") + 1);
}
}
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
storage.model_size = storage.model_max - storage.model_min;
log("Slicing model...\n");
int initial_layer_thickness = getSettingInMicrons("layer_height_0");
if(initial_layer_thickness <= 0) //Initial layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Initial layer height %i is disallowed.\n", initial_layer_thickness);
return false;
}
int layer_thickness = getSettingInMicrons("layer_height");
if(layer_thickness <= 0) //Layer height of 0 is not allowed. Negative layer height is nonsense.
{
logError("Layer height %i is disallowed.\n", layer_thickness);
return false;
}
int initial_slice_z = initial_layer_thickness - layer_thickness / 2;
int slice_layer_count = (storage.model_max.z - initial_slice_z) / layer_thickness + 1;
if (slice_layer_count <= 0) //Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
{
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++)
{
Mesh& mesh = meshgroup->meshes[mesh_idx];
Slicer* slicer = new Slicer(&mesh, initial_slice_z, layer_thickness, slice_layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
slicerList.push_back(slicer);
/*
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
meshgroup->clear();///Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
for(unsigned int meshIdx=0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
if (mesh.getSettingBoolean("conical_overhang_enabled") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
ConicalOverhang::apply(slicerList[meshIdx], mesh.getSettingInAngleRadians("conical_overhang_angle"), layer_thickness);
}
}
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
if (storage.getSettingBoolean("carve_multiple_volumes"))
{
carveMultipleVolumes(slicerList, storage.getSettingBoolean("alternate_carve_order"));
}
generateMultipleVolumesOverlap(slicerList);
storage.print_layer_count = 0;
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
Slicer* slicer = slicerList[meshIdx];
if (!mesh.getSettingBoolean("anti_overhang_mesh") && !mesh.getSettingBoolean("infill_mesh"))
{
storage.print_layer_count = std::max(storage.print_layer_count, (unsigned int)slicer->layers.size());
}
}
storage.support.supportLayers.resize(storage.print_layer_count);
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Slicer* slicer = slicerList[meshIdx];
Mesh& mesh = storage.meshgroup->meshes[meshIdx];
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
if (mesh.getSettingBoolean("anti_overhang_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.anti_overhang = support_layer.anti_overhang.unionPolygons(slicer_layer.polygons);
meshStorage.layers[layer_nr].printZ =
slicer_layer.z
+ getSettingInMicrons("layer_height_0")
- initial_slice_z;
}
continue;
}
if (mesh.getSettingBoolean("support_mesh"))
{
for (unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
SupportLayer& support_layer = storage.support.supportLayers[layer_nr];
SlicerLayer& slicer_layer = slicer->layers[layer_nr];
support_layer.support_mesh.add(slicer_layer.polygons);
meshStorage.layers[layer_nr].printZ =
slicer_layer.z
+ getSettingInMicrons("layer_height_0")
- initial_slice_z;
}
continue;
}
createLayerParts(meshStorage, slicer, mesh.getSettingBoolean("meshfix_union_all"), mesh.getSettingBoolean("meshfix_union_all_remove_holes"));
delete slicerList[meshIdx];
bool has_raft = getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT;
//Add the raft offset to each layer.
for(unsigned int layer_nr=0; layer_nr<meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
meshStorage.layers[layer_nr].printZ +=
getSettingInMicrons("layer_height_0")
- initial_slice_z;
if (has_raft)
{
ExtruderTrain* train = storage.meshgroup->getExtruderTrain(getSettingAsIndex("adhesion_extruder_nr"));
layer.printZ +=
Raft::getTotalThickness(storage)
+ train->getSettingInMicrons("raft_airgap")
- train->getSettingInMicrons("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
{
layer.printZ += train->getSettingInMicrons("layer_0_z_overlap"); // undo shifting down of first layer
}
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
meshStorage.layer_nr_max_filled_layer = layer_nr; // last set by the highest non-empty layer
}
}
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
return true;
}
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
// compute layer count and remove first empty layers
// there is no separate progress stage for removeEmptyFisrtLayer (TODO)
unsigned int slice_layer_count = 0;
for (SliceMeshStorage& mesh : storage.meshes)
{
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
slice_layer_count = std::max<unsigned int>(slice_layer_count, mesh.layers.size());
}
}
// handle meshes
std::vector<double> mesh_timings;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
mesh_timings.push_back(1.0); // TODO: have a more accurate estimate of the relative time it takes per mesh, based on the height and number of polygons
}
ProgressStageEstimator inset_skin_progress_estimate(mesh_timings);
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, &time_keeper);
std::vector<unsigned int> mesh_order;
{ // compute mesh order
std::multimap<int, unsigned int> order_to_mesh_indices;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{
order_to_mesh_indices.emplace(storage.meshes[mesh_idx].getSettingAsIndex("infill_mesh_order"), mesh_idx);
}
for (std::pair<const int, unsigned int>& order_and_mesh_idx : order_to_mesh_indices)
{
mesh_order.push_back(order_and_mesh_idx.second);
}
}
for (unsigned int mesh_order_idx(0); mesh_order_idx < mesh_order.size(); ++mesh_order_idx)
{
processBasicWallsSkinInfill(storage, mesh_order_idx, mesh_order, inset_skin_progress_estimate);
Progress::messageProgress(Progress::Stage::INSET_SKIN, mesh_order_idx + 1, storage.meshes.size());
}
for (unsigned int layer_nr = 0; layer_nr < slice_layer_count; layer_nr++)
{
SliceLayer* layer = nullptr;
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
{ // find first mesh which has this layer
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
if (int(layer_nr) <= mesh.layer_nr_max_filled_layer)
{
layer = &mesh.layers[layer_nr];
break;
}
}
if (layer != nullptr)
{
if (CommandSocket::isInstantiated())
{ // send layer info
CommandSocket::getInstance()->sendOptimizedLayerInfo(layer_nr, layer->printZ, layer_nr == 0? getSettingInMicrons("layer_height_0") : getSettingInMicrons("layer_height"));
}
}
}
log("Layer count: %i\n", storage.print_layer_count);
//layerparts2HTML(storage, "output/output.html");
Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper);
AreaSupport::generateSupportAreas(storage, storage.print_layer_count);
// we need to remove empty layers after we have procesed the insets
// processInsets might throw away parts if they have no wall at all (cause it doesn't fit)
// brim depends on the first layer not being empty
// only remove empty layers if we haven't generate support, because then support was added underneath the model.
// for some materials it's better to print on support than on the buildplate.
removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), storage.print_layer_count); // changes storage.print_layer_count!
if (storage.print_layer_count == 0)
{
log("Stopping process because there are no non-empty layers.\n");
return;
}
/*
if (storage.support.generated)
{
for (unsigned int layer_idx = 0; layer_idx < storage.print_layer_count; layer_idx++)
{
Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr"));
CommandSocket::sendPolygons(PrintFeatureType::Infill, support, 100); // infill_extr->getSettingInMicrons("support_line_width"));
}
}
*/
computePrintHeightStatistics(storage);
// handle helpers
storage.primeTower.generatePaths(storage);
storage.primeTower.subtractFromSupport(storage);
logDebug("Processing ooze shield\n");
processOozeShield(storage);
logDebug("Processing draft shield\n");
processDraftShield(storage);
logDebug("Processing platform adhesion\n");
processPlatformAdhesion(storage);
// meshes post processing
for (SliceMeshStorage& mesh : storage.meshes)
{
processDerivedWallsSkinInfill(mesh);
}
}
void FffPolygonGenerator::processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
size_t mesh_layer_count = mesh.layers.size();
if (mesh.getSettingBoolean("infill_mesh"))
{
processInfillMesh(storage, mesh_order_idx, mesh_order);
}
// TODO: make progress more accurate!!
// note: estimated time for insets : skins = 22.953 : 48.858
std::vector<double> walls_vs_skin_timing({22.953, 48.858});
ProgressStageEstimator* mesh_inset_skin_progress_estimator = new ProgressStageEstimator(walls_vs_skin_timing);
inset_skin_progress_estimate.nextStage(mesh_inset_skin_progress_estimator); // the stage of this function call
ProgressEstimatorLinear* inset_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(inset_estimator);
// walls
int processed_layer_count = 0;
#pragma omp parallel for default(none) shared(mesh_layer_count, mesh, inset_skin_progress_estimate, processed_layer_count) schedule(dynamic)
for(unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
logDebug("Processing insets for layer %i of %i\n", layer_number, mesh_layer_count);
processInsets(mesh, layer_number);
#ifdef _OPENMP
if (omp_get_thread_num() == 0)
#endif
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
int _processed_layer_count;
#pragma omp atomic read
_processed_layer_count = processed_layer_count;
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
#pragma omp atomic
processed_layer_count++;
}
ProgressEstimatorLinear* skin_estimator = new ProgressEstimatorLinear(mesh_layer_count);
mesh_inset_skin_progress_estimator->nextStage(skin_estimator);
bool process_infill = mesh.getSettingInMicrons("infill_line_distance") > 0;
if (!process_infill)
{ // do process infill anyway if it's modified by modifier meshes
for (unsigned int other_mesh_order_idx(mesh_order_idx + 1); other_mesh_order_idx < mesh_order.size(); ++other_mesh_order_idx)
{
unsigned int other_mesh_idx = mesh_order[other_mesh_order_idx];
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (other_mesh.getSettingBoolean("infill_mesh"))
{
AABB3D aabb = storage.meshgroup->meshes[mesh_idx].getAABB();
AABB3D other_aabb = storage.meshgroup->meshes[other_mesh_idx].getAABB();
if (aabb.hit(other_aabb))
{
process_infill = true;
}
}
}
}
// skin & infill
// Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper);
int mesh_max_bottom_layer_count = 0;
if (mesh.getSettingBoolean("magic_spiralize"))
{
mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
}
processed_layer_count = 0;
#pragma omp parallel default(none) shared(mesh_layer_count, mesh, mesh_max_bottom_layer_count, process_infill, inset_skin_progress_estimate, processed_layer_count)
{
#pragma omp for schedule(dynamic)
for (unsigned int layer_number = 0; layer_number < mesh.layers.size(); layer_number++)
{
logDebug("Processing skins and infill layer %i of %i\n", layer_number, mesh_layer_count);
if (!mesh.getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count) //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
{
processSkinsAndInfill(mesh, layer_number, process_infill);
}
#ifdef _OPENMP
if (omp_get_thread_num() == 0)
#endif
{ // progress estimation is done only in one thread so that no two threads message progress at the same time
int _processed_layer_count;
#pragma omp atomic read
_processed_layer_count = processed_layer_count;
double progress = inset_skin_progress_estimate.progress(_processed_layer_count);
Progress::messageProgress(Progress::Stage::INSET_SKIN, progress * 100, 100);
}
#pragma omp atomic
processed_layer_count++;
}
}
}
void FffPolygonGenerator::processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order)
{
unsigned int mesh_idx = mesh_order[mesh_order_idx];
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
mesh.layer_nr_max_filled_layer = -1;
for (unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
{
SliceLayer& layer = mesh.layers[layer_idx];
std::vector<PolygonsPart> new_parts;
for (unsigned int other_mesh_idx : mesh_order)
{ // limit the infill mesh's outline to within the infill of all meshes with lower order
if (other_mesh_idx == mesh_idx)
{
break; // all previous meshes have been processed
}
SliceMeshStorage& other_mesh = storage.meshes[other_mesh_idx];
if (layer_idx >= other_mesh.layers.size())
{ // there can be no interaction between the infill mesh and this other non-infill mesh
continue;
}
SliceLayer& other_layer = other_mesh.layers[layer_idx];
for (SliceLayerPart& part : layer.parts)
{
for (SliceLayerPart& other_part : other_layer.parts)
{ // limit the outline of each part of this infill mesh to the infill of parts of the other mesh with lower infill mesh order
if (!part.boundaryBox.hit(other_part.boundaryBox))
{ // early out
continue;
}
Polygons new_outline = part.outline.intersection(other_part.getOwnInfillArea());
if (new_outline.size() == 1)
{ // we don't have to call splitIntoParts, because a single polygon can only be a single part
PolygonsPart outline_part_here;
outline_part_here.add(PolygonRef{new_outline[0]});
new_parts.push_back(outline_part_here);
}
else if (new_outline.size() > 1)
{ // we don't know whether it's a multitude of parts because of newly introduced holes, or because the polygon has been split up
std::vector<PolygonsPart> new_parts_here = new_outline.splitIntoParts();
for (PolygonsPart& new_part_here : new_parts_here)
{
new_parts.push_back(new_part_here);
}
}
// change the infill area of the non-infill mesh which is to be filled with e.g. lines
other_part.infill_area_own = other_part.getOwnInfillArea().difference(part.outline);
// note: don't change the part.infill_area, because we change the structure of that area, while the basic area in which infill is printed remains the same
// the infill area remains the same for combing
}
}
}
layer.parts.clear();
for (PolygonsPart& part : new_parts)
{
layer.parts.emplace_back();
layer.parts.back().outline = part;
layer.parts.back().boundaryBox.calculate(part);
}
if (layer.parts.size() > 0 || (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0) )
{
mesh.layer_nr_max_filled_layer = layer_idx; // last set by the highest non-empty layer
}
}
}
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh)
{
// create gradual infill areas
SkinInfillAreaComputation::generateGradualInfill(mesh, mesh.getSettingInMicrons("gradual_infill_step_height"), mesh.getSettingAsCount("gradual_infill_steps"));
//SubDivCube Pre-compute Octree
if (mesh.getSettingAsFillMethod("infill_pattern") == EFillMethod::CUBICSUBDIV)
{
SubDivCube::precomputeOctree(mesh);
}
// combine infill
unsigned int combined_infill_layers = std::max(1U, round_divide(mesh.getSettingInMicrons("infill_sparse_thickness"), std::max(getSettingInMicrons("layer_height"), (coord_t)1))); //How many infill layers to combine to obtain the requested sparse thickness.
combineInfillLayers(mesh,combined_infill_layers);
// fuzzy skin
if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
{
processFuzzyWalls(mesh);
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInsets only reads and writes data for the current layer
*
* processInsets only reads and writes data for the current layer
*/
void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr)
{
SliceLayer* layer = &mesh.layers[layer_nr];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE)
{
int inset_count = mesh.getSettingAsCount("wall_line_count");
if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && ((layer_nr % 2) + 2) % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight.
inset_count += 5;
int line_width_x = mesh.getSettingInMicrons("wall_line_width_x");
int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0");
if (mesh.getSettingBoolean("alternate_extra_perimeter"))
{
inset_count += ((layer_nr % 2) + 2) % 2;
}
bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable");
WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall);
walls_computation.generateInsets(layer);
}
}
void FffPolygonGenerator::removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers)
{
int n_empty_first_layers = 0;
for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
{
bool layer_is_empty = true;
if (storage.support.generated && layer_idx < storage.support.supportLayers.size())
{
SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
if (support_layer.supportAreas.size() > 0 || support_layer.skin.size() > 0)
{
layer_is_empty = false;
break;
}
}
for (SliceMeshStorage& mesh : storage.meshes)
{
SliceLayer& layer = mesh.layers[layer_idx];
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL && layer.openPolyLines.size() > 0)
{
layer_is_empty = false;
break;
}
for (const SliceLayerPart& part : layer.parts)
{
if (part.print_outline.size() > 0)
{
layer_is_empty = false;
break;
}
}
}
if (layer_is_empty)
{
n_empty_first_layers++;
} else
{
break;
}
}
if (n_empty_first_layers > 0)
{
log("Removing %d layers because they are empty\n", n_empty_first_layers);
for (SliceMeshStorage& mesh : storage.meshes)
{
std::vector<SliceLayer>& layers = mesh.layers;
layers.erase(layers.begin(), layers.begin() + n_empty_first_layers);
for (SliceLayer& layer : layers)
{
layer.printZ -= n_empty_first_layers * layer_height;
}
mesh.layer_nr_max_filled_layer -= n_empty_first_layers;
}
total_layers -= n_empty_first_layers;
storage.support.layer_nr_max_filled_layer -= n_empty_first_layers;
std::vector<SupportLayer>& support_layers = storage.support.supportLayers;
support_layers.erase(support_layers.begin(), support_layers.begin() + n_empty_first_layers);
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateSkins read (depend on) data from mesh.layers[*].parts[*].insets and write mesh.layers[n].parts[*].skin_parts
* generateInfill read mesh.layers[n].parts[*].{insets,skin_parts,boundingBox} and write mesh.layers[n].parts[*].infill_area
*
* processSkinsAndInfill read (depend on) mesh.layers[*].parts[*].{insets,boundingBox}.
* write mesh.layers[n].parts[*].{skin_parts,infill_area}.
*/
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill)
{
if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)
{
return;
}
const int wall_line_count = mesh.getSettingAsCount("wall_line_count");
const int innermost_wall_line_width = (wall_line_count == 1) ? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x");
generateSkins(layer_nr, mesh, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_line_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic"));
if (process_infill)
{ // process infill when infill density > 0
// or when other infill meshes want to modify this infill
int infill_skin_overlap = 0;
bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10;
if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC)
{
infill_skin_overlap = innermost_wall_line_width / 2;
}
generateInfill(layer_nr, mesh, innermost_wall_line_width, infill_skin_overlap, wall_line_count);
}
}
void FffPolygonGenerator::computePrintHeightStatistics(SliceDataStorage& storage)
{
int extruder_count = storage.meshgroup->getExtruderCount();
std::vector<int>& max_print_height_per_extruder = storage.max_print_height_per_extruder;
assert(max_print_height_per_extruder.size() == 0 && "storage.max_print_height_per_extruder shouldn't have been initialized yet!");
max_print_height_per_extruder.resize(extruder_count, -1); //Initialize all as -1.
{ // compute max_object_height_per_extruder
//Height of the meshes themselves.
for (SliceMeshStorage& mesh : storage.meshes)
{
if (mesh.getSettingBoolean("anti_overhang_mesh") || mesh.getSettingBoolean("support_mesh"))
{
continue; //Special type of mesh that doesn't get printed.
}
const unsigned int extr_nr = mesh.getSettingAsIndex("extruder_nr");
max_print_height_per_extruder[extr_nr] = std::max(max_print_height_per_extruder[extr_nr], mesh.layer_nr_max_filled_layer);
}
//Height of where the support reaches.
const unsigned int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr"); // TODO: support extruder should be configurable per object
max_print_height_per_extruder[support_infill_extruder_nr] =
std::max(max_print_height_per_extruder[support_infill_extruder_nr],
storage.support.layer_nr_max_filled_layer);
const unsigned int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr"); // TODO: support skin extruder should be configurable per object
max_print_height_per_extruder[support_skin_extruder_nr] =
std::max(max_print_height_per_extruder[support_skin_extruder_nr],
storage.support.layer_nr_max_filled_layer);
//Height of where the platform adhesion reaches.
if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
max_print_height_per_extruder[adhesion_extruder_nr] =
std::max(0, max_print_height_per_extruder[adhesion_extruder_nr]);
}
}
storage.max_print_height_order = order(max_print_height_per_extruder);
if (extruder_count >= 2)
{
int second_highest_extruder = storage.max_print_height_order[extruder_count - 2];
storage.max_print_height_second_to_last_extruder = max_print_height_per_extruder[second_highest_extruder];
}
else
{
storage.max_print_height_second_to_last_extruder = -1;
}
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage)
{
if (!getSettingBoolean("ooze_shield_enabled"))
{
return;
}
const int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
for (int layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist, ClipperLib::jtRound));
}
double angle = getSettingInAngleDegrees("ooze_shield_angle");
if (angle <= 89)
{
int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height"); // Allow for a 60deg angle in the oozeShield.
for (int layer_nr = 1; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr - 1].offset(-allowed_angle_offset));
}
for (int layer_nr = storage.max_print_height_second_to_last_extruder; layer_nr > 0; layer_nr--)
{
storage.oozeShield[layer_nr - 1] = storage.oozeShield[layer_nr - 1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
}
}
const float largest_printed_area = 1.0; // TODO: make var a parameter, and perhaps even a setting?
for (int layer_nr = 0; layer_nr <= storage.max_print_height_second_to_last_extruder; layer_nr++)
{
storage.oozeShield[layer_nr].removeSmallAreas(largest_printed_area);
}
}
void FffPolygonGenerator::processDraftShield(SliceDataStorage& storage)
{
const unsigned int draft_shield_layers = getDraftShieldLayerCount(storage.print_layer_count);
if (draft_shield_layers <= 0)
{
return;
}
const int layer_height = getSettingInMicrons("layer_height");
const unsigned int layer_skip = 500 / layer_height + 1;
Polygons& draft_shield = storage.draft_protection_shield;
for (unsigned int layer_nr = 0; layer_nr < storage.print_layer_count && layer_nr < draft_shield_layers; layer_nr += layer_skip)
{
draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
}
const int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
storage.draft_protection_shield = draft_shield.approxConvexHull(draft_shield_dist);
}
void FffPolygonGenerator::processPlatformAdhesion(SliceDataStorage& storage)
{
SettingsBaseVirtual* train = storage.meshgroup->getExtruderTrain(getSettingBoolean("adhesion_extruder_nr"));
switch(getSettingAsPlatformAdhesion("adhesion_type"))
{
case EPlatformAdhesion::SKIRT:
{
constexpr bool outside_polygons_only = true;
SkirtBrim::generate(storage, train->getSettingInMicrons("skirt_gap"), train->getSettingAsCount("skirt_line_count"), outside_polygons_only);
}
break;
case EPlatformAdhesion::BRIM:
SkirtBrim::generate(storage, 0, train->getSettingAsCount("brim_line_count"), train->getSettingBoolean("brim_outside_only"));
break;
case EPlatformAdhesion::RAFT:
Raft::generate(storage, train->getSettingInMicrons("raft_margin"));
break;
case EPlatformAdhesion::NONE:
break;
}
}
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 (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;
Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0];
for (PolygonRef poly : skin)
{
// 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)
{
int r = rand() % (fuzziness * 2) - fuzziness;
Point perp_to_p0p1 = turn90CCW(p0p1);
Point fuzz = normal(perp_to_p0p1, r);
Point pa = *p0 + normal(p0p1, p0pa_dist) + 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);
}
}
skin = results;
}
}
}
}//namespace cura
-174
Ver Arquivo
@@ -1,174 +0,0 @@
#ifndef FFF_AREA_GENERATOR_H
#define FFF_AREA_GENERATOR_H
#include "MeshGroup.h"
#include "utils/polygonUtils.h"
#include "utils/NoCopy.h"
#include "utils/gettime.h"
#include "settings/settings.h"
#include "sliceDataStorage.h"
#include "commandSocket.h"
#include "PrintFeature.h"
#include "progress/ProgressEstimator.h"
#include "progress/ProgressStageEstimator.h"
namespace cura
{
/*!
* 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.
* After slicing, the layers are processed; for example the wall insets are generated, and the areas which are to be filled with support and infill, which are all represented by polygons.
* In this stage nothing other than areas and circular paths are generated, which are both represented by polygons.
* No infill lines or support pattern etc. is generated.
*
* The main function of this class is FffPolygonGenerator::generateAreas().
*/
class FffPolygonGenerator : public SettingsMessenger, NoCopy
{
public:
/*!
* Basic constructor
*/
FffPolygonGenerator(SettingsBase* settings_)
: SettingsMessenger(settings_)
{
}
/*!
* Slice the \p object, process the outline information into inset perimeter polygons, support area polygons, etc.
*
* \param object The object to slice.
* \param timeKeeper Object which keeps track of timings of each stage.
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
*/
bool generateAreas(SliceDataStorage& storage, MeshGroup* object, TimeKeeper& timeKeeper);
private:
/*!
* \brief Helper function to get the actual height of the draft shield.
*
* The draft shield is the height of the print if we've set the draft shield
* limitation to FULL. Otherwise the height is set to the height limit
* setting. If the draft shield is disabled, the height is always 0.
*
* \param total_layers The total number of layers in the print (the height
* of the draft shield if the limit is FULL.
* \return The actual height of the draft shield.
*/
unsigned int getDraftShieldLayerCount(unsigned int total_layers) const;
/*!
* Slice the \p object and store the outlines in the \p storage.
*
* \param object The object to slice.
* \param timeKeeper Object which keeps track of timings of each stage.
* \param storage Output parameter: where the outlines are stored. See SliceLayerPart::outline.
*
* \return Whether the process succeeded (always true).
*/
bool sliceModel(MeshGroup* object, TimeKeeper& timeKeeper, SliceDataStorage& storage); /// slices the model
/*!
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, support area polygons, etc.
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param timeKeeper Object which keeps track of timings of each stage.
*/
void slices2polygons(SliceDataStorage& storage, TimeKeeper& timeKeeper);
/*!
* Processes the outline information as stored in the \p storage: generates inset perimeter polygons, skin and infill
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed (used for infill meshes)
* \param inset_skin_progress_estimate The progress stage estimate calculator
*/
void processBasicWallsSkinInfill(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order, ProgressStageEstimator& inset_skin_progress_estimate);
/*!
* Process the mesh to be an infill mesh: limit all outlines to within the infill of normal meshes and subtract their volume from the infill of those meshes
*
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param mesh_order_idx The index of the mesh_idx in \p mesh_order to process in the vector of meshes in \p storage
* \param mesh_order The order in which the meshes are processed
*/
void processInfillMesh(SliceDataStorage& storage, unsigned int mesh_order_idx, std::vector<unsigned int>& mesh_order);
/*!
* Process features which are derived from the basic walls, skin, and infill:
* fuzzy skin, infill combine
*
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
*/
void processDerivedWallsSkinInfill(SliceMeshStorage& mesh);
/*!
* Remove all bottom layers which are empty.
*
* \warning Changes \p total_layers
*
* \param storage Input and Ouput parameter: stores all layers
* \param layer_height The height of each layer
* \param total_layers The total number of layers
*/
void removeEmptyFirstLayers(SliceDataStorage& storage, const int layer_height, unsigned int& total_layers);
/*!
* Set \ref SliceDataStorage::max_print_height_per_extruder and \ref SliceDataStorage::max_print_height_order and \ref SliceDataStorage::max_print_height_second_to_last_extruder
*
* \param[in,out] storage Where to retrieve mesh and support etc settings from and where the print height statistics are saved.
*/
void computePrintHeightStatistics(SliceDataStorage& storage);
/*!
* Generate the inset polygons which form the walls.
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the insets.
*/
void processInsets(SliceMeshStorage& mesh, unsigned int layer_nr);
/*!
* Generate the outline of the ooze shield.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
*/
void processOozeShield(SliceDataStorage& storage);
/*!
* Generate the skin areas.
* \param mesh Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
* \param layer_nr The layer for which to generate the skin areas.
* \param process_infill Generate infill areas
*/
void processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr, bool process_infill);
/*!
* 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
*/
void processDraftShield(SliceDataStorage& storage);
/*!
* Generate the skirt/brim/raft areas/insets.
* \param storage Input and Output parameter: fetches the outline information (see SliceLayerPart::outline) and generates the other reachable field of the \p storage
*/
void processPlatformAdhesion(SliceDataStorage& storage);
/*!
* Make the outer wall 'fuzzy'
*
* Introduce new vertices and move existing vertices in or out by a random distance, based on the fuzzy skin settings.
*
* This only changes the outer wall.
*
* \param[in,out] mesh where the outer wall is retrieved and stored in.
*/
void processFuzzyWalls(SliceMeshStorage& mesh);
};
}//namespace cura
#endif // FFF_AREA_GENERATOR_H
-119
Ver Arquivo
@@ -1,119 +0,0 @@
#include "FffProcessor.h"
namespace cura
{
FffProcessor FffProcessor::instance; // definition must be in cpp
FffProcessor::FffProcessor()
: polygon_generator(this)
, gcode_writer(this)
, meshgroup_number(0)
{
}
int FffProcessor::getMeshgroupNr()
{
return meshgroup_number;
}
std::string FffProcessor::getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup)
{
std::stringstream sstream;
if (first_meshgroup)
{
sstream << getAllLocalSettingsString(); // global settings
sstream << " -g";
}
else
{
sstream << " --next";
}
sstream << meshgroup.getAllLocalSettingsString();
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
{
ExtruderTrain* train = meshgroup.getExtruderTrain(extruder_nr);
sstream << " -e" << extruder_nr << train->getAllLocalSettingsString();
}
for (unsigned int mesh_idx = 0; mesh_idx < meshgroup.meshes.size(); mesh_idx++)
{
Mesh& mesh = meshgroup.meshes[mesh_idx];
sstream << " -e" << mesh.getSettingAsIndex("extruder_nr") << " -l \"" << mesh_idx << "\"" << mesh.getAllLocalSettingsString();
}
sstream << "\n";
return sstream.str();
}
bool FffProcessor::processMeshGroup(MeshGroup* meshgroup)
{
if (SHOW_ALL_SETTINGS) { logWarning(getAllSettingsString(*meshgroup, meshgroup_number == 0).c_str()); }
time_keeper.restart();
if (!meshgroup)
return false;
TimeKeeper time_keeper_total;
polygon_generator.setParent(meshgroup);
gcode_writer.setParent(meshgroup);
bool empty = true;
for (Mesh& mesh : meshgroup->meshes)
{
if (!mesh.getSettingBoolean("infill_mesh") && !mesh.getSettingBoolean("anti_overhang_mesh"))
{
empty = false;
}
}
if (empty)
{
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
return true;
}
if (meshgroup->getSettingBoolean("wireframe_enabled"))
{
log("starting Neith Weaver...\n");
Weaver w(this);
w.weave(meshgroup);
log("starting Neith Gcode generation...\n");
Wireframe2gcode gcoder(w, gcode_writer.gcode, this);
gcoder.writeGCode();
log("finished Neith Gcode generation...\n");
} else
{
SliceDataStorage storage(meshgroup);
if (!polygon_generator.generateAreas(storage, meshgroup, time_keeper))
{
return false;
}
Progress::messageProgressStage(Progress::Stage::EXPORT, &time_keeper);
gcode_writer.writeGCode(storage, time_keeper);
}
Progress::messageProgress(Progress::Stage::FINISH, 1, 1); // 100% on this meshgroup
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
CommandSocket::getInstance()->sendOptimizedLayerData();
}
log("Total time elapsed %5.2fs.\n", time_keeper_total.restart());
profile_string += getAllSettingsString(*meshgroup, meshgroup_number == 0);
meshgroup_number++;
polygon_generator.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
gcode_writer.setParent(this); // otherwise consequent getSetting calls (e.g. for finalize) will refer to non-existent meshgroup
return true;
}
} // namespace cura
-166
Ver Arquivo
@@ -1,166 +0,0 @@
#ifndef FFF_PROCESSOR_H
#define FFF_PROCESSOR_H
#include "settings/settings.h"
#include "FffGcodeWriter.h"
#include "FffPolygonGenerator.h"
#include "commandSocket.h"
#include "Weaver.h"
#include "Wireframe2gcode.h"
#include "progress/Progress.h"
#include "utils/gettime.h"
#include "utils/NoCopy.h"
#define SHOW_ALL_SETTINGS true
namespace cura {
//FusedFilamentFabrication processor. Singleton class
class FffProcessor : public SettingsBase , NoCopy
{
private:
/*!
* The FffProcessor used for the (current) slicing (The instance of this singleton)
*/
static FffProcessor instance;
FffProcessor();
public:
/*!
* Get the instance
* \return The instance
*/
static FffProcessor* getInstance()
{
return &instance;
}
/*!
* Get the index of the meshgroup currently being processed, starting at zero.
*/
int getMeshgroupNr();
private:
/*!
* The polygon generator, which slices the models and generates all polygons to be printed and areas to be filled.
*/
FffPolygonGenerator polygon_generator;
/*!
* The gcode writer, which generates paths in layer plans in a buffer, which converts these paths into gcode commands.
*/
FffGcodeWriter gcode_writer;
/*!
* The index of the meshgroup currently being processed, starting at zero.
*/
int meshgroup_number;
/*!
* A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* Used in debugging.
*/
std::string profile_string = "";
/*!
* Get all settings for the current meshgroup in the format by which CuraEngine is called via the command line.
*
* Also includes all global settings if this is the first meshgroup.
*
* Used in debugging.
*
* \param meshgroup The meshgroup for which to stringify all settings
* \param first_meshgroup Whether this is the first meshgroup and all global settigns should be included as well
*/
std::string getAllSettingsString(MeshGroup& meshgroup, bool first_meshgroup);
public:
/*!
* Get a string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*
* \return A string containing all setting values passed to the engine in the format by which CuraEngine is called via the command line.
*/
std::string getProfileString() { return profile_string; }
/*!
* The stop watch used to time how long the different stages take to compute.
*/
TimeKeeper time_keeper; // TODO: use singleton time keeper
/*!
* Reset the meshgroup number to the first meshgroup to start a new slicing.
*/
void resetMeshGroupNumber()
{
meshgroup_number = 0;
}
/*!
* Set the target to write gcode to: to a file.
*
* Used when CuraEngine is used as command line tool.
*
* \param filename The filename of the file to which to write the gcode.
*/
bool setTargetFile(const char* filename)
{
return gcode_writer.setTargetFile(filename);
}
/*!
* Set the target to write gcode to: an output stream.
*
* Used when CuraEngine is NOT used as command line tool.
*
* \param stream The stream to write gcode to.
*/
void setTargetStream(std::ostream* stream)
{
return gcode_writer.setTargetStream(stream);
}
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr)
{
return gcode_writer.getTotalFilamentUsed(extruder_nr);
}
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime()
{
return gcode_writer.getTotalPrintTime();
}
/*!
* Add the end gcode and set all temperatures to zero.
*/
void finalize()
{
gcode_writer.finalize();
}
/*!
* Generate gcode for a given \p meshgroup
* The primary function of this class.
*
* \param meshgroup The meshgroup for which to generate gcode
* \return Whether this function succeeded
*/
bool processMeshGroup(MeshGroup* meshgroup);
};
}//namespace cura
#endif//FFF_PROCESSOR_H
-77
Ver Arquivo
@@ -1,77 +0,0 @@
#ifndef FLOW_TEMP_GRAPH
#define FLOW_TEMP_GRAPH
#include <cassert>
#include "utils/logoutput.h"
namespace cura
{
/*!
* Class representing a graph matching a flow to a temperature.
* The graph generally consists of several linear line segments between points at which the temperature and flow are matched.
*/
class FlowTempGraph
{
public:
struct Datum
{
double flow; //!< The flow in mm^3/s
double temp; //!< The temperature in *C
Datum(double flow, double temp)
: flow(flow)
, temp(temp)
{}
};
std::vector<Datum> data; //!< The points of the graph between which the graph is linearly interpolated
FlowTempGraph()
{}
/*!
* Get the temperature corresponding to a specific flow.
*
* For flows outside of the chart, the temperature at the minimal or maximal flow is returned.
* When the graph is empty, the @p material_print_temperature is returned.
*
* \param flow the flow in mm^3/s
* \param material_print_temperature The default printing temp (backward compatibility for when the graph fails)
* \return the corresponding temp
*/
double getTemp(double flow, double material_print_temperature, bool flow_dependent_temperature)
{
if (!flow_dependent_temperature || data.size() == 0)
{
return material_print_temperature;
}
if (data.size() == 1)
{
return data.front().temp;
}
if (flow < data.front().flow)
{
logWarning("Warning! Flow too low!\n"); // TODO
return data.front().temp;
}
Datum* last_datum = &data.front();
for (unsigned int datum_idx = 1; datum_idx < data.size(); datum_idx++)
{
Datum& datum = data[datum_idx];
if (datum.flow >= flow)
{
return last_datum->temp + (datum.temp - last_datum->temp) * (flow - last_datum->flow) / (datum.flow - last_datum->flow);
}
last_datum = &datum;
}
logWarning("Warning! Flow too high!\n"); // TODO
return data.back().temp;
};
};
} // namespace cura
#endif // FLOW_TEMP_GRAPH
-111
Ver Arquivo
@@ -1,111 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "utils/intpoint.h" // INT2MM
#include "GCodePathConfig.h"
namespace cura
{
GCodePathConfig::BasicConfig::BasicConfig()
: speed(0)
, acceleration(0)
, jerk(0)
, line_width(0)
, flow(100)
{
}
GCodePathConfig::BasicConfig::BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow)
: speed(speed)
, acceleration(acceleration)
, jerk(jerk)
, line_width(line_width)
, flow(flow)
{
}
void GCodePathConfig::BasicConfig::set(double speed, double acceleration, double jerk, int line_width, double flow)
{
this->speed = speed;
this->acceleration = acceleration;
this->jerk = jerk;
this->line_width = line_width;
this->flow = flow;
}
GCodePathConfig::GCodePathConfig(PrintFeatureType type)
: extrusion_mm3_per_mm(0.0)
, type(type)
{
}
void GCodePathConfig::init(double speed, double acceleration, double jerk, int line_width, double flow)
{
iconic_config.set(speed, acceleration, jerk, line_width, flow);
current_config = iconic_config;
}
void GCodePathConfig::setLayerHeight(int layer_height)
{
this->layer_thickness = layer_height;
calculateExtrusion();
}
void GCodePathConfig::smoothSpeed(GCodePathConfig::BasicConfig first_layer_config, int layer_nr, double max_speed_layer)
{
current_config.speed = (iconic_config.speed * layer_nr) / max_speed_layer + (first_layer_config.speed * (max_speed_layer - layer_nr) / max_speed_layer);
current_config.acceleration = (iconic_config.acceleration * layer_nr) / max_speed_layer + (first_layer_config.acceleration * (max_speed_layer - layer_nr) / max_speed_layer);
current_config.jerk = (iconic_config.jerk * layer_nr) / max_speed_layer + (first_layer_config.jerk * (max_speed_layer - layer_nr) / max_speed_layer);
}
void GCodePathConfig::setSpeedIconic()
{
current_config.speed = iconic_config.speed;
current_config.acceleration = iconic_config.acceleration;
current_config.jerk = iconic_config.jerk;
}
double GCodePathConfig::getExtrusionMM3perMM() const
{
return extrusion_mm3_per_mm;
}
double GCodePathConfig::getSpeed() const
{
return current_config.speed;
}
double GCodePathConfig::getAcceleration() const
{
return current_config.acceleration;
}
double GCodePathConfig::getJerk() const
{
return current_config.jerk;
}
int GCodePathConfig::getLineWidth() const
{
return current_config.line_width;
}
bool GCodePathConfig::isTravelPath() const
{
return current_config.line_width == 0;
}
double GCodePathConfig::getFlowPercentage() const
{
return current_config.flow;
}
void GCodePathConfig::calculateExtrusion()
{
extrusion_mm3_per_mm = INT2MM(current_config.line_width) * INT2MM(layer_thickness) * double(current_config.flow) / 100.0;
}
}//namespace cura
-112
Ver Arquivo
@@ -1,112 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef G_CODE_PATH_CONFIG_H
#define G_CODE_PATH_CONFIG_H
#include "RetractionConfig.h"
#include "PrintFeature.h"
namespace cura
{
/*!
* The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
*/
class GCodePathConfig
{
friend class GCodePlannerTest;
public:
/*!
* The path config settings which may change from layer to layer
*/
struct BasicConfig
{
double speed; //!< movement speed (mm/s)
double acceleration; //!< acceleration of head movement (mm/s^2)
double jerk; //!< jerk of the head movement (around stand still) (mm/s^3)
int line_width; //!< width of the line extruded
double flow; //!< extrusion flow modifier in %
BasicConfig(); //!< basic contructor initializing with inaccurate values
BasicConfig(double speed, double acceleration, double jerk, int line_width, double flow); //!< basic contructor initializing all values
void set(double speed, double acceleration, double jerk, int line_width, double flow); //!< Set all config values
};
private:
BasicConfig iconic_config; //!< The basic path configuration iconic to this print feature type
BasicConfig current_config; //!< The current path configuration for the current layer
int layer_thickness; //!< current layer height in micron
double extrusion_mm3_per_mm;//!< current mm^3 filament moved per mm line traversed
public:
const PrintFeatureType type; //!< name of the feature type
/*!
* Basic constructor.
*/
GCodePathConfig(PrintFeatureType type);
/*!
* Initialize some of the member variables.
*
* \warning GCodePathConfig::setLayerHeight still has to be called before this object can be used.
*
* \param speed The regular speed with which to print this feature
* \param line_width The line width for this feature
* \param flow The flow modifier to apply to the extruded filament when printing this feature
*/
void init(double speed, double acceleration, double jerk, int line_width, double flow);
/*!
* Set the layer height and (re)compute the extrusion_per_mm
*/
void setLayerHeight(int layer_height);
/*!
* Set the speed to somewhere between the speed of @p first_layer_config and the iconic speed.
*
* \warning This functions should not be called with @p layer_nr > @p max_speed_layer !
*
* \param first_layer_config The speed settings at layer zero
* \param layer_nr The layer number
* \param max_speed_layer The layer number for which the speed_iconic should be used.
*/
void smoothSpeed(BasicConfig first_layer_config, int layer_nr, double max_speed_layer);
/*!
* Set the speed config to the iconic speed config, i.e. the normal speed of the feature type for which this is a config.
*
* Does the same for acceleration and jerk.
*/
void setSpeedIconic();
/*!
* Can only be called after the layer height has been set (which is done while writing the gcode!)
*/
double getExtrusionMM3perMM() const;
/*!
* Get the movement speed in mm/s
*/
double getSpeed() const;
/*!
* Get the current acceleration of this config
*/
double getAcceleration() const;
/*!
* Get the current jerk of this config
*/
double getJerk() const;
int getLineWidth() const;
bool isTravelPath() const;
double getFlowPercentage() const;
private:
void calculateExtrusion();
};
}//namespace cura
#endif // G_CODE_PATH_CONFIG_H
-439
Ver Arquivo
@@ -1,439 +0,0 @@
/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */
#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "FffProcessor.h"
namespace cura {
void issueWriteGCode_impl(
GCodeExport* p_gcode,
GCodePlanner* p_front_buffer
){
#pragma omp task default(none) firstprivate(p_gcode, p_front_buffer)
{ MULTITHREAD_TASK_CATCH_EXCEPTION(
GCodeExport& gcode_ref = *p_gcode;
#ifdef _OPENMP
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode_ref.getOutputStreamLock());
#endif
p_front_buffer->writeGCode(gcode_ref);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
)}
}
void LayerPlanBuffer::issueWriteGCode()
{
assert(!(buffer.front().isGCodeWritten()) && "GCode shouldn't be written more than once");
GCodeExport* p_gcode = &gcode;
GCodePlanner* p_front_buffer = &buffer.front();
issueWriteGCode_impl(p_gcode, p_front_buffer);
}
void LayerPlanBuffer::flush()
{
if (buffer.size() > 0)
{
insertTempCommands(); // insert preheat commands of the very last layer
}
#ifdef _OPENMP
omp_lock_guard_t<omp_nest_lock_type> gcode_output_lock_guard(gcode.getOutputStreamLock());
#endif
while (!buffer.empty())
{
buffer.front().writeGCode(gcode);
if (CommandSocket::isInstantiated())
{
CommandSocket::getInstance()->flushGcode();
}
buffer.pop_front();
}
}
void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
{
double acc_time = 0.0;
std::vector<GCodePath>& extruder_plan_before_paths = extruder_plan_before.getPaths();
for (unsigned int path_idx = extruder_plan_before_paths.size() - 1; int(path_idx) != -1 ; path_idx--)
{
GCodePath& path = extruder_plan_before_paths[path_idx];
const double time_this_path = path.estimates.getTotalTime();
acc_time += time_this_path;
if (acc_time > time_after_extruder_plan_start)
{
const double time_before_path_end = acc_time - time_after_extruder_plan_start;
bool wait = false;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait, time_this_path - time_before_path_end);
return;
}
}
bool wait = false;
unsigned int path_idx = 0;
extruder_plan_before.insertCommand(path_idx, extruder, temp, wait); // insert at start of extruder plan if time_after_extruder_plan_start > extruder_plan.time
}
Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double initial_print_temp = extruder_plan.initial_printing_temperature;
double in_between_time = 0.0;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{ // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
ExtruderPlan& extruder_plan_before = *extruder_plans[extruder_plan_before_idx];
if (extruder_plan_before.extruder == extruder)
{
double temp_before = preheat_config.getFinalPrintTemp(extruder);
if (temp_before == 0)
{
temp_before = extruder_plan_before.printing_temperature;
}
constexpr bool during_printing = false;
Preheat::WarmUpResult warm_up = preheat_config.getWarmUpPointAfterCoolDown(in_between_time, extruder, temp_before, preheat_config.getStandbyTemp(extruder), initial_print_temp, during_printing);
warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + extra_preheat_time);
return warm_up;
}
in_between_time += extruder_plan_before.estimates.getTotalTime();
}
// The last extruder plan with the same extruder falls outside of the buffer
// assume the nozzle has cooled down to strandby temperature already.
Preheat::WarmUpResult warm_up;
warm_up.total_time_window = in_between_time;
warm_up.lowest_temperature = preheat_config.getStandbyTemp(extruder);
constexpr bool during_printing = false;
warm_up.heating_time = preheat_config.getTimeToGoFromTempToTemp(extruder, warm_up.lowest_temperature, initial_print_temp, during_printing);
if (warm_up.heating_time > in_between_time)
{
warm_up.heating_time = in_between_time;
warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder, during_printing);
}
warm_up.heating_time = warm_up.heating_time + extra_preheat_time;
return warm_up;
}
void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
{
// time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
constexpr bool during_printing = true;
double time_before_extruder_plan_end = 0.5 * preheat_config.getTimeToGoFromTempToTemp(extruder, prev_extruder_plan.printing_temperature, required_temp, during_printing);
time_before_extruder_plan_end = std::min(prev_extruder_plan.estimates.getTotalTime(), time_before_extruder_plan_end);
insertPreheatCommand(prev_extruder_plan, time_before_extruder_plan_end, extruder, required_temp);
}
void LayerPlanBuffer::handleStandbyTemp(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx, double standby_temp)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 2; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
if (extruder_plans[extruder_plan_before_idx]->extruder == extruder)
{
extruder_plans[extruder_plan_before_idx + 1]->prev_extruder_standby_temp = standby_temp;
return;
}
}
logWarning("Warning: Couldn't find previous extruder plan so as to set the standby temperature. Inserting temp command in earliest available layer.\n");
ExtruderPlan& earliest_extruder_plan = *extruder_plans[0];
constexpr bool wait = false;
earliest_extruder_plan.insertCommand(0, extruder, standby_temp, wait);
}
void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double initial_print_temp = extruder_plan.initial_printing_temperature;
Preheat::WarmUpResult heating_time_and_from_temp = timeBeforeExtruderPlanToInsert(extruder_plans, extruder_plan_idx);
if (heating_time_and_from_temp.total_time_window < preheat_config.getMinimalTimeWindow(extruder))
{
handleStandbyTemp(extruder_plans, extruder_plan_idx, initial_print_temp);
return; // don't insert preheat command and just stay on printing temperature
}
else
{
handleStandbyTemp(extruder_plans, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);
}
// handle preheat command
double time_before_extruder_plan_to_insert = heating_time_and_from_temp.heating_time;
for (unsigned int extruder_plan_before_idx = extruder_plan_idx - 1; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
{
ExtruderPlan& extruder_plan_before = *extruder_plans[extruder_plan_before_idx];
assert (extruder_plan_before.extruder != extruder);
double time_here = extruder_plan_before.estimates.getTotalTime();
if (time_here >= time_before_extruder_plan_to_insert)
{
insertPreheatCommand(extruder_plan_before, time_before_extruder_plan_to_insert, extruder, initial_print_temp);
return;
}
time_before_extruder_plan_to_insert -= time_here;
}
// time_before_extruder_plan_to_insert falls before all plans in the buffer
bool wait = false;
unsigned int path_idx = 0;
extruder_plans[0]->insertCommand(path_idx, extruder, initial_print_temp, wait); // insert preheat command at verfy beginning of buffer
}
void LayerPlanBuffer::insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx)
{
ExtruderPlan& extruder_plan = *extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
ExtruderPlan* prev_extruder_plan = extruder_plans[extruder_plan_idx - 1];
int prev_extruder = prev_extruder_plan->extruder;
if (prev_extruder != extruder)
{ // set previous extruder to standby temperature
extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(prev_extruder);
}
if (prev_extruder == extruder)
{
insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, extruder_plan.printing_temperature);
prev_extruder_plan->printing_temperature_command = --prev_extruder_plan->inserts.end();
}
else
{
insertPreheatCommand_multiExtrusion(extruder_plans, extruder_plan_idx);
insertFinalPrintTempCommand(extruder_plans, extruder_plan_idx - 1);
insertPrintTempCommand(extruder_plan);
}
}
void LayerPlanBuffer::insertPrintTempCommand(ExtruderPlan& extruder_plan)
{
unsigned int extruder = extruder_plan.extruder;
double print_temp = extruder_plan.printing_temperature;
double heated_pre_travel_time = 0;
if (preheat_config.getInitialPrintTemp(extruder) != 0)
{ // handle heating from initial_print_temperature to printing_tempreature
unsigned int path_idx;
for (path_idx = 0; path_idx < extruder_plan.getPaths().size(); path_idx++)
{
GCodePath& path = extruder_plan.getPaths()[path_idx];
heated_pre_travel_time += path.estimates.getTotalTime();
if (!path.isTravelPath())
{
break;
}
}
bool wait = false;
extruder_plan.insertCommand(path_idx, extruder, print_temp, wait);
}
extruder_plan.heated_pre_travel_time = heated_pre_travel_time;
}
void LayerPlanBuffer::insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx)
{
ExtruderPlan& last_extruder_plan = *extruder_plans[last_extruder_plan_idx];
int extruder = last_extruder_plan.extruder;
double final_print_temp = preheat_config.getFinalPrintTemp(extruder);
if (final_print_temp == 0)
{
return;
}
double heated_post_travel_time = 0; // The time after the last extrude move toward the end of the extruder plan during which the nozzle is stable at the final print temperature
{ // compute heated_post_travel_time
unsigned int path_idx;
for (path_idx = last_extruder_plan.getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = last_extruder_plan.getPaths()[path_idx];
if (!path.isTravelPath())
{
break;
}
heated_post_travel_time += path.estimates.getTotalTime();
}
}
double time_window = 0; // The time window within which the nozzle needs to heat from the initial print temp to the printing temperature and then back to the final print temp; i.e. from the first to the last extrusion move with this extruder
double weighted_average_print_temp = 0; // The average of the normal printing temperatures of the extruder plans (which might be different due to flow dependent temp or due to initial layer temp) Weighted by time
double initial_print_temp = -1; // The initial print temp of the first extruder plan with this extruder
{ // compute time window and print temp statistics
double heated_pre_travel_time = -1; // The time before the first extrude move from the start of the extruder plan during which the nozzle is stable at the initial print temperature
for (unsigned int prev_extruder_plan_idx = last_extruder_plan_idx; (int)prev_extruder_plan_idx >= 0; prev_extruder_plan_idx--)
{
ExtruderPlan& prev_extruder_plan = *extruder_plans[prev_extruder_plan_idx];
if (prev_extruder_plan.extruder != extruder)
{
break;
}
double prev_extruder_plan_time = prev_extruder_plan.estimates.getTotalTime();
time_window += prev_extruder_plan_time;
heated_pre_travel_time = prev_extruder_plan.heated_pre_travel_time;
if (prev_extruder_plan.estimates.getTotalUnretractedTime() > 0)
{ // handle temp statistics
assert(prev_extruder_plan.printing_temperature != -1 && "Previous extruder plan should already have a temperature planned");
weighted_average_print_temp += prev_extruder_plan.printing_temperature * prev_extruder_plan_time;
initial_print_temp = prev_extruder_plan.initial_printing_temperature;
}
}
weighted_average_print_temp /= time_window;
time_window -= heated_pre_travel_time + heated_post_travel_time;
assert(heated_pre_travel_time != -1 && "heated_pre_travel_time must have been computed; there must have been an extruder plan!");
}
assert((time_window >= 0 || last_extruder_plan.estimates.getMaterial() == 0) && "Time window should always be positive if we actually extrude");
// ,layer change .
// : ,precool command ,layer change .
// : ____: : ,precool command .
// :/ \ _____:_____: .
// _____/ \ / \ .
// / \ / \ .
// / / .
// / / .
// .
// approximate ^ by ^ .
// This approximation is quite ok since it only determines where to insert the precool temp command,
// which means the stable temperature of the previous extruder plan and the stable temperature of the next extruder plan couldn't be reached
constexpr bool during_printing = true;
Preheat::CoolDownResult warm_cool_result = preheat_config.getCoolDownPointAfterWarmUp(time_window, extruder, initial_print_temp, weighted_average_print_temp, final_print_temp, during_printing);
double cool_down_time = warm_cool_result.cooling_time;
assert(cool_down_time >= 0);
// find extruder plan in which to insert cooling command
ExtruderPlan* precool_extruder_plan = &last_extruder_plan;
{
for (unsigned int precool_extruder_plan_idx = last_extruder_plan_idx; (int)precool_extruder_plan_idx >= 0; precool_extruder_plan_idx--)
{
precool_extruder_plan = extruder_plans[precool_extruder_plan_idx];
if (precool_extruder_plan->printing_temperature_command)
{ // the precool command ends up before the command to go to the print temperature of the next extruder plan, so remove that print temp command
precool_extruder_plan->inserts.erase(*precool_extruder_plan->printing_temperature_command);
}
double time_here = precool_extruder_plan->estimates.getTotalTime();
if (cool_down_time < time_here)
{
break;
}
cool_down_time -= time_here;
}
}
// at this point cool_down_time is what time is left if cool down time of extruder plans after precool_extruder_plan (up until last_extruder_plan) are already taken into account
{ // insert temp command in precool_extruder_plan
double extrusion_time_seen = 0;
unsigned int path_idx;
for (path_idx = precool_extruder_plan->getPaths().size() - 1; int(path_idx) >= 0; path_idx--)
{
GCodePath& path = precool_extruder_plan->getPaths()[path_idx];
extrusion_time_seen += path.estimates.getTotalTime();
if (extrusion_time_seen >= cool_down_time)
{
break;
}
}
bool wait = false;
double time_after_path_start = extrusion_time_seen - cool_down_time;
precool_extruder_plan->insertCommand(path_idx, extruder, final_print_temp, wait, time_after_path_start);
}
}
void LayerPlanBuffer::insertTempCommands()
{
if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].getPathsList().empty()))
{ // disregard empty layer
buffer.pop_back();
return;
}
for (ExtruderPlan& plan: buffer.back().extruder_plans)
{
plan.convertListToVector();
}
std::vector<ExtruderPlan*> extruder_plans;
extruder_plans.reserve(buffer.size() * 2);
for (GCodePlanner& layer_plan : buffer)
{
for (ExtruderPlan& extr_plan : layer_plan.extruder_plans)
{
extruder_plans.push_back(&extr_plan);
}
}
// insert commands for all extruder plans on this layer
GCodePlanner& layer_plan = buffer.back();
for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
{
unsigned int overall_extruder_plan_idx = extruder_plans.size() - layer_plan.extruder_plans.size() + extruder_plan_idx;
ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
int extruder = extruder_plan.extruder;
double time = extruder_plan.estimates.getTotalUnretractedTime();
if (time <= 0.0)
{
continue;
}
double avg_flow = extruder_plan.estimates.getMaterial() / time;
extruder_plan.printing_temperature = preheat_config.getTemp(extruder, avg_flow, extruder_plan.is_initial_layer);
extruder_plan.initial_printing_temperature = preheat_config.getInitialPrintTemp(extruder);
if (extruder_plan.initial_printing_temperature == 0
|| !extruder_used_in_meshgroup[extruder]
|| (overall_extruder_plan_idx > 0 && extruder_plans[overall_extruder_plan_idx - 1]->extruder == extruder)
)
{
extruder_plan.initial_printing_temperature = extruder_plan.printing_temperature;
extruder_used_in_meshgroup[extruder] = true;
}
assert(extruder_plan.printing_temperature != -1 && "extruder_plan.printing_temperature should now have been set");
if (buffer.size() == 1 && extruder_plan_idx == 0)
{ // the very first extruder plan of the current meshgroup
int extruder = extruder_plan.extruder;
for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
{ // set temperature of the first nozzle, turn other nozzles down
if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
{
// override values from GCodeExport::setInitialTemps
// the first used extruder should be set to the required temp in the start gcode
// see FffGcodeWriter::processStartingCode
if (extruder_idx == extruder)
{
gcode.setInitialTemp(extruder_idx, extruder_plan.printing_temperature);
}
else
{
gcode.setInitialTemp(extruder_idx, preheat_config.getStandbyTemp(extruder_idx));
}
}
else
{
if (extruder_idx != extruder)
{ // TODO: do we need to do this?
extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(extruder_idx);
}
}
}
continue;
}
insertTempCommands(extruder_plans, overall_extruder_plan_idx);
}
}
} // namespace cura
-193
Ver Arquivo
@@ -1,193 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef LAYER_PLAN_BUFFER_H
#define LAYER_PLAN_BUFFER_H
#include <list>
#include "settings/settings.h"
#include "commandSocket.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "MeshGroup.h"
#include "Preheat.h"
namespace cura
{
/*!
* Class for buffering multiple layer plans (\ref GCodePlanner) / extruder plans within those layer plans, so that temperature commands can be inserted in earlier layer plans.
*
* This class handles where to insert temperature commands for:
* - initial layer temperature
* - flow dependent temperature
* - starting to heat up from the standby temperature
* - initial printing temperature | printing temperature | final printing temperature
*
* \image html assets/precool.png "Temperature Regulation" width=10cm
* \image latex assets/precool.png "Temperature Regulation" width=10cm
*
*/
class LayerPlanBuffer : SettingsMessenger
{
GCodeExport& gcode;
Preheat preheat_config; //!< the nozzle and material temperature settings for each extruder train.
static constexpr unsigned int buffer_size = 5; // should be as low as possible while still allowing enough time in the buffer to heat up from standby temp to printing temp // TODO: hardcoded value
// this value should be higher than 1, cause otherwise each layer is viewed as the first layer and no temp commands are inserted.
static constexpr const double extra_preheat_time = 1.0; //!< Time to start heating earlier than computed to avoid accummulative discrepancy between actual heating times and computed ones.
std::vector<bool> extruder_used_in_meshgroup; //!< For each extruder whether it has already been planned once in this meshgroup. This is used to see whether we should heat to the initial_print_temp or to the printing_temperature
public:
std::list<GCodePlanner> buffer; //!< The buffer containing several layer plans (GCodePlanner) before writing them to gcode.
LayerPlanBuffer(SettingsBaseVirtual* settings, GCodeExport& gcode)
: SettingsMessenger(settings)
, gcode(gcode)
, extruder_used_in_meshgroup(MAX_EXTRUDERS, false)
{ }
void setPreheatConfig(MeshGroup& settings)
{
preheat_config.setConfig(settings);
}
/*!
* Place a new layer plan (GcodePlanner) by constructing it with the given arguments.
*/
template<typename... Args>
GCodePlanner& createPlanner(Args&&... constructor_args)
{
if (buffer.size() > 0)
{
insertTempCommands(); // insert preheat commands of the just completed layer plan (not the newly emplaced one)
}
buffer.emplace_back(constructor_args...);
if (buffer.size() > buffer_size)
{
issueWriteGCode();
}
return buffer.back();
}
/*
* Write GCode for the oldest layer plan.
*/
void issueWriteGCode();
/*
* Pop back the oldest layer plan if it exceeds the buffer size and it has been written to gcode.
*/
void trimBuffer()
{
if (buffer.size() > buffer_size)
{
assert(buffer.front().isGCodeWritten() && "GCode should be written before planner is discarded");
buffer.pop_front();
}
}
/*!
* Write all remaining layer plans (GCodePlanner) to gcode and empty the buffer.
*/
void flush();
private:
/*!
* Insert the preheat command for @p extruder into @p extruder_plan_before
*
* \param extruder_plan_before An extruder plan before the extruder plan for which the temperature is computed, in which to insert the preheat command
* \param time_before_extruder_plan_end The time before the end of the extruder plan, before which to insert the preheat command
* \param extruder The extruder for which to set the temperature
* \param temp The temperature of the preheat command
*/
void insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_before_extruder_plan_end, int extruder, double temp);
/*!
* Compute the time needed to preheat, based either on the time the extruder has been on standby
* or based on the temp of the previous extruder plan which has the same extruder nr.
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed
* \return the time needed to preheat and the temperature from which heating starts
*/
Preheat::WarmUpResult timeBeforeExtruderPlanToInsert(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* For two consecutive extruder plans of the same extruder (so on different layers),
* preheat the extruder to the temperature corresponding to the average flow of the second extruder plan.
*
* The preheat commands are inserted such that the middle of the temperature change coincides with the start of the next layer.
*
* \param prev_extruder_plan The former extruder plan (of the former layer)
* \param extruder The extruder for which too set the temperature
* \param required_temp The required temperature for the second extruder plan
*/
void insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp);
/*!
* Insert the preheat command for an extruder plan which is preceded by an extruder plan with a different extruder.
* Find the time window in which this extruder hasn't been used
* and compute at what time the preheat command needs to be inserted.
* Then insert the preheat command in the right extruder plan.
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to find the preheat time needed
*/
void insertPreheatCommand_multiExtrusion(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the preheat command for the extruder plan corersponding to @p extruder_plan_idx of the layer corresponding to @p layer_plan_idx.
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans for which to generate the preheat command
*/
void insertTempCommands(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx);
/*!
* Insert the temperature command to heat from the initial print temperature to the printing temperature
*
* The temperature command is insert at the start of the very first extrusion move
*
* \param extruder_plan The extruder plan in which to insert the heat up command
*/
void insertPrintTempCommand(ExtruderPlan& extruder_plan);
/*!
* Insert the temp command to start cooling from the printing temperature to the final print temp
*
* The print temp is inserted before the last extrusion move of the extruder plan corresponding to \p last_extruder_plan_idx
*
* The command is inserted at a timed offset before the end of the last extrusion move
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param last_extruder_plan_idx The index of the last extruder plan in \p extruder_plans with the same extruder as previous extruder plans
*/
void insertFinalPrintTempCommand(std::vector<ExtruderPlan*>& extruder_plans, unsigned int last_extruder_plan_idx);
/*!
* Insert the preheat commands for the last added layer (unless that layer was empty)
*/
void insertTempCommands();
/*!
* Reconfigure the standby temperature during which we didn't print with this extruder.
* Find the previous extruder plan with the same extruder as layers[layer_plan_idx].extruder_plans[extruder_plan_idx]
* Set the prev_extruder_standby_temp in the next extruder plan
*
* \param extruder_plans The extruder plans in the buffer, moved to a temporary vector (from lower to upper layers)
* \param extruder_plan_idx The index of the extruder plan in \p extruder_plans before which to reconfigure the standby temperature
* \param standby_temp The temperature to which to cool down when the extruder is in standby mode.
*/
void handleStandbyTemp(std::vector<ExtruderPlan*>& extruder_plans, unsigned int extruder_plan_idx, double standby_temp);
};
} // namespace cura
#endif // LAYER_PLAN_BUFFER_H
-234
Ver Arquivo
@@ -1,234 +0,0 @@
#include "MergeInfillLines.h"
#include <algorithm> // min
#include "utils/linearAlg2D.h"
namespace cura
{
void MergeInfillLines::writeCompensatedMove(Point& to, double speed, GCodePath& last_path, int64_t new_line_width)
{
double old_line_width = INT2MM(last_path.config->getLineWidth());
double new_line_width_mm = INT2MM(new_line_width);
double extrusion_mod = new_line_width_mm / old_line_width;
double new_speed = speed;
if (speed_equalize_flow_enabled)
{
double speed_mod = old_line_width / new_line_width_mm;
new_speed = std::min(speed * speed_mod, speed_equalize_flow_max);
}
sendLineTo(last_path.config->type, to, last_path.getLineWidth());
gcode.writeMove(to, new_speed, last_path.getExtrusionMM3perMM() * extrusion_mod);
}
bool MergeInfillLines::mergeInfillLines(unsigned int& path_idx)
{ //Check for lots of small moves and combine them into one large line
Point prev_middle;
Point last_middle;
int64_t line_width;
if (isConvertible(path_idx, prev_middle, last_middle, line_width, false))
{
// path_idx + 3 is the index of the second extrusion move to be converted in combination with the first
{
GCodePath& move_path = paths[path_idx];
for(unsigned int point_idx = 0; point_idx < move_path.points.size() - 1; point_idx++)
{
gcode.writeMove(move_path.points[point_idx], move_path.config->getSpeed() * extruder_plan.getTravelSpeedFactor(), move_path.getExtrusionMM3perMM());
}
gcode.writeMove(prev_middle, travelConfig.getSpeed(), 0);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, last_path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(), last_path, line_width);
}
path_idx += 2;
extruder_plan.handleInserts(path_idx, gcode);
for (; isConvertible(path_idx, prev_middle, last_middle, line_width, true); path_idx += 2)
{
extruder_plan.handleInserts(path_idx, gcode);
GCodePath& last_path = paths[path_idx + 3];
writeCompensatedMove(last_middle, last_path.config->getSpeed() * extruder_plan.getExtrudeSpeedFactor(), last_path, line_width);
}
path_idx = path_idx + 1; // means that the next path considered is the travel path after the converted extrusion path corresponding to the updated path_idx
extruder_plan.handleInserts(path_idx, gcode);
return true;
}
return false;
};
bool MergeInfillLines::isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
unsigned int idx = path_idx_first_move;
if (idx + 3 > paths.size()-1)
{
return false;
}
if ( paths[idx+0].config != &travelConfig // must be travel
|| paths[idx+1].points.size() > 1 // extrusion path is single line
|| paths[idx+1].config == &travelConfig // must be extrusion
// || paths[idx+2].points.size() > 1 // travel must be direct
|| paths[idx+2].config != &travelConfig // must be travel
|| paths[idx+3].points.size() > 1 // extrusion path is single line
|| paths[idx+3].config == &travelConfig // must be extrusion
|| paths[idx+1].config != paths[idx+3].config // both extrusion moves should have the same config
)
{
return false;
}
if (!(paths[idx+1].config->type == PrintFeatureType::Infill || paths[idx+1].config->type == PrintFeatureType::Skin))
{ // only (skin) infill lines can be merged (note that the second extrusion line config is already checked to be the same as the first in code above)
return false;
}
if (paths[idx+1].space_fill_type != SpaceFillType::Lines || paths[idx+3].space_fill_type != SpaceFillType::Lines)
{ // both extrusion moves must be of lines space filling type!
return false;
}
int64_t line_width = paths[idx+1].config->getLineWidth();
Point& a = paths[idx+0].points.back(); // first extruded line from
Point& b = paths[idx+1].points.back(); // first extruded line to
Point& c = paths[idx+2].points.back(); // second extruded line from
Point& d = paths[idx+3].points.back(); // second extruded line to
return isConvertible(a, b, c, d, line_width, first_middle, second_middle, resulting_line_width, use_second_middle_as_first);
}
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
use_second_middle_as_first = false;
int64_t max_line_width = nozzle_size * 3 / 2;
Point ab = b - a;
Point cd = d - c;
if (b == c)
{
return false; // the line segments are connected!
}
int64_t ab_size = vSize(ab);
int64_t cd_size = vSize(cd);
if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
{
return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
}
// if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
int64_t prod = dot(ab,cd);
if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
{
return false; // extrusion moves not in the same or opposite diraction
}
// make lines in the same direction by flipping one
if (prod < 0)
{
ab = ab * -1;
}
else if (prod == 0)
{
return false; // lines are orthogonal!
}
else if (b == d || a == c)
{
return false; // the line segments are connected!
}
first_middle = (use_second_middle_as_first)?
second_middle :
(a + b) / 2;
second_middle = (c + d) / 2;
Point dir_vector_perp = turn90CCW(second_middle - first_middle);
int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
if (dir_vector_perp_length == 0)
{
return false;
}
if (dir_vector_perp_length > 5 * nozzle_size)
{
return false; // infill lines too far apart
}
Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill
// compute the resulting line width
resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
if (resulting_line_width > max_line_width)
{
return false; // combined lines would be too wide
}
if (resulting_line_width == 0)
{
return false; // dot is zero, so lines are in each others extension, not next to eachother
}
// check whether two lines are adjacent (note: not 'line segments' but 'lines')
Point ac = c - first_middle;
Point infill_vector_perp = turn90CCW(infill_vector);
int64_t perp_proj = dot(ac, infill_vector_perp);
int64_t infill_vector_perp_length = vSize(infill_vector_perp);
if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
{
return false; // lines are too far apart or too close together
}
// check whether the two line segments are adjacent.
// full infill in a narrow area might result in line segments with arbitrary distance between them
// the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
// however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
{
return false;
}
return true;
};
/*
void MergeInfillLines::merge(Point& from, Point& p0, Point& p1)
{ //Check for lots of small moves and combine them into one large line
if (path->points.size() == 1 && path->config != &travelConfig); // && shorterThen(from - path->points[0], path->config->getLineWidth() * 2))
{
Point p0 = path->points[0];
unsigned int path_idx_last = path_idx + 1; // index of the last short move
while(path_idx_last < paths.size() && paths[path_idx_last].points.size() == 1 && shorterThen(p0 - paths[path_idx_last].points[0], path->config->getLineWidth() * 2))
{
p0 = paths[path_idx_last].points[0];
path_idx_last ++;
}
if (paths[path_idx_last-1].config == &travelConfig)
path_idx_last --;
if (path_idx_last > path_idx + 2)
{
p0 = from;
for(unsigned int path_idx_short = path_idx; path_idx_short < path_idx_last-1; path_idx_short+=2)
{
int64_t oldLen = vSize(p0 - paths[path_idx_short].points[0]);
Point newPoint = (paths[path_idx_short].points[0] + paths[path_idx_short+1].points[0]) / 2;
int64_t newLen = vSize(from - newPoint);
if (newLen > 0)
{
if (oldLen > 0)
gcode.writeMove(newPoint, speed * oldLen / newLen, path->getExtrusionMM3perMM() * newLen / oldLen);
else
gcode.writeMove(newPoint, speed, path->getExtrusionMM3perMM());
}
}
gcode.writeMove(paths[path_idx_last-1].points[0], speed, path->getExtrusionMM3perMM());
path_idx = path_idx_last - 1;
continue;
}
}
}*/
}//namespace cura
-93
Ver Arquivo
@@ -1,93 +0,0 @@
#ifndef MERGE_INFILL_LINES_H
#define MERGE_INFILL_LINES_H
#include "utils/intpoint.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "GCodePathConfig.h"
namespace cura
{
class MergeInfillLines
{
// void merge(Point& from, Point& p0, Point& p1);
GCodeExport& gcode; //!< Where to write the combined line to
int layer_nr; //!< The current layer number
std::vector<GCodePath>& paths; //!< The paths currently under consideration
ExtruderPlan& extruder_plan; //!< The extruder plan of the paths currently under consideration
GCodePathConfig& travelConfig; //!< The travel settings used to see whether a path is a travel path or an extrusion path
int64_t nozzle_size; //!< The diameter of the hole in the nozzle
bool speed_equalize_flow_enabled; //!< Should the speed be varied with extrusion width
double speed_equalize_flow_max; //!< Maximum speed when adjusting speed for flow
/*!
* Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
* \param path_idx_first_move Index into MergeInfillLines::paths to the travel before the two extrusion moves udner consideration
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(unsigned int path_idx_first_move, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Whether the two consecutive extrusion paths (ab and cd) are convitrible to a single line segment.
*
* Note: as an optimization the \p second_middle from the previous call to isConvertible can be used for \p first_middle, instead of recomputing it.
*
* \param a first from
* \param b first to
* \param c second from
* \param d second to
* \param line_width The line width of the moves
* \param first_middle Output parameter: the middle of the first extrusion move
* \param second_middle Input/Output parameter: outputs the middle of the second extrusion move; inputs \p first_middle so we don't have to compute it
* \param resulting_line_width Output parameter: The width of the resulting combined line (the average length of the lines combined)
* \param use_second_middle_as_first Whether to use \p second_middle as input parameter for \p first_middle
* \return Whether the next two extrusion paths are convertible to a single line segment, starting from the end point the of the last travel move at \p path_idx_first_move
*/
bool isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first = false);
/*!
* Write an extrusion move with compensated width and compensated speed so that the material flow will be the same.
*
* \param to The point to move to
* \param speed The original speed
* \param old_path The original path
* \param new_line_width The width of the convewrted line (approximately the length of the original line)
*/
void writeCompensatedMove(Point& to, double speed, GCodePath& old_path, int64_t new_line_width);
public:
/*!
* Simple constructor only used by MergeInfillLines::isConvertible to easily convey the environment
*/
MergeInfillLines(GCodeExport& gcode, int layer_nr, std::vector<GCodePath>& paths, ExtruderPlan& extruder_plan, GCodePathConfig& travelConfig, int64_t nozzle_size, bool speed_equalize_flow_enabled, double speed_equalize_flow_max)
: gcode(gcode), layer_nr(layer_nr), paths(paths), extruder_plan(extruder_plan), travelConfig(travelConfig), nozzle_size(nozzle_size), speed_equalize_flow_enabled(speed_equalize_flow_enabled), speed_equalize_flow_max(speed_equalize_flow_max) { }
/*!
* Check for lots of small moves and combine them into one large line.
* Updates \p path_idx to the next path which is not combined.
*
* \param gcode Where to write the combined line to
* \param paths The paths currently under consideration
* \param travelConfig The travel settings used to see whether a path is a travel path or an extrusion path
* \param nozzle_size The diameter of the hole in the nozzle
* \param path_idx Input/Output parameter: The current index in \p paths where to start combining and the current index after combining as output parameter.
* \return Whether lines have been merged and normal path-to-gcode generation can be skipped for the current resulting \p path_idx .
*/
bool mergeInfillLines(unsigned int& path_idx);
/*!
* send a line segment through the command socket from the previous point to the given point \p to
*/
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width)
{
CommandSocket::sendLineTo(print_feature_type, to, line_width);
}
};
}//namespace cura
#endif // MERGE_INFILL_LINES_H
-350
Ver Arquivo
@@ -1,350 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include "MeshGroup.h"
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/string.h"
#include "settings/SettingRegistry.h" // loadExtruderJSONsettings
namespace cura
{
FILE* binaryMeshBlob = nullptr;
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
void* fgets_(char* ptr, size_t len, FILE* f)
{
while(len && fread(ptr, 1, 1, f) > 0)
{
if (*ptr == '\n' || *ptr == '\r')
{
*ptr = '\0';
return ptr;
}
ptr++;
len--;
}
return nullptr;
}
MeshGroup::MeshGroup(SettingsBaseVirtual* settings_base)
: SettingsBase(settings_base)
, extruder_count(-1)
{}
MeshGroup::~MeshGroup()
{
for (unsigned int extruder = 0; extruder < MAX_EXTRUDERS; extruder++)
{
if (extruders[extruder])
{
delete extruders[extruder];
}
}
}
int MeshGroup::getExtruderCount() const
{
if (extruder_count == -1)
{
extruder_count = getSettingAsCount("machine_extruder_count");
}
return extruder_count;
}
ExtruderTrain* MeshGroup::createExtruderTrain(unsigned int extruder_nr)
{
assert((int)extruder_nr >= 0 && (int)extruder_nr < getSettingAsCount("machine_extruder_count") && "only valid extruder trains may be requested!");
if (!extruders[extruder_nr])
{
extruders[extruder_nr] = new ExtruderTrain(this, extruder_nr);
int err = SettingRegistry::getInstance()->loadExtruderJSONsettings(extruder_nr, extruders[extruder_nr]);
if (err)
{
logError("Couldn't load extruder.def.json for extruder %i\n", extruder_nr);
std::exit(1);
}
}
return extruders[extruder_nr];
}
ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr)
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
const ExtruderTrain* MeshGroup::getExtruderTrain(unsigned int extruder_nr) const
{
assert(extruders[extruder_nr]);
return extruders[extruder_nr];
}
Point3 MeshGroup::min() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 MeshGroup::max() const
{
if (meshes.size() < 1)
{
return Point3(0, 0, 0);
}
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void MeshGroup::clear()
{
for(Mesh& m : meshes)
{
m.clear();
}
}
void MeshGroup::finalize()
{
extruder_count = getSettingAsCount("machine_extruder_count");
for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
{
createExtruderTrain(extruder_nr); // create it if it didn't exist yet
if (getSettingAsIndex("adhesion_extruder_nr") == extruder_nr && getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
{
getExtruderTrain(extruder_nr)->setIsUsed(true);
continue;
}
for (const Mesh& mesh : meshes)
{
if (mesh.getSettingBoolean("support_enable")
&& (
getSettingAsIndex("support_infill_extruder_nr") == extruder_nr
|| getSettingAsIndex("support_extruder_nr_layer_0") == extruder_nr
|| (getSettingBoolean("support_interface_enable") && getSettingAsIndex("support_interface_extruder_nr") == extruder_nr)
)
)
{
getExtruderTrain(extruder_nr)->setIsUsed(true);
break;
}
}
}
for (const Mesh& mesh : meshes)
{
if (!mesh.getSettingBoolean("anti_overhang_mesh")
&& !mesh.getSettingBoolean("support_mesh")
)
{
getExtruderTrain(mesh.getSettingAsIndex("extruder_nr"))->setIsUsed(true);
}
}
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
Point3 meshgroup_offset(0, 0, 0);
if (!getSettingBoolean("machine_center_is_zero"))
{
meshgroup_offset.x = getSettingInMicrons("machine_width") / 2;
meshgroup_offset.y = getSettingInMicrons("machine_depth") / 2;
}
// If a mesh position was given, put the mesh at this position in 3D space.
for(Mesh& mesh : meshes)
{
Point3 mesh_offset(mesh.getSettingInMicrons("mesh_position_x"), mesh.getSettingInMicrons("mesh_position_y"), mesh.getSettingInMicrons("mesh_position_z"));
if (mesh.getSettingBoolean("center_object"))
{
Point3 object_min = mesh.min();
Point3 object_max = mesh.max();
Point3 object_size = object_max - object_min;
mesh_offset += Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
}
mesh.offset(mesh_offset + meshgroup_offset);
}
}
bool loadMeshSTL_ascii(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rt");
char buffer[1024];
FPoint3 vertex;
int n = 0;
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
while(fgets_(buffer, sizeof(buffer), f))
{
if (sscanf(buffer, " vertex %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
{
n++;
switch(n)
{
case 1:
v0 = matrix.apply(vertex);
break;
case 2:
v1 = matrix.apply(vertex);
break;
case 3:
v2 = matrix.apply(vertex);
mesh->addFace(v0, v1, v2);
n = 0;
break;
}
}
}
fclose(f);
mesh->finish();
return true;
}
bool loadMeshSTL_binary(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
fseek(f, 0L, SEEK_END);
long long file_size = ftell(f); //The file size is the position of the cursor after seeking to the end.
rewind(f); //Seek back to start.
size_t face_count = (file_size - 80 - sizeof(uint32_t)) / 50; //Subtract the size of the header. Every face uses exactly 50 bytes.
char buffer[80];
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return false;
}
uint32_t reported_face_count;
//Read the face count. We'll use it as a sort of redundancy code to check for file corruption.
if (fread(&reported_face_count, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return false;
}
if (reported_face_count != face_count)
{
logWarning("Face count reported by file (%s) is not equal to actual face count (%s). File could be corrupt!\n", std::to_string(reported_face_count).c_str(), std::to_string(face_count).c_str());
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
// Every Face is 50 Bytes: Normal(3*float), Vertices(9*float), 2 Bytes Spacer
mesh->faces.reserve(face_count);
mesh->vertices.reserve(face_count);
for (unsigned int i = 0; i < face_count; i++)
{
if (fread(buffer, 50, 1, f) != 1)
{
fclose(f);
return false;
}
float *v= ((float*)buffer)+3;
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
mesh->addFace(v0, v1, v2);
}
fclose(f);
mesh->finish();
return true;
}
bool loadMeshSTL(Mesh* mesh, const char* filename, const FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
if (f == nullptr)
{
return false;
}
//Skip any whitespace at the beginning of the file.
unsigned long long num_whitespace = 0; //Number of whitespace characters.
unsigned char whitespace;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
while(isspace(whitespace))
{
num_whitespace++;
if (fread(&whitespace, 1, 1, f) != 1)
{
fclose(f);
return false;
}
}
fseek(f, num_whitespace, SEEK_SET); //Seek to the place after all whitespace (we may have just read too far).
char buffer[6];
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
return false;
}
fclose(f);
buffer[5] = '\0';
if (stringcasecompare(buffer, "solid") == 0)
{
bool load_success = loadMeshSTL_ascii(mesh, filename, matrix);
if (!load_success)
return false;
// This logic is used to handle the case where the file starts with
// "solid" but is a binary file.
if (mesh->faces.size() < 1)
{
mesh->clear();
return loadMeshSTL_binary(mesh, filename, matrix);
}
return true;
}
return loadMeshSTL_binary(mesh, filename, matrix);
}
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings)
{
TimeKeeper load_timer;
const char* ext = strrchr(filename, '.');
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
{
Mesh mesh = object_parent_settings ? Mesh(object_parent_settings) : Mesh(meshgroup); //If we have object_parent_settings, use them as parent settings. Otherwise, just use meshgroup.
if(loadMeshSTL(&mesh,filename,transformation)) //Load it! If successful...
{
meshgroup->meshes.push_back(mesh);
log("loading '%s' took %.3f seconds\n",filename,load_timer.restart());
return true;
}
}
return false;
}
}//namespace cura
-60
Ver Arquivo
@@ -1,60 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MESH_GROUP_H
#define MESH_GROUP_H
#include "utils/NoCopy.h"
#include "mesh.h"
#include "ExtruderTrain.h"
namespace cura
{
/*!
* A MeshGroup is a collection with 1 or more 3D meshes.
*
* One MeshGroup is a whole which is printed at once.
* Generally there is one single MeshGroup, though when using one-at-a-time printing, multiple MeshGroups are processed consecutively.
*/
class MeshGroup : public SettingsBase, NoCopy
{
ExtruderTrain* extruders[MAX_EXTRUDERS] = {nullptr};
mutable int extruder_count; //!< The number of extruders. (mutable because of lazy evaluation)
public:
int getExtruderCount() const;
MeshGroup(SettingsBaseVirtual* settings_base);
~MeshGroup();
/*!
* Create a new extruder train for the @p extruder_nr, or return the one which already exists.
*/
ExtruderTrain* createExtruderTrain(unsigned int extruder_nr);
ExtruderTrain* getExtruderTrain(unsigned int extruder_nr);
const ExtruderTrain* getExtruderTrain(unsigned int extruder_nr) const;
std::vector<Mesh> meshes;
Point3 min() const; //! minimal corner of bounding box
Point3 max() const; //! maximal corner of bounding box
void clear();
void finalize();
};
/*!
* Load a Mesh from file and store it in the \p meshgroup.
*
* \param meshgroup The meshgroup where to store the mesh
* \param filename The filename of the mesh file
* \param transformation The transformation applied to all vertices
* \param object_parent_settings (optional) The parent settings object of the new mesh. Defaults to \p meshgroup if none is given.
* \return whether the file could be loaded
*/
bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const FMatrix3x3& transformation, SettingsBaseVirtual* object_parent_settings = nullptr);
}//namespace cura
#endif//MESH_GROUP_H
-184
Ver Arquivo
@@ -1,184 +0,0 @@
#include "Preheat.h"
namespace cura
{
void Preheat::setConfig(const MeshGroup& meshgroup)
{
for (int extruder_nr = 0; extruder_nr < meshgroup.getExtruderCount(); extruder_nr++)
{
assert(meshgroup.getExtruderTrain(extruder_nr) != nullptr);
const ExtruderTrain& extruder_train = *meshgroup.getExtruderTrain(extruder_nr);
config_per_extruder.emplace_back();
Config& config = config_per_extruder.back();
double machine_nozzle_cool_down_speed = extruder_train.getSettingInSeconds("machine_nozzle_cool_down_speed");
double machine_nozzle_heat_up_speed = extruder_train.getSettingInSeconds("machine_nozzle_heat_up_speed");
double material_extrusion_cool_down_speed = extruder_train.getSettingInSeconds("material_extrusion_cool_down_speed");
assert(material_extrusion_cool_down_speed < machine_nozzle_heat_up_speed && "The extrusion cooldown speed must be smaller than the heat up speed; otherwise the printing temperature cannot be reached!");
config.time_to_cooldown_1_degree[0] = 1.0 / machine_nozzle_cool_down_speed;
config.time_to_heatup_1_degree[0] = 1.0 / machine_nozzle_heat_up_speed;
config.time_to_cooldown_1_degree[1] = 1.0 / (machine_nozzle_cool_down_speed + material_extrusion_cool_down_speed);
config.time_to_heatup_1_degree[1] = 1.0 / (machine_nozzle_heat_up_speed - material_extrusion_cool_down_speed);
config.standby_temp = extruder_train.getSettingInSeconds("material_standby_temperature");
config.min_time_window = extruder_train.getSettingInSeconds("machine_min_cool_heat_time_window");
config.material_print_temperature = extruder_train.getSettingInDegreeCelsius("material_print_temperature");
config.material_print_temperature_layer_0 = extruder_train.getSettingInDegreeCelsius("material_print_temperature_layer_0");
config.material_initial_print_temperature = extruder_train.getSettingInDegreeCelsius("material_initial_print_temperature");
config.material_final_print_temperature = extruder_train.getSettingInDegreeCelsius("material_final_print_temperature");
config.flow_dependent_temperature = extruder_train.getSettingBoolean("material_flow_dependent_temperature");
config.flow_temp_graph = extruder_train.getSettingAsFlowTempGraph("material_flow_temp_graph"); // [[0.1,180],[20,230]]
}
}
double Preheat::getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing)
{
Config& config = config_per_extruder[extruder];
double time;
if (temp_after > temp_before)
{
time = (temp_after - temp_before) * config.time_to_heatup_1_degree[during_printing];
}
else
{
time = (temp_before - temp_after) * config.time_to_cooldown_1_degree[during_printing];
}
return std::max(0.0, time);
}
double Preheat::getTemp(unsigned int extruder, double flow, bool is_initial_layer)
{
if (is_initial_layer && config_per_extruder[extruder].material_print_temperature_layer_0 != 0)
{
return config_per_extruder[extruder].material_print_temperature_layer_0;
}
return config_per_extruder[extruder].flow_temp_graph.getTemp(flow, config_per_extruder[extruder].material_print_temperature, config_per_extruder[extruder].flow_dependent_temperature);
}
Preheat::WarmUpResult Preheat::getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
WarmUpResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
result.total_time_window = time_window;
// ,temp_end
// / .
// ,temp_start / .
// \ ' ' ' ' '/ ' ' '> outer_temp .
// \________/ .
// "-> temp_mid
// ^^^^^^^^^^
// limited_time_window
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.heating_time = extra_heatup_time;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_start;
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.heating_time = 0;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_end;
}
if (limited_time_window < 0.0)
{
result.heating_time = 0.0;
result.lowest_temperature = std::min(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double time_to_heat_from_standby_to_print_temp = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_standby_temp = time_to_heat_from_standby_to_print_temp * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_standby_temp < limited_time_window)
{
result.heating_time += time_to_heat_from_standby_to_print_temp;
result.lowest_temperature = temp_mid;
}
else
{
result.heating_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.lowest_temperature = std::max(temp_mid, temp_end - result.heating_time / time_to_heatup_1_degree);
}
if (result.heating_time > time_window || result.heating_time < 0.0)
{
logWarning("getWarmUpPointAfterCoolDown returns result outside of the time window!");
}
return result;
}
Preheat::CoolDownResult Preheat::getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing)
{
CoolDownResult result;
const Config& config = config_per_extruder[extruder];
double time_to_cooldown_1_degree = config.time_to_cooldown_1_degree[during_printing];
double time_to_heatup_1_degree = config.time_to_heatup_1_degree[during_printing];
assert(temp_start != -1 && temp_mid != -1 && temp_end != -1 && "temperatures must be initialized!");
result.total_time_window = time_window;
// limited_time_window
// :^^^^^^^^^^^^:
// : ________. : . . .> temp_mid
// : / \ : .
// :/ . . . . .\:. . .> outer_temp .
// ^temp_start \ .
// \ .
// ^temp_end
double outer_temp;
double limited_time_window;
if (temp_start < temp_end)
{ // extra time needed during heating
double extra_heatup_time = (temp_end - temp_start) * time_to_heatup_1_degree;
result.cooling_time = 0;
limited_time_window = time_window - extra_heatup_time;
outer_temp = temp_end;
}
else
{
double extra_cooldown_time = (temp_start - temp_end) * time_to_cooldown_1_degree;
result.cooling_time = extra_cooldown_time;
limited_time_window = time_window - extra_cooldown_time;
outer_temp = temp_start;
}
if (limited_time_window < 0.0)
{
result.cooling_time = 0.0;
result.highest_temperature = std::max(temp_start, temp_end);
return result;
}
double time_ratio_cooldown_heatup = time_to_cooldown_1_degree / time_to_heatup_1_degree;
double cool_down_time = getTimeToGoFromTempToTemp(extruder, temp_mid, outer_temp, during_printing);
double time_needed_to_reach_temp1 = cool_down_time * (1.0 + time_ratio_cooldown_heatup);
if (time_needed_to_reach_temp1 < limited_time_window)
{
result.cooling_time += cool_down_time;
result.highest_temperature = temp_mid;
}
else
{
result.cooling_time += limited_time_window * time_to_heatup_1_degree / (time_to_cooldown_1_degree + time_to_heatup_1_degree);
result.highest_temperature = std::min(temp_mid, temp_end + result.cooling_time / time_to_cooldown_1_degree);
}
if (result.cooling_time > time_window || result.cooling_time < 0.0)
{
logWarning("getCoolDownPointAfterWarmUp returns result outside of the time window!");
}
return result;
}
}//namespace cura
-202
Ver Arquivo
@@ -1,202 +0,0 @@
#ifndef PREHEAT_H
#define PREHEAT_H
#include <cassert>
#include <algorithm> // max
#include "utils/logoutput.h"
#include "MeshGroup.h"
#include "FlowTempGraph.h"
namespace cura
{
/*!
* Class for computing heatup and cooldown times used for computing the time the printer needs to heat up to a printing temperature.
*/
class Preheat
{
/*!
* The nozzle and material temperature settings for an extruder train.
*/
class Config
{
public:
double time_to_heatup_1_degree[2]; //!< average time it takes to heat up one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double time_to_cooldown_1_degree[2]; //!< average time it takes to cool down one degree (in the range of normal print temperatures and standby temperature), while not-printing and while printing
double standby_temp; //!< The temperature at which the nozzle rests when it is not printing.
double min_time_window; //!< Minimal time (in seconds) to allow an extruder to cool down and then warm up again.
double material_print_temperature; //!< default print temp (backward compatilibily)
double material_print_temperature_layer_0; //!< initial layer print temp
double material_initial_print_temperature; //!< print temp when first starting to extrude after a layer switch
double material_final_print_temperature; //!< print temp at the end of all extrusion moves of an extruder to which it's cooled down just before - during the extrusion
bool flow_dependent_temperature; //!< Whether to make the temperature dependent on flow
FlowTempGraph flow_temp_graph; //!< The graph linking flows to corresponding temperatures
};
std::vector<Config> config_per_extruder;//!< the nozzle and material temperature settings for each extruder train.
public:
/*!
* The type of result when computing when to start heating up a nozzle before it's going to be used again.
*/
struct WarmUpResult
{
double total_time_window; //!< The total time in which cooling and heating takes place.
double heating_time; //!< The total time needed to heat to the required temperature.
double lowest_temperature; //!< The lower temperature from which heating starts.
};
/*!
* The type of result when computing when to start cooling down a nozzle before it's not going to be used again.
*/
struct CoolDownResult
{
double total_time_window; //!< The total time in which heating and cooling takes place.
double cooling_time; //!< The total time needed to cool down to the required temperature.
double highest_temperature; //!< The upper temperature from which cooling starts.
};
/*!
* Get the standby temperature of an extruder train
* \param extruder the extruder train for which to get the standby tmep
* \return the standby temp
*/
double getStandbyTemp(int extruder)
{
return config_per_extruder[extruder].standby_temp;
}
/*!
* Get the time it takes to heat up one degree celsius
*
* \param extruder the extruder train for which to get time it takes to heat up one degree celsius
* \param during_printing whether the heating takes time during printing or when idle
* \return the time it takes to heat up one degree celsius
*/
double getTimeToHeatup1Degree(int extruder, bool during_printing)
{
return config_per_extruder[extruder].time_to_heatup_1_degree[during_printing];
}
/*!
* Get the initial print temperature when starting to extrude.
* \param during_printing whether the heating takes time during printing or when idle
*/
double getInitialPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_initial_print_temperature;
}
/*!
* Get the final print temperature at the end of all extrusion moves with the current extruder
*/
double getFinalPrintTemp(int extruder)
{
return config_per_extruder[extruder].material_final_print_temperature;
}
/*!
* Set the nozzle and material temperature settings for each extruder train.
* \param meshgroup Where to get settings from
*/
void setConfig(const MeshGroup& meshgroup);
bool usesFlowDependentTemp(int extruder_nr)
{
return config_per_extruder[extruder_nr].flow_dependent_temperature;
}
/*!
* Get the optimal temperature corresponding to a given average flow,
* or the initial layer temperature.
*
* \param extruder The extruder train
* \param flow The flow for which to get the optimal temperature
* \param is_initial_layer Whether the initial layer temperature should be returned instead of flow-based temperature
* \return The corresponding optimal temperature
*/
double getTemp(unsigned int extruder, double flow, bool is_initial_layer);
/*!
* Return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
* \param extruder The extruder for which to get the minimal time window
* \return the minimal time window of a specific extruder for letting an unused extruder cool down and warm up again
*/
double getMinimalTimeWindow(unsigned int extruder)
{
return config_per_extruder[extruder].min_time_window;
}
/*!
* Decide when to start warming up again after starting to cool down towards \p temp_mid.
* Two cases are considered:
* the case where the standby temperature is reached \__/ .
* and the case where it isn't \/ .
*
* \warning it is assumed that \p temp_mid is lower than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
// ,temp_end
// / .
// ,temp_start / .
// \ / .
// \________/ .
// "-> temp_mid
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp_start The temperature from which to start cooling down
* \param temp_mid The temeprature to which we try to cool down
* \param temp_end The temperature to which we need to have heated up at the end of the \p time_window
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the @p time_window to insert the preheat command and the temperature from which the heating starts
*/
WarmUpResult getWarmUpPointAfterCoolDown(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* Decide when to start cooling down again after starting to warm up towards the \p temp_mid
* Two cases are considered:
* the case where the temperature is reached /"""\ .
* and the case where it isn't /\ .
*
* \warning it is assumed that \p temp_mid is higher than both \p temp_start and \p temp_end. If not somewhat weird results may follow.
*
// _> temp_mid
// /""""""""\ .
// / \ .
// ^temp_start \ .
// \ .
// ^temp_end
* \param window_time The time window within which the cooldown and heat up must take place.
* \param extruder The extruder used
* \param temp_start The temperature from which to start heating up
* \param temp_mid The temeprature to which we try to heat up
* \param temp_end The temperature to which we need to have cooled down after \p time_window time
* \param during_printing Whether the warming up and cooling down is performed during printing
* \return The time before the end of the \p time_window to insert the preheat command and the temperature from which the cooling starts
*/
CoolDownResult getCoolDownPointAfterWarmUp(double time_window, unsigned int extruder, double temp_start, double temp_mid, double temp_end, bool during_printing);
/*!
* Get the time to go from one temperature to another temperature
* \param extruder The extruder number for which to perform the heatup / cooldown
* \param temp_before The before temperature
* \param temp_after The after temperature
* \param during_printing Whether the planned cooldown / warmup occurs during printing or while in standby mode
* \return The time needed
*/
double getTimeToGoFromTempToTemp(int extruder, double temp_before, double temp_after, bool during_printing);
};
} // namespace cura
#endif // PREHEAT_H
-309
Ver Arquivo
@@ -1,309 +0,0 @@
#include "PrimeTower.h"
#include <limits>
#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
#include "gcodePlanner.h"
#include "infill.h"
#include "PrintFeature.h"
namespace cura
{
PrimeTower::PrimeTower(const SliceDataStorage& storage)
: is_hollow(false)
, wipe_from_middle(false)
, current_pre_wipe_location_idx(0)
{
for (int extruder_nr = 0; extruder_nr < MAX_EXTRUDERS; extruder_nr++)
{
last_prime_tower_poly_printed[extruder_nr] = -1;
}
enabled = storage.getSettingBoolean("prime_tower_enable")
&& storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
&& storage.getSettingInMicrons("prime_tower_size") > 10;
if (enabled)
{
generateGroundpoly(storage);
}
}
void PrimeTower::initConfigs(const MeshGroup* meshgroup)
{
extruder_count = meshgroup->getExtruderCount();
for (int extr = 0; extr < extruder_count; extr++)
{
config_per_extruder.emplace_back(PrintFeatureType::Support);// so that visualization in the old Cura still works (TODO)
}
for (int extr = 0; extr < extruder_count; extr++)
{
const ExtruderTrain* train = meshgroup->getExtruderTrain(extr);
config_per_extruder[extr].init(train->getSettingInMillimetersPerSecond("speed_prime_tower"), train->getSettingInMillimetersPerSecond("acceleration_prime_tower"), train->getSettingInMillimetersPerSecond("jerk_prime_tower"), train->getSettingInMicrons("prime_tower_line_width"), train->getSettingInPercentage("prime_tower_flow"));
}
}
void PrimeTower::setConfigs(const MeshGroup* meshgroup, const int layer_thickness)
{
extruder_count = meshgroup->getExtruderCount();
for (int extr = 0; extr < extruder_count; extr++)
{
GCodePathConfig& conf = config_per_extruder[extr];
conf.setLayerHeight(layer_thickness);
}
}
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
extruder_count = storage.meshgroup->getExtruderCount();
int64_t prime_tower_wall_thickness = storage.getSettingInMicrons("prime_tower_wall_thickness");
int64_t tower_size = storage.getSettingInMicrons("prime_tower_size");
if (prime_tower_wall_thickness * 2 < tower_size)
{
is_hollow = true;
}
PolygonRef p = ground_poly.newPoly();
int tower_distance = 0;
int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
p.add(Point(x + tower_distance, y + tower_distance));
p.add(Point(x + tower_distance, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
p.add(Point(x + tower_distance - tower_size, y + tower_distance));
middle = Point(x - tower_size / 2, y + tower_size / 2);
if (is_hollow)
{
ground_poly = ground_poly.difference(ground_poly.offset(-prime_tower_wall_thickness));
}
post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
enabled &= storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches.
if (enabled)
{
generatePaths_denseInfill(storage);
generateWipeLocations(storage);
}
}
void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
{
int n_patterns = 2; // alternating patterns between layers
int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
int extra_infill_shift = 0;
int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position
for (int extruder = 0; extruder < extruder_count; extruder++)
{
int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
patterns_per_extruder.emplace_back(n_patterns);
std::vector<ExtrusionMoves>& patterns = patterns_per_extruder.back();
patterns.resize(n_patterns);
for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
{
patterns[pattern_idx].polygons = ground_poly.offset(-line_width / 2);
Polygons& result_lines = patterns[pattern_idx].lines;
int outline_offset = -line_width;
int line_distance = line_width;
double fill_angle = 45 + pattern_idx * 90;
Polygons result_polygons; // should remain empty, since we generate lines pattern!
Infill infill_comp(EFillMethod::LINES, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
infill_comp.generate(result_polygons, result_lines);
}
}
}
void PrimeTower::addToGcode(const SliceDataStorage& storage, GCodePlanner& gcodeLayer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder)
{
if (!enabled)
{
return;
}
bool prime_tower_added = false;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++)
{
prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr);
}
if (prime_tower_added)
{ // don't print the prime tower if it has been printed already
return;
}
if (layer_nr > storage.max_print_height_second_to_last_extruder + 1)
{
return;
}
bool pre_wipe = storage.meshgroup->getExtruderTrain(new_extruder)->getSettingBoolean("dual_pre_wipe");
bool post_wipe = storage.meshgroup->getExtruderTrain(prev_extruder)->getSettingBoolean("prime_tower_wipe_enabled");
if (prev_extruder == new_extruder)
{
pre_wipe = false;
post_wipe = false;
}
// pre-wipe:
if (pre_wipe)
{
preWipe(storage, gcodeLayer, new_extruder);
}
addToGcode_denseInfill(gcodeLayer, layer_nr, new_extruder);
// post-wipe:
if (post_wipe)
{ //Make sure we wipe the old extruder on the prime tower.
gcodeLayer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
}
}
void PrimeTower::addToGcode_denseInfill(GCodePlanner& gcodeLayer, const int layer_nr, const int extruder)
{
ExtrusionMoves& pattern = patterns_per_extruder[extruder][((layer_nr % 2) + 2) % 2]; // +2) %2 to handle negative layer numbers
GCodePathConfig& config = config_per_extruder[extruder];
gcodeLayer.addPolygonsByOptimizer(pattern.polygons, &config);
gcodeLayer.addLinesByOptimizer(pattern.lines, &config, SpaceFillType::Lines);
last_prime_tower_poly_printed[extruder] = layer_nr;
}
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage)
{
Point ret(0, 0);
int absolute_starting_points = 0;
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0);
if (train.getSettingBoolean("machine_extruder_start_pos_abs"))
{
ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y"));
absolute_starting_points++;
}
}
if (absolute_starting_points > 0)
{ // take the average over all absolute starting positions
ret /= absolute_starting_points;
}
else
{ // use the middle of the bed
if (!storage.getSettingBoolean("machine_center_is_zero"))
{
ret = Point(storage.getSettingInMicrons("machine_width"), storage.getSettingInMicrons("machine_depth")) / 2;
}
// otherwise keep (0, 0)
}
return ret;
}
void PrimeTower::generateWipeLocations(const SliceDataStorage& storage)
{
wipe_from_middle = is_hollow;
// only wipe from the middle of the prime tower if we have a z hop already on the first move after the layer switch
for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
{
const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
wipe_from_middle &= train.getSettingBoolean("retraction_hop_enabled")
&& (!train.getSettingBoolean("retraction_hop_only_when_collides") || train.getSettingBoolean("retraction_hop_after_extruder_switch"));
}
PolygonsPointIndex segment_start; // from where to start the sequence of wipe points
PolygonsPointIndex segment_end; // where to end the sequence of wipe points
if (wipe_from_middle)
{
// take the same start as end point so that the whole poly os covered.
// find the inner polygon.
segment_start = segment_end = PolygonUtils::findNearestVert(middle, ground_poly);
}
else
{
// take the closer corner of the wipe tower and generate wipe locations on that side only:
//
// |
// |
// +-----
// .
// ^ nozzle switch location
Point from = getLocationBeforePrimeTower(storage);
// find the single line segment closest to [from] pointing most toward [from]
PolygonsPointIndex closest_vert = PolygonUtils::findNearestVert(from, ground_poly);
PolygonsPointIndex prev = closest_vert.prev();
PolygonsPointIndex next = closest_vert.next();
int64_t prev_dot_score = dot(from - closest_vert.p(), turn90CCW(prev.p() - closest_vert.p()));
int64_t next_dot_score = dot(from - closest_vert.p(), turn90CCW(closest_vert.p() - next.p()));
if (prev_dot_score > next_dot_score)
{
segment_start = prev;
segment_end = closest_vert;
}
else
{
segment_start = closest_vert;
segment_end = next;
}
}
PolygonUtils::spreadDots(segment_start, segment_end, number_of_pre_wipe_locations, pre_wipe_locations);
}
void PrimeTower::preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr)
{
const ClosestPolygonPoint wipe_location = pre_wipe_locations[current_pre_wipe_location_idx];
current_pre_wipe_location_idx = (current_pre_wipe_location_idx + pre_wipe_location_skip) % number_of_pre_wipe_locations;
ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
const int inward_dist = train.getSettingInMicrons("machine_nozzle_size") * 3 / 2 ;
const int start_dist = train.getSettingInMicrons("machine_nozzle_size") * 2;
const Point end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist);
const Point outward_dir = wipe_location.location - end;
const Point start = wipe_location.location + normal(outward_dir, start_dist);
if (wipe_from_middle)
{
// for hollow wipe tower:
// start from above
// go to wipe start
// go to the Z height of the previous/current layer
// wipe
// go to normal layer height (automatically on the next extrusion move)...
GCodePath& toward_middle = gcode_layer.addTravel(middle);
toward_middle.perform_z_hop = true;
gcode_layer.forceNewPathStart();
GCodePath& toward_wipe_start = gcode_layer.addTravel_simple(start);
toward_wipe_start.perform_z_hop = false;
toward_wipe_start.retract = true;
}
else
{
gcode_layer.addTravel(start);
}
float flow = 0.0001; // force this path being interpreted as an extrusion path, so that no Z hop will occur (TODO: really separately handle travel and extrusion moves)
gcode_layer.addExtrusionMove(end, &config_per_extruder[extruder_nr], SpaceFillType::None, flow);
}
void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
{
const Polygons outside_polygon = ground_poly.getOutsidePolygons();
for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++)
{
storage.support.supportLayers[layer].supportAreas = storage.support.supportLayers[layer].supportAreas.difference(outside_polygon);
}
}
}//namespace cura
-179
Ver Arquivo
@@ -1,179 +0,0 @@
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef PRIME_TOWER_H
#define PRIME_TOWER_H
#include <vector>
#include "GCodePathConfig.h"
#include "MeshGroup.h"
#include "utils/polygon.h" // Polygons
#include "utils/polygonUtils.h"
namespace cura
{
class SliceDataStorage;
class GCodePlanner;
class GCodeExport;
/*!
* Class for everything to do with the prime tower:
* - generating the areas
* - checking up till which height the prime tower has to be printed
* - generating the paths and adding them to the layer plan
*/
class PrimeTower
{
private:
struct ExtrusionMoves
{
Polygons polygons;
Polygons lines;
};
int extruder_count; //!< number of extruders
std::vector<GCodePathConfig> config_per_extruder; //!< Path config for prime tower for each extruder
bool is_hollow; //!< Whether the prime tower is hollow
bool wipe_from_middle; //!< Whether to wipe on the inside of the hollow prime tower
Point middle; //!< The middle of the prime tower
Point post_wipe_point; //!< location to post-wipe the unused nozzle off on
std::vector<ClosestPolygonPoint> pre_wipe_locations; //!< The differernt locations where to pre-wipe the active nozzle
const unsigned int pre_wipe_location_skip = 13; //!< How big the steps are when stepping through \ref PrimeTower::wipe_locations
const unsigned int number_of_pre_wipe_locations = 21; //!< The required size of \ref PrimeTower::wipe_locations
// note that the above are two consecutive numbers in the Fibonacci sequence
int current_pre_wipe_location_idx; //!< Index into \ref PrimeTower::wipe_locations of where to pre-wipe the nozzle
public:
bool enabled; //!< Whether the prime tower is enabled.
Polygons ground_poly; //!< The outline of the prime tower to be used for each layer
std::vector<std::vector<ExtrusionMoves>> patterns_per_extruder; //!< for each extruder a vector of patterns to alternate between, over the layers
/*!
* \brief Creates a prime tower instance that will determine where and how
* the prime tower gets printed.
*
* \param storage A storage where it retrieves the prime tower settings.
*/
PrimeTower(const SliceDataStorage& storage);
/*!
* Initialize \ref PrimeTower::config_per_extruder with speed and line width settings.
*
* \param meshgroup Where to retrieve the setttings for each extruder
*/
void initConfigs(const MeshGroup* meshgroup);
/*!
* Complete the \ref PrimeTower::config_per_extruder by settings the layer height.
*
* \param meshgroup Where to retrieve the setttings for each extruder
* \param layer_thickness The current layer thickness
*/
void setConfigs(const MeshGroup* meshgroup, const int layer_thickness);
/*!
* Generate the prime tower area to be used on each layer
*
* Fills \ref PrimeTower::ground_poly and sets \ref PrimeTower::middle
*
* \param storage Where to retrieve prime tower settings from
*/
void generateGroundpoly(const SliceDataStorage& storage);
/*!
* Generate the area where the prime tower should be.
*
* \param storage where to get settings from
* \param total_layers The total number of layers
*/
void generatePaths(const SliceDataStorage& storage);
/*!
* Add path plans for the prime tower to the \p gcode_layer
*
* \param storage where to get settings from; where to get the maximum height of the prime tower from
* \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths
* \param layer_nr The layer for which to generate the prime tower paths
* \param prev_extruder The previous extruder with which paths were planned; from which extruder a switch was made
* \param new_extruder The switched to extruder with which the prime tower paths should be generated.
*/
void addToGcode(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const GCodeExport& gcode, const int layer_nr, const int prev_extruder, const int new_extruder);
/*!
* \brief Subtract the prime tower from the support areas in storage.
*
* \param storage The storage where to find the support from which to
* subtract a prime tower.
*/
void subtractFromSupport(SliceDataStorage& storage);
private:
/*!
* Layer number of the last layer in which a prime tower has been printed per extruder train.
*
* This is recorded per extruder to account for a prime tower per extruder, instead of the mixed prime tower.
*/
int last_prime_tower_poly_printed[MAX_EXTRUDERS];
/*!
* Find an approriate representation for the point representing the location before going to the prime tower
*
* \warning This is not the actual position each time before the wipe tower
*
* \param storage where to get settings from
* \return that location
*/
Point getLocationBeforePrimeTower(const SliceDataStorage& storage);
/*!
* \param storage where to get settings from
* Depends on ground_poly being generated
*/
void generateWipeLocations(const SliceDataStorage& storage);
/*!
* \see WipeTower::generatePaths
*
* Generate the extrude paths for each extruder on even and odd layers
* Fill the ground poly with dense infill.
*
* \param storage where to get settings from
* \param total_layers The total number of layers
*/
void generatePaths_denseInfill(const SliceDataStorage& storage);
/*!
* \see PrimeTower::addToGcode
*
* Add path plans for the prime tower to the \p gcode_layer
*
* \param[in,out] gcode_layer Where to get the current extruder from; where to store the generated layer paths
* \param layer_nr The layer for which to generate the prime tower paths
* \param extruder The extruder we just switched to, with which the prime
* tower paths should be drawn.
*/
void addToGcode_denseInfill(GCodePlanner& gcode_layer, const int layer_nr, const int extruder);
/*!
* Plan the moves for wiping the current nozzles oozed material before starting to print the prime tower.
*
* \param storage where to get settings from
* \param[out] gcode_layer where to add the planned paths for wiping
* \param extruder_nr The current extruder
*/
void preWipe(const SliceDataStorage& storage, GCodePlanner& gcode_layer, const int extruder_nr);
};
}//namespace cura
#endif // PRIME_TOWER_H
-27
Ver Arquivo
@@ -1,27 +0,0 @@
#ifndef PRINT_FEATURE
#define PRINT_FEATURE
namespace cura
{
enum class PrintFeatureType: unsigned char
{
NoneType, // used to mark unspecified jumps in polygons. libArcus depends on it
OuterWall,
InnerWall,
Skin,
Support,
SkirtBrim,
Infill,
SupportInfill,
MoveCombing,
MoveRetraction,
SupportInterface
};
} // namespace cura
#endif // PRINT_FEATURE
-28
Ver Arquivo
@@ -1,28 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef RETRACTION_CONFIG_H
#define RETRACTION_CONFIG_H
namespace cura
{
/*!
* The retraction configuration used in the GCodePathConfig of each feature (and the travel config)
*/
class RetractionConfig
{
public:
double distance; //!< The distance retracted (in mm)
double speed; //!< The speed with which to retract (in mm/s)
double primeSpeed; //!< the speed with which to unretract (in mm/s)
double prime_volume; //!< the amount of material primed after unretracting (in mm^3)
int zHop; //!< the amount with which to lift the head during a retraction-travel
int retraction_min_travel_distance; //!< Minimal distance traversed to even consider retracting (in micron)
double retraction_extrusion_window; //!< Window of mm extruded filament in which to limit the amount of retractions
int retraction_count_max; //!< The maximum amount of retractions allowed to occur in the RetractionConfig::retraction_extrusion_window
};
}//namespace cura
#endif // RETRACTION_CONFIG_H
-198
Ver Arquivo
@@ -1,198 +0,0 @@
//Copyright (C) 2013 David Braam
//Copyright (c) 2016 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkirtBrim.h"
#include "support.h"
namespace cura
{
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline)
{
bool external_only = is_skirt; // whether to include holes or not
const int layer_nr = 0;
if (is_skirt)
{
const bool include_helper_parts = true;
first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
first_layer_outline = first_layer_outline.approxConvexHull();
}
else
{ // add brim underneath support by removing support where there's brim around the model
const bool include_helper_parts = false; // include manually below
first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
Polygons first_layer_empty_holes;
if (outside_only)
{
first_layer_empty_holes = first_layer_outline.getEmptyHoles();
first_layer_outline = first_layer_outline.removeEmptyHoles();
}
if (storage.support.generated && primary_line_count > 0)
{ // remove model-brim from support
// avoid gap in the middle
// V
// +---+ +----+
// |+-+| |+--+|
// || || ||[]|| > expand to fit an extra brim line
// |+-+| |+--+|
// +---+ +----+
Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
if (outside_only)
{ // don't remove support within empty holes where no brim is generated.
model_brim_covered_area.add(first_layer_empty_holes);
}
SupportLayer& support_layer = storage.support.supportLayers[0];
support_layer.supportAreas = support_layer.supportAreas.difference(model_brim_covered_area);
first_layer_outline.add(support_layer.supportAreas);
first_layer_outline.add(support_layer.skin);
}
if (storage.primeTower.enabled)
{
first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
}
}
constexpr int join_distance = 20;
first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
constexpr int smallest_line_length = 200;
constexpr int largest_error_of_removed_point = 50;
first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
if (first_layer_outline.size() == 0)
{
logError("Couldn't generate skirt / brim! No polygons on first layer.");
}
}
int SkirtBrim::generatePrimarySkirtBrimLines(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const int64_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder)
{
int offset_distance = start_distance - primary_extruder_skirt_brim_line_width / 2;
for (unsigned int skirt_brim_number = 0; skirt_brim_number < primary_line_count; skirt_brim_number++)
{
offset_distance += primary_extruder_skirt_brim_line_width;
Polygons outer_skirt_brim_line = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
//Remove small inner skirt and brim holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
for (unsigned int n = 0; n < outer_skirt_brim_line.size(); n++)
{
double area = PolygonRef{outer_skirt_brim_line[n]}.area();
if (area < 0 && area > -primary_extruder_skirt_brim_line_width * primary_extruder_skirt_brim_line_width * 100)
{
outer_skirt_brim_line.remove(n--);
}
}
skirt_brim_primary_extruder.add(outer_skirt_brim_line);
int length = skirt_brim_primary_extruder.polygonLength();
if (skirt_brim_number + 1 >= primary_line_count && length > 0 && length < primary_extruder_minimal_length) //Make brim or skirt have more lines when total length is too small.
{
primary_line_count++;
}
}
return offset_distance;
}
void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, bool outside_only)
{
const bool is_skirt = start_distance > 0;
const int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
const ExtruderTrain* adhesion_extruder = storage.meshgroup->getExtruderTrain(adhesion_extruder_nr);
const int primary_extruder_skirt_brim_line_width = adhesion_extruder->getSettingInMicrons("skirt_brim_line_width");
const int64_t primary_extruder_minimal_length = adhesion_extruder->getSettingInMicrons("skirt_brim_minimal_length");
Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];
Polygons first_layer_outline;
getFirstLayerOutline(storage, primary_line_count, primary_extruder_skirt_brim_line_width, is_skirt, outside_only, first_layer_outline);
const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
const bool has_draft_shield = storage.draft_protection_shield.size() > 0;
if (is_skirt && (has_ooze_shield || has_draft_shield))
{ // make sure we don't generate skirt through draft / ooze shield
first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
if (has_ooze_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
}
first_layer_outline = first_layer_outline.approxConvexHull();
start_distance = primary_extruder_skirt_brim_line_width / 2;
}
int offset_distance = generatePrimarySkirtBrimLines(storage, start_distance, primary_line_count, primary_extruder_skirt_brim_line_width, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);
// generate brim for ooze shield and draft shield
if (!is_skirt && (has_ooze_shield || has_draft_shield))
{
// generate areas where to make extra brim for the shields
// avoid gap in the middle
// V
// +---+ +----+
// |+-+| |+--+|
// || || ||[]|| > expand to fit an extra brim line
// |+-+| |+--+|
// +---+ +----+
const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides
Polygons shield_brim;
if (has_ooze_shield)
{
shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
}
if (has_draft_shield)
{
shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
}
const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));
// generate brim within shield_brim
skirt_brim_primary_extruder.add(shield_brim);
while (shield_brim.size() > 0)
{
shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
skirt_brim_primary_extruder.add(shield_brim);
}
// update parameters to generate secondary skirt around
first_layer_outline = outer_primary_brim;
if (has_draft_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
}
if (has_ooze_shield)
{
first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
}
offset_distance = 0;
}
{ // process other extruders' brim/skirt (as one brim line around the old brim)
int last_width = primary_extruder_skirt_brim_line_width;
for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
{
if (extruder == adhesion_extruder_nr || !storage.meshgroup->getExtruderTrain(extruder)->getIsUsed())
{
continue;
}
const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
const int width = train->getSettingInMicrons("skirt_brim_line_width");
const int64_t minimal_length = train->getSettingInMicrons("skirt_brim_minimal_length");
offset_distance += last_width / 2 + width/2;
last_width = width;
while (storage.skirt_brim[extruder].polygonLength() < minimal_length)
{
storage.skirt_brim[extruder].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
offset_distance += width;
}
}
}
}
}//namespace cura
-59
Ver Arquivo
@@ -1,59 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef SKIRT_BRIM_H
#define SKIRT_BRIM_H
#include "sliceDataStorage.h"
namespace cura
{
class SkirtBrim
{
public:
/*!
* Generate skirt or brim (depending on parameters).
*
* When \p distance > 0 and \p count == 1 a skirt is generated, which has
* slightly different configuration. Otherwise, a brim is generated.
*
* \param storage Storage containing the parts at the first layer.
* \param distance The distance of the first outset from the parts at the first
* layer.
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param outside_only Whether to only generate a brim on the outside, rather than also in holes
*/
static void generate(SliceDataStorage& storage, int distance, unsigned int primary_line_count, bool outside_only);
private:
/*!
* Get the reference outline of the first layer around which to generate the first brim/skirt line.
*
* This function may change the support polygons in the first layer
* in order to meet criteria for putting brim around the model as well as around the support.
*
* \param storage Storage containing the parts at the first layer.
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param primary_extruder_skirt_brim_line_width Line widths of the initial skirt/brim lines
* \param is_skirt Whether a skirt is being generated vs a brim
* \param outside_only Whether to only generate a brim on the outside, rather than also in holes
* \param[out] first_layer_outline The resulting reference polygons
*/
static void getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline);
/*!
* Generate the skirt/brim lines around the model
*
* \param storage Storage containing the parts at the first layer.
* \param start_distance The distance of the first outset from the parts at the first
* \param primary_line_count Number of outsets / brim lines of the primary extruder.
* \param primary_extruder_skirt_brim_line_width Line widths of the initial skirt/brim lines
* \param primary_extruder_minimal_length The minimal total length of the skirt/brim lines of the primary extruder
* \param first_layer_outline The reference polygons from which to offset outward to generate skirt/brim lines
* \param[out] skirt_brim_primary_extruder Where to store the resulting brim/skirt lines in
* \return The offset of the last brim/skirt line from the reference polygon \p first_layer_outline
*/
static int generatePrimarySkirtBrimLines(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const int64_t primary_extruder_minimal_length, const Polygons& first_layer_outline, Polygons& skirt_brim_primary_extruder);
};
}//namespace cura
#endif //SKIRT_BRIM_H
-25
Ver Arquivo
@@ -1,25 +0,0 @@
#ifndef SPACE_FILL_TYPE
#define SPACE_FILL_TYPE
namespace cura
{
/*!
* Enum class enumerating the strategies with which an area can be occupied with filament
*
* The walls/perimeters are Polygons
* ZigZag infill is PolyLines, and so is following mesh surface mode for non-polygon surfaces
* Grid, Triangles and lines infill is Lines
*/
enum class SpaceFillType
{
None,
Polygons,
PolyLines,
Lines
};
} // namespace cura
#endif // SPACE_FILL_TYPE
-92
Ver Arquivo
@@ -1,92 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "WallsComputation.h"
#include "utils/polygonUtils.h"
namespace cura {
WallsComputation::WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall)
: wall_0_inset(wall_0_inset)
, line_width_0(line_width_0)
, line_width_x(line_width_x)
, insetCount(insetCount)
, recompute_outline_based_on_outer_wall(recompute_outline_based_on_outer_wall)
{
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInsets only reads and writes data for the current layer
*/
void WallsComputation::generateInsets(SliceLayerPart* part)
{
if (insetCount == 0)
{
part->insets.push_back(part->outline);
part->print_outline = part->outline;
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
part->insets[0] = part->outline.offset(-line_width_0 / 2 - wall_0_inset);
} else if (i == 1)
{
part->insets[1] = part->insets[0].offset(-line_width_0 / 2 + wall_0_inset - line_width_x / 2);
} else
{
part->insets[i] = part->insets[i-1].offset(-line_width_x);
}
//Finally optimize all the polygons. Every point removed saves time in the long run.
part->insets[i].simplify();
part->insets[i].removeDegenerateVerts();
if (i == 0)
{
if (recompute_outline_based_on_outer_wall)
{
part->print_outline = part->insets[0].offset(line_width_0 / 2);
}
else
{
part->print_outline = part->outline;
}
}
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
/*
* This function is executed in a parallel region based on layer_nr.
* When modifying make sure any changes does not introduce data races.
*
* generateInsets only reads and writes data for the current layer
*/
void WallsComputation::generateInsets(SliceLayer* layer)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr]);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
-69
Ver Arquivo
@@ -1,69 +0,0 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef WALLS_COMPUTATION_H
#define WALLS_COMPUTATION_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Function container for computing the outer walls / insets / perimeters polygons of a layer
*/
class WallsComputation
{
public:
/*!
* The offset applied to the outer wall
*/
int wall_0_inset;
/*!
* line width of the outer wall
*/
int line_width_0;
/*!
* line width of other walls
*/
int line_width_x;
/*!
* The number of insets to to generate
*/
int insetCount;
/*!
* Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
bool recompute_outline_based_on_outer_wall;
/*!
* Basic constructor initializing the parameters with which to perform the walls computation
*
* \param wall_0_inset The offset applied to the outer wall
* \param line_width_0 line width of the outer wall
* \param line_width_x line width of other walls
* \param insetCount The number of insets to to generate
* \param recompute_outline_based_on_outer_wall Whether to compute a more accurate poly representation of the printed outlines, based on the outer wall
*/
WallsComputation(int wall_0_inset, int line_width_0, int line_width_x, int insetCount, bool recompute_outline_based_on_outer_wall);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* Note that the second inset gets offsetted by WallsComputation::line_width_0 instead of the first,
* which leads to better results for a smaller WallsComputation::line_width_0 than WallsComputation::line_width_x and when printing the outer wall last.
*
* \param layer The layer for which to generate the insets.
*/
void generateInsets(SliceLayer* layer);
private:
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
*/
void generateInsets(SliceLayerPart* part);
};
}//namespace cura
#endif//WALLS_COMPUTATION_H
+60 -63
Ver Arquivo
@@ -4,45 +4,42 @@
#include <fstream> // debug IO
#include <unistd.h>
#include "progress/Progress.h"
#include "weaveDataStorage.h"
#include "PrintFeature.h"
namespace cura
{
void Weaver::weave(MeshGroup* meshgroup)
{
wireFrame.meshgroup = meshgroup;
int maxz = meshgroup->max().z;
void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
{
int maxz = object->max().z;
int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;
std::cerr << "Layer count: " << layer_count << "\n";
DEBUG_SHOW(layer_count);
std::vector<cura::Slicer*> slicerList;
for(Mesh& mesh : meshgroup->meshes)
for(Mesh& mesh : object->meshes)
{
cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
slicerList.push_back(slicer);
}
int starting_layer_idx;
{ // find first non-empty layer
for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
{
Polygons parts;
for (cura::Slicer* slicer : slicerList)
parts.add(slicer->layers[starting_layer_idx].polygons);
parts.add(slicer->layers[starting_layer_idx].polygonList);
if (parts.size() > 0)
break;
}
if (starting_layer_idx > 0)
{
logWarning("First %i layers are empty!\n", starting_layer_idx);
logError("First %i layers are empty!\n", starting_layer_idx);
}
}
@@ -51,41 +48,35 @@ void Weaver::weave(MeshGroup* meshgroup)
{
int starting_z = -1;
for (cura::Slicer* slicer : slicerList)
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons);
wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*0,*/ wireFrame.bottom_outline, 1);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline, 1);
if (slicerList.empty()) //Wait, there is nothing to slice.
{
wireFrame.z_bottom = 0;
}
else
{
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
}
wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
Point starting_point_in_layer;
if (wireFrame.bottom_outline.size() > 0)
starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
else
starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
starting_point_in_layer = (Point(0,0) + object->max() + object->min()) / 2;
Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr);
for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
{
Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
logProgress("inset", layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
Polygons parts1;
for (cura::Slicer* slicer : slicerList)
parts1.add(slicer->layers[layer_idx].polygons);
parts1.add(slicer->layers[layer_idx].polygonList);
Polygons chainified;
chainify_polygons(parts1, starting_point_in_layer, chainified, false);
CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*layer_idx - starting_layer_idx,*/ chainified, 1);
if (commandSocket)
commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified, 1);
if (chainified.size() > 0)
{
if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
@@ -106,10 +97,9 @@ void Weaver::weave(MeshGroup* meshgroup)
{
Polygons* lower_top_parts = &wireFrame.bottom_outline;
Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr);
for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
{
Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
logProgress("skin", layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
WeaveLayer& layer = wireFrame.layers[layer_idx];
@@ -141,21 +131,16 @@ void Weaver::weave(MeshGroup* meshgroup)
{ // roofs:
if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
{
WeaveLayer& top_layer = wireFrame.layers.back();
Polygons to_be_supported; // empty for the top layer
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
}
WeaveLayer& top_layer = wireFrame.layers.back();
Polygons to_be_supported; // empty for the top layer
fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
}
{ // bottom:
if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
{
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
}
Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
}
}
@@ -166,7 +151,7 @@ void Weaver::createHorizontalFill(Polygons& lower_top_parts, WeaveLayer& layer,
{
int64_t bridgable_dist = connectionHeight;
// Polygons& polys_below = lower_top_parts;
Polygons& polys_below = lower_top_parts;
Polygons& polys_here = layer.supported;
Polygons& polys_above = layer_above;
@@ -206,14 +191,14 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
Polygons roof_outlines;
Polygons roof_holes;
{ // split roofs into outlines and holes
std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts();
for (PolygonsPart& roof_part : roof_parts)
std::vector<Polygons> roof_parts = roofs.splitIntoParts();
for (Polygons& roof_part : roof_parts)
{
roof_outlines.add(PolygonRef{roof_part[0]});
roof_outlines.add(roof_part[0]);
for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++)
{
roof_holes.add(PolygonRef{roof_part[hole_idx]});
PolygonRef{roof_holes.back()}.reverse();
roof_holes.add(roof_part[hole_idx]);
roof_holes.back().reverse();
}
}
}
@@ -221,9 +206,9 @@ void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int dire
Polygons supporting_outlines;
std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts();
for (PolygonsPart& supporting_part : supporting_parts)
supporting_outlines.add(PolygonRef{supporting_part[0]}); // only add outlines, not the holes
std::vector<Polygons> supporting_parts = supporting.splitIntoParts();
for (Polygons& supporting_part : supporting_parts)
supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes
@@ -268,16 +253,16 @@ void Weaver::fillFloors(Polygons& supporting, Polygons& to_be_supported, int dir
if (floors.size() == 0) return;
std::vector<PolygonsPart> floor_parts = floors.splitIntoParts();
std::vector<Polygons> floor_parts = floors.splitIntoParts();
Polygons floor_outlines;
Polygons floor_holes;
for (PolygonsPart& floor_part : floor_parts)
for (Polygons& floor_part : floor_parts)
{
floor_outlines.add(PolygonRef{floor_part[0]});
floor_outlines.add(floor_part[0]);
for (unsigned int hole_idx = 1; hole_idx < floor_part.size(); hole_idx++)
{
floor_holes.add(PolygonRef{floor_part[hole_idx]});
floor_holes.add(floor_part[hole_idx]);
//floor_holes.back().reverse();
}
}
@@ -324,7 +309,7 @@ void Weaver::connections2moves(WeaveRoofPart& inset)
WeaveConnectionSegment& segment = segments[idx];
assert(segment.segmentType == WeaveSegmentType::UP);
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
bool skipped = (segment.to - from).vSize2() < line_width * line_width;
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
if (skipped)
{
unsigned int begin = idx;
@@ -333,11 +318,9 @@ void Weaver::connections2moves(WeaveRoofPart& inset)
WeaveConnectionSegment& segment = segments[idx];
assert(segments[idx].segmentType == WeaveSegmentType::UP);
Point3 from = (idx == 0)? part.connection.from : segments[idx-1].to;
bool skipped = (segment.to - from).vSize2() < line_width * line_width;
bool skipped = (segment.to - from).vSize2() < extrusionWidth * extrusionWidth;
if (!skipped)
{
break;
}
}
int end = idx - ((include_half_of_last_down)? 2 : 1);
if (idx >= segments.size())
@@ -387,11 +370,13 @@ void Weaver::connect(Polygons& parts0, int z0, Polygons& parts1, int z1, WeaveCo
void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons& result, bool include_last)
{
for (unsigned int prt = 0 ; prt < parts1.size(); prt++)
{
const PolygonRef upperPart = parts1[prt];
ClosestPolygonPoint closestInPoly = PolygonUtils::findClosest(start_close_to, upperPart);
ClosestPolygonPoint closestInPoly = findClosest(start_close_to, upperPart);
PolygonRef part_top = result.newPoly();
@@ -400,9 +385,9 @@ void Weaver::chainify_polygons(Polygons& parts1, Point start_close_to, Polygons&
bool found = true;
int idx = 0;
for (Point upper_point = upperPart[closestInPoly.point_idx]; found; upper_point = next_upper.location)
for (Point upper_point = upperPart[closestInPoly.pos]; found; upper_point = next_upper.location)
{
found = PolygonUtils::getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.point_idx, next_upper);
found = getNextPointWithDistance(upper_point, nozzle_top_diameter, upperPart, idx, closestInPoly.pos, next_upper);
if (!found)
@@ -427,7 +412,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
if (supporting.size() < 1)
{
std::cerr << "lower layer has zero parts!\n";
DEBUG_PRINTLN("lower layer has zero parts!");
return;
}
@@ -452,7 +437,7 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
for (const Point& upper_point : upperPart)
{
ClosestPolygonPoint lowerPolyPoint = PolygonUtils::findClosest(upper_point, supporting);
ClosestPolygonPoint lowerPolyPoint = findClosest(upper_point, supporting);
Point& lower = lowerPolyPoint.location;
Point3 lower3 = Point3(lower.X, lower.Y, z0);
@@ -473,4 +458,16 @@ void Weaver::connect_polygons(Polygons& supporting, int z0, Polygons& supported,
}
}//namespace cura
} // namespace cura
+12 -9
Ver Arquivo
@@ -3,22 +3,23 @@
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings/settings.h"
#include "settings.h"
#include "MeshGroup.h"
#include "modelFile/modelFile.h" // PrintObject
#include "slicer.h"
#include "utils/NoCopy.h"
#include "utils/polygon.h"
#include "utils/polygonUtils.h"
#include "debug.h"
namespace cura
{
/*!
* The main weaver / WirePrint / wireframe printing class, which computes the basic paths to be followed.
*/
class Weaver : public SettingsMessenger, NoCopy
class Weaver : public SettingsBase
{
friend class Wireframe2gcode;
private:
@@ -28,7 +29,7 @@ private:
int initial_layer_thickness;
int connectionHeight;
int line_width;
int extrusionWidth;
int roof_inset;
@@ -39,13 +40,14 @@ private:
public:
Weaver(SettingsBase* settings_base) : SettingsMessenger(settings_base)
Weaver(SettingsBase* settings_base) : SettingsBase(settings_base)
{
initial_layer_thickness = getSettingInMicrons("layer_height_0");
connectionHeight = getSettingInMicrons("wireframe_height");
line_width = getSettingInMicrons("wall_line_width_x");
extrusionWidth = getSettingInMicrons("wall_line_width_x");
roof_inset = getSettingInMicrons("wireframe_roof_inset");
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
@@ -58,9 +60,10 @@ public:
* This is the main function for Neith / Weaving / WirePrinting / Webbed printing.
* Creates a wireframe for the model consisting of horizontal 'flat' parts and connections between consecutive flat parts consisting of UP moves and diagonally DOWN moves.
*
* \param objects The objects for which to create a wireframe print
* \param object The object for which to create a wireframe print
* \param commandSocket the commandSocket
*/
void weave(MeshGroup* objects);
void weave(PrintObject* object, CommandSocket* commandSocket);
private:
+83 -171
Ver Arquivo
@@ -3,55 +3,52 @@
#include <cmath> // sqrt
#include <fstream> // debug IO
#include "utils/math.h"
#include "utils/logoutput.h"
#include "weaveDataStorage.h"
#include "progress/Progress.h"
#include "pathOrderOptimizer.h" //For skirt/brim.
namespace cura
{
void Wireframe2gcode::writeGCode()
void Wireframe2gcode::writeGCode(CommandSocket* commandSocket, int& maxObjectHeight)
{
gcode.preSetup(wireFrame.meshgroup);
const unsigned int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr"); // TODO: figure out how Wireframe works with dual extrusion
gcode.setInitialTemps(*wireFrame.meshgroup, start_extruder_nr);
if (commandSocket)
commandSocket->beginGCode();
if (CommandSocket::getInstance())
CommandSocket::getInstance()->beginGCode();
maxObjectHeight = wireFrame.layers.back().z1;
processStartingCode();
int maxObjectHeight;
if (wireFrame.layers.empty())
{
maxObjectHeight = 0;
}
else
{
maxObjectHeight = wireFrame.layers.back().z1;
{ // starting Gcode
if (hasSetting("material_bed_temperature") && getSettingInDegreeCelsius("material_bed_temperature") > 0)
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), true);
if (hasSetting("material_print_temperature") && getSettingInDegreeCelsius("material_print_temperature") > 0)
gcode.writeTemperatureCommand(getSettingAsIndex("extruder_nr"), getSettingInDegreeCelsius("material_print_temperature"));
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
if (gcode.getFlavor() == GCODE_FLAVOR_BFB)
{
gcode.writeComment("enable auto-retraction");
std::ostringstream tmp;
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
gcode.writeLine(tmp.str().c_str());
}
}
unsigned int totalLayers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment("SKIRT");
gcode.setZ(initial_layer_thickness);
processSkirt();
unsigned int total_layers = wireFrame.layers.size();
gcode.writeLayerComment(0);
gcode.writeTypeComment(PrintFeatureType::SkirtBrim);
for (PolygonRef bottom_part : wireFrame.bottom_infill.roof_outlines)
{
if (bottom_part.size() == 0) continue;
writeMoveWithRetract(bottom_part[bottom_part.size()-1]);
for (Point& segment_to : bottom_part)
{
gcode.writeMove(segment_to, speedBottom, extrusion_mm3_per_mm_flat);
gcode.writeMove(segment_to, speedBottom, extrusion_per_mm_flat);
}
}
@@ -67,7 +64,7 @@ void Wireframe2gcode::writeGCode()
writeMoveWithRetract(segment.to);
} else
{
gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_connection);
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_connection);
}
}
,
@@ -77,19 +74,21 @@ void Wireframe2gcode::writeGCode()
else if (segment.segmentType == WeaveSegmentType::DOWN_AND_FLAT)
return; // do nothing
else
gcode.writeMove(segment.to, speedBottom, extrusion_mm3_per_mm_flat);
gcode.writeMove(segment.to, speedBottom, extrusion_per_mm_flat);
}
);
Progress::messageProgressStage(Progress::Stage::EXPORT, nullptr);
for (unsigned int layer_nr = 0; layer_nr < wireFrame.layers.size(); layer_nr++)
{
Progress::messageProgress(Progress::Stage::EXPORT, layer_nr+1, total_layers); // abuse the progress system of the normal mode of CuraEngine
logProgress("export", layer_nr+1, totalLayers); // abuse the progress system of the normal mode of CuraEngine
if (commandSocket) commandSocket->sendProgress(2.0/3.0 + 1.0/3.0 * float(layer_nr) / float(totalLayers));
WeaveLayer& layer = wireFrame.layers[layer_nr];
gcode.writeLayerComment(layer_nr+1);
double fanSpeed = getSettingInPercentage("cool_fan_speed_max");
int fanSpeed = getSettingInPercentage("cool_fan_speed_max");
if (layer_nr == 0)
fanSpeed = getSettingInPercentage("cool_fan_speed_min");
gcode.writeFanCommand(fanSpeed);
@@ -100,7 +99,7 @@ void Wireframe2gcode::writeGCode()
if (part.connection.segments.size() == 0) continue;
gcode.writeTypeComment(PrintFeatureType::Support); // connection
gcode.writeTypeComment("SUPPORT"); // connection
{
if (vSize2(gcode.getPositionXY() - part.connection.from) > connectionHeight)
{
@@ -116,7 +115,7 @@ void Wireframe2gcode::writeGCode()
gcode.writeTypeComment(PrintFeatureType::OuterWall); // top
gcode.writeTypeComment("WALL-OUTER"); // top
{
for (unsigned int segment_idx = 0; segment_idx < part.connection.segments.size(); segment_idx++)
{
@@ -127,7 +126,7 @@ void Wireframe2gcode::writeGCode()
writeMoveWithRetract(segment.to);
} else
{
gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat);
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
gcode.writeDelay(flat_delay);
}
}
@@ -149,7 +148,7 @@ void Wireframe2gcode::writeGCode()
// do nothing
} else
{
gcode.writeMove(segment.to, speedFlat, extrusion_mm3_per_mm_flat);
gcode.writeMove(segment.to, speedFlat, extrusion_per_mm_flat);
gcode.writeDelay(flat_delay);
}
});
@@ -160,7 +159,7 @@ void Wireframe2gcode::writeGCode()
gcode.setZ(maxObjectHeight);
gcode.writeRetraction(standard_retraction_config);
gcode.writeRetraction(&standard_retraction_config);
gcode.updateTotalPrintTime();
@@ -169,7 +168,15 @@ void Wireframe2gcode::writeGCode()
gcode.writeFanCommand(0);
finalize();
if (commandSocket)
{
gcode.finalize(maxObjectHeight, getSettingInMillimetersPerSecond("speed_travel"), getSettingString("machine_end_gcode").c_str());
for(int e=0; e<MAX_EXTRUDERS; e++)
gcode.writeTemperatureCommand(e, 0, false);
commandSocket->sendGCodeLayer();
commandSocket->endSendSlicedObject();
}
}
@@ -181,7 +188,7 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
gcode.writeMove(from, speedDown, 0);
if (straight_first_when_going_down <= 0)
{
gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection);
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
} else
{
Point3& to = segment.to;
@@ -193,14 +200,14 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
int64_t new_length = (up - from).vSize() + (to - up).vSize() + 5;
int64_t orr_length = vec.vSize();
double enlargement = new_length / orr_length;
gcode.writeMove(up, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement);
gcode.writeMove(to, speedDown*enlargement, extrusion_mm3_per_mm_connection / enlargement);
gcode.writeMove(up, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
gcode.writeMove(to, speedDown*enlargement, extrusion_per_mm_connection / enlargement);
}
gcode.writeDelay(bottom_delay);
if (up_dist_half_speed > 0)
{
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_mm3_per_mm_connection * 2);
gcode.writeMove(Point3(0,0,up_dist_half_speed) + gcode.getPosition(), speedUp / 2, extrusion_per_mm_connection * 2);
}
}
@@ -209,7 +216,7 @@ void Wireframe2gcode::go_down(WeaveLayer& layer, WeaveConnectionPart& part, unsi
void Wireframe2gcode::strategy_knot(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
{
WeaveConnectionSegment& segment = part.connection.segments[segment_idx];
gcode.writeMove(segment.to, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeMove(segment.to, speedUp, extrusion_per_mm_connection);
Point3 next_vector;
if (segment_idx + 1 < part.connection.segments.size())
{
@@ -237,14 +244,11 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
RetractionConfig retraction_config;
// TODO: get these from the settings!
retraction_config.distance = 500; //INT2MM(getSettingInt("retraction_amount"))
retraction_config.prime_volume = 0;//INT2MM(getSettingInt("retractionPrime
retraction_config.amount = 500; //INT2MM(getSettingInt("retractionAmount"))
retraction_config.primeAmount = 0;//INT2MM(getSettingInt("retractionPrime
retraction_config.speed = 20; // 40;
retraction_config.primeSpeed = 15; // 30;
retraction_config.zHop = 0; //getSettingInt("retraction_hop");
retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
double top_retract_pause = 2.0;
int retract_hop_dist = 1000;
@@ -259,8 +263,8 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
Point3 vec = to - from;
Point3 lowering = vec * retract_hop_dist / 2 / vec.vSize();
Point3 lower = to - lowering;
gcode.writeMove(lower, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(retraction_config);
gcode.writeMove(lower, speedUp, extrusion_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeMove(to + lowering, speedUp, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -268,8 +272,8 @@ void Wireframe2gcode::strategy_retract(WeaveLayer& layer, WeaveConnectionPart& p
} else
{
gcode.writeMove(to, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeRetraction(retraction_config);
gcode.writeMove(to, speedUp, extrusion_per_mm_connection);
gcode.writeRetraction(&retraction_config);
gcode.writeMove(to + Point3(0, 0, retract_hop_dist), speedFlat, 0);
gcode.writeDelay(top_retract_pause);
if (after_retract_hop)
@@ -306,7 +310,7 @@ void Wireframe2gcode::strategy_compensate(WeaveLayer& layer, WeaveConnectionPart
int64_t orrLength = (segment.to - from).vSize() + next_vector.vSize() + 1; // + 1 in order to avoid division by zero
int64_t newLength = (newTop - from).vSize() + (next_point - newTop).vSize() + 1; // + 1 in order to avoid division by zero
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_mm3_per_mm_connection * orrLength / newLength);
gcode.writeMove(newTop, speedUp * newLength / orrLength, extrusion_per_mm_connection * orrLength / newLength);
}
void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& part, unsigned int segment_idx)
{
@@ -321,7 +325,7 @@ void Wireframe2gcode::handle_segment(WeaveLayer& layer, WeaveConnectionPart& par
go_down(layer, part, segment_idx);
break;
case WeaveSegmentType::FLAT:
logWarning("Warning: flat piece in wire print connection.\n");
DEBUG_SHOW("flat piece in connection?!!?!");
break;
case WeaveSegmentType::UP:
if (strategy == STRATEGY_KNOT)
@@ -385,12 +389,12 @@ void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionP
detoured -= next_dir;
}
gcode.writeMove(detoured, speedUp, extrusion_mm3_per_mm_connection);
gcode.writeMove(detoured, speedUp, extrusion_per_mm_connection);
}
break;
case WeaveSegmentType::DOWN:
gcode.writeMove(segment.to, speedDown, extrusion_mm3_per_mm_connection);
gcode.writeMove(segment.to, speedDown, extrusion_per_mm_connection);
gcode.writeDelay(roof_outer_delay);
break;
case WeaveSegmentType::FLAT:
@@ -402,16 +406,16 @@ void Wireframe2gcode::handle_roof_segment(WeaveRoofPart& inset, WeaveConnectionP
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& roof_outlines
void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& roof_outlines
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler)
{
// bottom:
gcode.writeTypeComment(PrintFeatureType::Infill);
for (unsigned int inset_idx = 0; inset_idx < infill_insets.size(); inset_idx++)
gcode.writeTypeComment("FILL");
for (unsigned int inset_idx = 0; inset_idx < fill_insets.size(); inset_idx++)
{
WeaveRoofPart& inset = infill_insets[inset_idx];
WeaveRoofPart& inset = fill_insets[inset_idx];
for (unsigned int inset_part_nr = 0; inset_part_nr < inset.connections.size(); inset_part_nr++)
@@ -419,7 +423,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
WeaveConnectionPart& inset_part = inset.connections[inset_part_nr];
std::vector<WeaveConnectionSegment>& segments = inset_part.connection.segments;
gcode.writeTypeComment(PrintFeatureType::Support); // connection
gcode.writeTypeComment("SUPPORT"); // connection
if (segments.size() == 0) continue;
Point3 first_extrusion_from = inset_part.connection.from;
unsigned int first_segment_idx;
@@ -435,7 +439,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
connectionHandler(*this, inset, inset_part, segment_idx);
}
gcode.writeTypeComment(PrintFeatureType::InnerWall); // top
gcode.writeTypeComment("WALL-INNER"); // top
for (unsigned int segment_idx = 0; segment_idx < segments.size(); segment_idx++)
{
WeaveConnectionSegment& segment = segments[segment_idx];
@@ -449,7 +453,7 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
}
gcode.writeTypeComment(PrintFeatureType::OuterWall); // outer perimeter of the flat parts
gcode.writeTypeComment("WALL-OUTER"); // outer perimeter of the flat parts
for (PolygonRef poly : roof_outlines)
{
writeMoveWithRetract(poly[poly.size() - 1]);
@@ -468,36 +472,37 @@ void Wireframe2gcode::writeFill(std::vector<WeaveRoofPart>& infill_insets, Polyg
void Wireframe2gcode::writeMoveWithRetract(Point3 to)
{
if ((gcode.getPosition() - to).vSize2() >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(standard_retraction_config);
gcode.writeRetraction(&standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
void Wireframe2gcode::writeMoveWithRetract(Point to)
{
if (vSize2(gcode.getPositionXY() - to) >= nozzle_top_diameter * nozzle_top_diameter * 2 * 2)
gcode.writeRetraction(standard_retraction_config);
gcode.writeRetraction(&standard_retraction_config);
gcode.writeMove(to, moveSpeed, 0);
}
Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base)
: SettingsMessenger(settings_base)
: SettingsBase(settings_base)
, gcode(gcode)
, wireFrame(weaver.wireFrame)
{
wireFrame = weaver.wireFrame;
initial_layer_thickness = getSettingInMicrons("layer_height_0");
connectionHeight = getSettingInMicrons("wireframe_height");
roof_inset = getSettingInMicrons("wireframe_roof_inset");
filament_diameter = getSettingInMicrons("material_diameter");
line_width = getSettingInMicrons("wall_line_width_x");
extrusionWidth = getSettingInMicrons("wall_line_width_x");
flowConnection = getSettingInPercentage("wireframe_flow_connection");
flowFlat = getSettingInPercentage("wireframe_flow_flat");
const double line_area = M_PI * square(INT2MM(line_width) / 2.0);
extrusion_mm3_per_mm_connection = line_area * flowConnection / 100.0;
extrusion_mm3_per_mm_flat = line_area * flowFlat / 100.0;
double filament_area = /* M_PI * */ (INT2MM(filament_diameter) / 2.0) * (INT2MM(filament_diameter) / 2.0);
double lineArea = /* M_PI * */ (INT2MM(extrusionWidth) / 2.0) * (INT2MM(extrusionWidth) / 2.0);
extrusion_per_mm_connection = lineArea / filament_area * double(flowConnection) / 100.0;
extrusion_per_mm_flat = lineArea / filament_area * double(flowFlat) / 100.0;
nozzle_outer_diameter = getSettingInMicrons("machine_nozzle_tip_outer_diameter"); // ___ ___ .
nozzle_head_distance = getSettingInMicrons("machine_nozzle_head_distance"); // | | .
nozzle_expansion_angle = getSettingInAngleRadians("machine_nozzle_expansion_angle"); // \_U_/ .
@@ -537,107 +542,14 @@ Wireframe2gcode::Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBas
roof_outer_delay = getSettingInSeconds("wireframe_roof_outer_delay");
standard_retraction_config.distance = getSettingInMillimeters("retraction_amount");
standard_retraction_config.prime_volume = getSettingInCubicMillimeters("retraction_extra_prime_amount");
standard_retraction_config.amount = INT2MM(getSettingInMicrons("retraction_amount"));
standard_retraction_config.primeAmount = INT2MM(getSettingInMicrons("retraction_extra_prime_amount"));
standard_retraction_config.speed = getSettingInMillimetersPerSecond("retraction_retract_speed");
standard_retraction_config.primeSpeed = getSettingInMillimetersPerSecond("retraction_prime_speed");
standard_retraction_config.zHop = getSettingInMicrons("retraction_hop");
standard_retraction_config.retraction_count_max = getSettingAsCount("retraction_count_max");
standard_retraction_config.retraction_extrusion_window = getSettingInMillimeters("retraction_extrusion_window");
standard_retraction_config.retraction_min_travel_distance = getSettingInMicrons("retraction_min_travel");
}
void Wireframe2gcode::processStartingCode()
{
if (!CommandSocket::isInstantiated())
{
std::string prefix = gcode.getFileHeader();
gcode.writeCode(prefix.c_str());
}
int start_extruder_nr = getSettingAsIndex("adhesion_extruder_nr");
gcode.writeComment("Generated with Cura_SteamEngine " VERSION);
if (gcode.getFlavor() != EGCodeFlavor::ULTIGCODE && gcode.getFlavor() != EGCodeFlavor::GRIFFIN)
{
if (getSettingBoolean("material_bed_temp_prepend"))
{
if (getSettingBoolean("machine_heated_bed") && getSettingInDegreeCelsius("material_bed_temperature") != 0)
{
gcode.writeBedTemperatureCommand(getSettingInDegreeCelsius("material_bed_temperature"), getSettingBoolean("material_bed_temp_wait"));
}
}
if (getSettingBoolean("material_print_temp_prepend"))
{
for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++)
{
double print_temp = getSettingInDegreeCelsius("material_print_temperature");
gcode.writeTemperatureCommand(extruder_nr, print_temp);
}
if (getSettingBoolean("material_print_temp_wait"))
{
for (int extruder_nr = 0; extruder_nr < getSettingAsCount("machine_extruder_count"); extruder_nr++)
{
double print_temp = getSettingInDegreeCelsius("material_print_temperature");
gcode.writeTemperatureCommand(extruder_nr, print_temp, true);
}
}
}
}
gcode.writeCode(getSettingString("machine_start_gcode").c_str());
if (gcode.getFlavor() == EGCodeFlavor::BFB)
{
gcode.writeComment("enable auto-retraction");
std::ostringstream tmp;
tmp << "M227 S" << (getSettingInMicrons("retraction_amount") * 2560 / 1000) << " P" << (getSettingInMicrons("retraction_amount") * 2560 / 1000);
gcode.writeLine(tmp.str().c_str());
}
else if (gcode.getFlavor() == EGCodeFlavor::GRIFFIN)
{ // initialize extruder trains
gcode.writeCode("T0"); // Toolhead already assumed to be at T0, but writing it just to be safe...
CommandSocket::setSendCurrentPosition(gcode.getPositionXY());
gcode.startExtruder(start_extruder_nr);
constexpr bool wait = true;
gcode.writeTemperatureCommand(start_extruder_nr, getSettingInDegreeCelsius("material_print_temperature"), wait);
gcode.writePrimeTrain(getSettingInMillimetersPerSecond("speed_travel"));
gcode.writeRetraction(standard_retraction_config);
}
}
void Wireframe2gcode::processSkirt()
{
if (wireFrame.bottom_outline.size() == 0) //If we have no layers, don't create a skirt either.
{
return;
}
Polygons skirt = wireFrame.bottom_outline.offset(100000+5000).offset(-100000);
PathOrderOptimizer order(Point(INT32_MIN, INT32_MIN));
order.addPolygons(skirt);
order.optimize();
for (unsigned int poly_order_idx = 0; poly_order_idx < skirt.size(); poly_order_idx++)
{
unsigned int poly_idx = order.polyOrder[poly_order_idx];
PolygonRef poly = skirt[poly_idx];
gcode.writeMove(poly[order.polyStart[poly_idx]], getSettingInMillimetersPerSecond("speed_travel"), 0);
for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
{
Point& p = poly[(point_idx + order.polyStart[poly_idx] + 1) % poly.size()];
gcode.writeMove(p, getSettingInMillimetersPerSecond("skirt_brim_speed"), getSettingInMillimeters("skirt_brim_line_width") * INT2MM(initial_layer_thickness));
}
}
}
void Wireframe2gcode::finalize()
{
gcode.finalize(getSettingString("machine_end_gcode").c_str());
for(int e=0; e<getSettingAsCount("machine_extruder_count"); e++)
gcode.writeTemperatureCommand(e, 0, false);
}
}//namespace cura
} // namespace cura
+38 -53
Ver Arquivo
@@ -4,25 +4,25 @@
#include <functional> // passing function pointer or lambda as argument to a function
#include "utils/NoCopy.h"
#include "weaveDataStorage.h"
#include "commandSocket.h"
#include "settings/settings.h"
#include "settings.h"
#include "MeshGroup.h"
#include "modelFile/modelFile.h" // PrintObject
#include "slicer.h"
#include "utils/polygon.h"
#include "Weaver.h"
#include "debug.h"
namespace cura
{
/*!
* Export class for exporting wireframe print gcode / weaver gcode / wireprint gcode.
*/
class Wireframe2gcode : public SettingsMessenger, NoCopy
class Wireframe2gcode : public SettingsBase
{
private:
static const int STRATEGY_COMPENSATE = 0;
@@ -31,36 +31,36 @@ private:
int initial_layer_thickness;
int filament_diameter;
int line_width;
double flowConnection;
double flowFlat;
double extrusion_mm3_per_mm_connection;
double extrusion_mm3_per_mm_flat;
int nozzle_outer_diameter;
int nozzle_head_distance;
double nozzle_expansion_angle;
int nozzle_clearance;
int nozzle_top_diameter;
double moveSpeed;
double speedBottom;
double speedUp;
double speedDown;
double speedFlat;
int connectionHeight;
int roof_inset;
double flat_delay;
double bottom_delay;
double top_delay;
int up_dist_half_speed;
int top_jump_dist;
int fall_down;
int drag_along;
int strategy;
double go_back_to_last_top;
int straight_first_when_going_down;
int roof_fall_down;
int roof_drag_along;
double roof_outer_delay;
int extrusionWidth;
int flowConnection;// = getSettingInt("wireframeFlowConnection");
int flowFlat; // = getSettingInt("wireframeFlowFlat");
double extrusion_per_mm_connection; // = lineArea / filament_area * double(flowConnection) / 100.0;
double extrusion_per_mm_flat; // = lineArea / filament_area * double(flowFlat) / 100.0;
int nozzle_outer_diameter; // = getSettingInt("machineNozzleTipOuterDiameter"); // ___ ___ .
int nozzle_head_distance; // = getSettingInt("machineNozzleHeadDistance"); // | | .
int nozzle_expansion_angle; // = getSettingInt("machineNozzleExpansionAngle"); // \_U_/ .
int nozzle_clearance; // = getSettingInt("wireframeNozzleClearance"); // at least line width
int nozzle_top_diameter; // = tan(static_cast<double>(nozzle_expansion_angle)/180.0 * M_PI) * connectionHeight + nozzle_outer_diameter + nozzle_clearance;
int moveSpeed; // = 40;
int speedBottom; // = getSettingInt("wireframePrintspeedBottom");
int speedUp; // = getSettingInt("wireframePrintspeedUp");
int speedDown; // = getSettingInt("wireframePrintspeedDown");
int speedFlat; // = getSettingInt("wireframePrintspeedFlat");
int connectionHeight; // = getSettingInt("wireframeConnectionHeight");
int roof_inset; // = getSettingInt("wireframeRoofInset");
double flat_delay; // = getSettingInt("wireframeFlatDelay")/100.0;
double bottom_delay; // = getSettingInt("wireframeBottomDelay")/100.0;
double top_delay; // = getSettingInt("wireframeTopDelay")/100.0;
int up_dist_half_speed; // = getSettingInt("wireframeUpDistHalfSpeed");
int top_jump_dist; // = getSettingInt("wireframeTopJump");
int fall_down; // = getSettingInt("wireframeFallDown");
int drag_along; // = getSettingInt("wireframeDragAlong");
int strategy; // = getSettingInt("wireframeStrategy"); // HIGHER_BEND_NO_STRAIGHTEN; // RETRACT_TO_STRAIGHTEN; // MOVE_TO_STRAIGHTEN; //
double go_back_to_last_top; // = false;
int straight_first_when_going_down; // = getSettingInt("wireframeStraightBeforeDown"); // %
int roof_fall_down; // = getSettingInt("wireframeRoofFallDown");
int roof_drag_along; // = getSettingInt("wireframeRoofDragAlong");
double roof_outer_delay; // = getSettingInt("wireframeRoofOuterDelay")/100.0;
RetractionConfig standard_retraction_config; //!< The standard retraction settings used for moves between parts etc.
@@ -69,28 +69,13 @@ public:
Wireframe2gcode(Weaver& weaver, GCodeExport& gcode, SettingsBase* settings_base);
void writeGCode();
void writeGCode(CommandSocket* commandSocket, int& maxObjectHeight);
private:
WireFrame& wireFrame;
WireFrame wireFrame;
/*!
* Startup gcode: nozzle temp up, retraction settings, bed temp
*/
void processStartingCode();
/*!
* Lay down a skirt
*/
void processSkirt();
/*!
* End gcode: nozzle temp down
*/
void finalize();
void writeFill(std::vector<WeaveRoofPart>& infill_insets, Polygons& outlines
void writeFill(std::vector<WeaveRoofPart>& fill_insets, Polygons& outlines
, std::function<void (Wireframe2gcode& thiss, WeaveRoofPart& inset, WeaveConnectionPart& part, unsigned int segment_idx)> connectionHandler
, std::function<void (Wireframe2gcode& thiss, WeaveConnectionSegment& p)> flatHandler);
+5 -7
Ver Arquivo
@@ -1,11 +1,9 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "bridge.h"
#include "sliceDataStorage.h"
namespace cura {
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
int bridgeAngle(Polygons outline, SliceLayer* prevLayer)
{
AABB boundaryBox(outline);
//To detect if we have a bridge, first calculate the intersection of the current layer with the previous layer.
@@ -29,9 +27,9 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
for(unsigned int n=0; n<islands.size(); n++)
{
//Skip internal holes
if (!PolygonRef{islands[n]}.orientation())
if (!islands[n].orientation())
continue;
double area = fabs(PolygonRef{islands[n]}.area());
double area = fabs(islands[n].area());
if (area > area1)
{
if (area1 > area2)
@@ -51,8 +49,8 @@ int bridgeAngle(Polygons outline, const SliceLayer* prevLayer)
if (idx1 < 0 || idx2 < 0)
return -1;
Point center1 = PolygonRef{islands[idx1]}.centerOfMass();
Point center2 = PolygonRef{islands[idx2]}.centerOfMass();
Point center1 = islands[idx1].centerOfMass();
Point center2 = islands[idx2].centerOfMass();
return angle(center2 - center1);
}
+4 -4
Ver Arquivo
@@ -2,11 +2,11 @@
#ifndef BRIDGE_H
#define BRIDGE_H
namespace cura {
class Polygons;
class SliceLayer;
#include "sliceDataStorage.h"
int bridgeAngle(Polygons outline, const SliceLayer* prevLayer);
namespace cura {
int bridgeAngle(Polygons outline, SliceLayer* prevLayer);
}//namespace cura
+237
Ver Arquivo
@@ -0,0 +1,237 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "comb.h"
namespace cura {
bool Comb::preTest(Point startPoint, Point endPoint)
{
return collisionTest(startPoint, endPoint);
}
bool Comb::collisionTest(Point startPoint, Point endPoint)
{
Point diff = endPoint - startPoint;
matrix = PointMatrix(diff);
sp = matrix.apply(startPoint);
ep = matrix.apply(endPoint);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x > sp.X && x < ep.X)
return true;
}
p0 = p1;
}
}
return false;
}
void Comb::calcMinMax()
{
for(unsigned int n=0; n<boundery.size(); n++)
{
minX[n] = INT64_MAX;
maxX[n] = INT64_MIN;
Point p0 = matrix.apply(boundery[n][boundery[n].size()-1]);
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = matrix.apply(boundery[n][i]);
if ((p0.Y > sp.Y && p1.Y < sp.Y) || (p1.Y > sp.Y && p0.Y < sp.Y))
{
int64_t x = p0.X + (p1.X - p0.X) * (sp.Y - p0.Y) / (p1.Y - p0.Y);
if (x >= sp.X && x <= ep.X)
{
if (x < minX[n]) { minX[n] = x; minIdx[n] = i; }
if (x > maxX[n]) { maxX[n] = x; maxIdx[n] = i; }
}
}
p0 = p1;
}
}
}
unsigned int Comb::getPolygonAbove(int64_t x)
{
int64_t min = POINT_MAX;
unsigned int ret = NO_INDEX;
for(unsigned int n=0; n<boundery.size(); n++)
{
if (minX[n] > x && minX[n] < min)
{
min = minX[n];
ret = n;
}
}
return ret;
}
Point Comb::getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx)
{
Point p0 = boundery[polygonNr][(idx > 0) ? (idx - 1) : (boundery[polygonNr].size() - 1)];
Point p1 = boundery[polygonNr][idx];
Point p2 = boundery[polygonNr][(idx < (boundery[polygonNr].size() - 1)) ? (idx + 1) : (0)];
Point off0 = crossZ(normal(p1 - p0, MM2INT(1.0)));
Point off1 = crossZ(normal(p2 - p1, MM2INT(1.0)));
Point n = normal(off0 + off1, MM2INT(0.2));
return p1 + n;
}
Comb::Comb(Polygons& _boundery)
: boundery(_boundery)
{
minX = new int64_t[boundery.size()];
maxX = new int64_t[boundery.size()];
minIdx = new unsigned int[boundery.size()];
maxIdx = new unsigned int[boundery.size()];
}
Comb::~Comb()
{
delete[] minX;
delete[] maxX;
delete[] minIdx;
delete[] maxIdx;
}
bool Comb::moveInside(Point* p, int distance)
{
Point ret = *p;
int64_t bestDist = MM2INT(2.0) * MM2INT(2.0);
for(unsigned int n=0; n<boundery.size(); n++)
{
if (boundery[n].size() < 1)
continue;
Point p0 = boundery[n][boundery[n].size()-1];
for(unsigned int i=0; i<boundery[n].size(); i++)
{
Point p1 = boundery[n][i];
//Q = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B ));
Point pDiff = p1 - p0;
int64_t lineLength = vSize(pDiff);
int64_t distOnLine = dot(pDiff, *p - p0) / lineLength;
if (distOnLine < 10)
distOnLine = 10;
if (distOnLine > lineLength - 10)
distOnLine = lineLength - 10;
Point q = p0 + pDiff * distOnLine / lineLength;
int64_t dist = vSize2(q - *p);
if (dist < bestDist)
{
bestDist = dist;
ret = q + crossZ(normal(p1 - p0, distance));
}
p0 = p1;
}
}
if (bestDist < MM2INT(2.0) * MM2INT(2.0))
{
*p = ret;
return true;
}
return false;
}
bool Comb::calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints)
{
if (shorterThen(endPoint - startPoint, MM2INT(1.5)))
return true;
bool addEndpoint = false;
//Check if we are inside the comb boundaries
if (!boundery.inside(startPoint))
{
if (!moveInside(&startPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
combPoints.push_back(startPoint);
}
if (!boundery.inside(endPoint))
{
if (!moveInside(&endPoint)) //If we fail to move the point inside the comb boundary we need to retract.
return false;
addEndpoint = true;
}
//Check if we are crossing any bounderies, and pre-calculate some values.
if (!preTest(startPoint, endPoint))
{
//We're not crossing any boundaries. So skip the comb generation.
if (!addEndpoint && combPoints.size() == 0) //Only skip if we didn't move the start and end point.
return true;
}
//Calculate the minimum and maximum positions where we cross the comb boundary
calcMinMax();
int64_t x = sp.X;
std::vector<Point> pointList;
//Now walk trough the crossings, for every boundary we cross, find the initial cross point and the exit point. Then add all the points in between
// to the pointList and continue with the next boundary we will cross, until there are no more boundaries to cross.
// This gives a path from the start to finish curved around the holes that it encounters.
while(true)
{
unsigned int n = getPolygonAbove(x);
if (n == NO_INDEX) break;
pointList.push_back(matrix.unapply(Point(minX[n] - MM2INT(0.2), sp.Y)));
if ( (minIdx[n] - maxIdx[n] + boundery[n].size()) % boundery[n].size() > (maxIdx[n] - minIdx[n] + boundery[n].size()) % boundery[n].size())
{
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i < boundery[n].size() - 1) ? (i + 1) : (0))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}else{
if (minIdx[n] == 0)
minIdx[n] = boundery[n].size() - 1;
else
minIdx[n]--;
if (maxIdx[n] == 0)
maxIdx[n] = boundery[n].size() - 1;
else
maxIdx[n]--;
for(unsigned int i=minIdx[n]; i != maxIdx[n]; i = (i > 0) ? (i - 1) : (boundery[n].size() - 1))
{
pointList.push_back(getBounderyPointWithOffset(n, i));
}
}
pointList.push_back(matrix.unapply(Point(maxX[n] + MM2INT(0.2), sp.Y)));
x = maxX[n];
}
pointList.push_back(endPoint);
//Optimize the pointList, skip each point we could already reach by not crossing a boundary. This smooths out the path and makes it skip any unneeded corners.
Point p0 = startPoint;
for(unsigned int n=1; n<pointList.size(); n++)
{
if (collisionTest(p0, pointList[n]))
{
if (collisionTest(p0, pointList[n-1]))
return false;
p0 = pointList[n-1];
combPoints.push_back(p0);
}
}
if (addEndpoint)
combPoints.push_back(endPoint);
return true;
}
}//namespace cura
+44
Ver Arquivo
@@ -0,0 +1,44 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef COMB_H
#define COMB_H
#include "utils/polygon.h"
namespace cura {
class Comb
{
private:
Polygons& boundery;
int64_t* minX;
int64_t* maxX;
unsigned int* minIdx;
unsigned int* maxIdx;
PointMatrix matrix;
Point sp;
Point ep;
bool preTest(Point startPoint, Point endPoint);
bool collisionTest(Point startPoint, Point endPoint);
void calcMinMax();
unsigned int getPolygonAbove(int64_t x);
Point getBounderyPointWithOffset(unsigned int polygonNr, unsigned int idx);
public:
Comb(Polygons& _boundery);
~Comb();
bool inside(const Point p) { return boundery.inside(p); }
bool moveInside(Point* p, int distance = 100);
bool calc(Point startPoint, Point endPoint, std::vector<Point>& combPoints);
};
}//namespace cura
#endif//COMB_H
+138 -691
Ver Arquivo
@@ -1,620 +1,202 @@
#include "utils/logoutput.h"
#include "commandSocket.h"
#include "FffProcessor.h"
#include "progress/Progress.h"
#include "fffProcessor.h"
#include <thread>
#include <cinttypes>
#ifdef ARCUS
#include <Arcus/Socket.h>
#include <Arcus/SocketListener.h>
#include <Arcus/Error.h>
#endif
#include <string> // stoi
#ifdef _WIN32
#include <windows.h>
#endif
#define DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(x)
// std::cerr << x;
namespace cura {
#define BYTES_PER_FLOAT 4
#define FLOATS_PER_VECTOR 3
#define VECTORS_PER_FACE 3
CommandSocket* CommandSocket::instance = nullptr; // instantiate instance
#ifdef ARCUS
class Listener : public Arcus::SocketListener
{
public:
void stateChanged(Arcus::SocketState::SocketState newState) override
{
}
void messageReceived() override
{
}
void error(const Arcus::Error & error) override
{
if (error.getErrorCode() == Arcus::ErrorCode::Debug)
{
log("%s\n", error.toString().c_str());
}
else
{
logError("%s\n", error.toString().c_str());
}
}
};
/*!
* A template structure used to store data to be sent to the front end.
*/
template <typename T>
class SliceDataStruct
{
SliceDataStruct(const SliceDataStruct&) = delete;
SliceDataStruct& operator=(const SliceDataStruct&) = delete;
public:
SliceDataStruct()
: sliced_objects(0)
, current_layer_count(0)
, current_layer_offset(0)
{ }
//! The number of sliced objects for this sliced object list
int sliced_objects;
int current_layer_count;//!< Number of layers for which data has been buffered in slice_data so far.
int current_layer_offset;//!< Offset to add to layer number for the current slice object when slicing one at a time.
std::unordered_map<int, std::shared_ptr<T>> slice_data;
};
class CommandSocket::Private
{
public:
Private()
: socket(nullptr)
: processor(nullptr)
, socket(nullptr)
, object_count(0)
, current_object_number(0)
, currentSlicedObject(nullptr)
, slicedObjects(0)
{ }
std::shared_ptr<cura::proto::Layer> getLayerById(int id);
Cura::Layer* getLayerById(int id);
std::shared_ptr<cura::proto::LayerOptimized> getOptimizedLayerById(int id);
fffProcessor* processor;
Arcus::Socket* socket;
// Number of objects that need to be sliced
int object_count;
int current_object_number;
std::string temp_gcode_file;
std::shared_ptr<Cura::SlicedObjectList> slicedObjectList;
Cura::SlicedObject* currentSlicedObject;
int slicedObjects;
std::vector<int64_t> objectIds;
std::string tempGCodeFile;
std::ostringstream gcode_output_stream;
// Print object that olds one or more meshes that need to be sliced.
std::vector< std::shared_ptr<MeshGroup> > objects_to_slice;
SliceDataStruct<cura::proto::Layer> sliced_layers;
SliceDataStruct<cura::proto::LayerOptimized> optimized_layers;
std::shared_ptr<PrintObject> objectToSlice;
};
/*!
* PathCompiler buffers and prepares the sliced data to be sent to the front end and saves them in
* appropriate buffers
*/
class CommandSocket::PathCompiler
CommandSocket::CommandSocket(fffProcessor* processor)
: d(new Private)
{
typedef cura::proto::PathSegment::PointType PointType;
static_assert(sizeof(PrintFeatureType) == 1, "To be compatible with the Cura frontend code PrintFeatureType needs to be of size 1");
//! Reference to the private data of the CommandSocket used to send the data to the front end.
CommandSocket::Private& _cs_private_data;
//! Keeps track of the current layer number being processed. If layer number is set to a different value, the current data is flushed to CommandSocket.
int _layer_nr;
int extruder;
PointType data_point_type;
std::vector<PrintFeatureType> line_types; //!< Line types for the line segments stored, the size of this vector is N.
std::vector<float> line_widths; //!< Line widths for the line segments stored, the size of this vector is N.
std::vector<float> points; //!< The points used to define the line segments, the size of this vector is D*(N+1) as each line segment is defined from one point to the next. D is the dimensionality of the point.
Point last_point;
PathCompiler(const PathCompiler&) = delete;
PathCompiler& operator=(const PathCompiler&) = delete;
public:
PathCompiler(CommandSocket::Private& cs_private_data):
_cs_private_data(cs_private_data),
_layer_nr(0),
extruder(0),
data_point_type(cura::proto::PathSegment::Point2D),
line_types(),
line_widths(),
points(),
last_point{0,0}
{}
~PathCompiler()
{
if (line_types.size())
{
flushPathSegments();
}
}
/*!
* Used to select which layer the following layer data is intended for.
*/
void setLayer(int new_layer_nr)
{
if (_layer_nr != new_layer_nr)
{
flushPathSegments();
_layer_nr = new_layer_nr;
}
}
/*!
* Returns the current layer which data is written to.
*/
int getLayer() const
{
return _layer_nr;
}
/*!
* Used to set which extruder will be used for printing the following layer data is intended for.
*/
void setExtruder(int new_extruder)
{
if (extruder != new_extruder)
{
flushPathSegments();
extruder = new_extruder;
}
}
/*!
* Special handling of the first point in an added line sequence.
* If the new sequence of lines does not start at the current end point
* of the path this jump is marked as PrintFeatureType::NoneType
*/
void handleInitialPoint(Point from)
{
if (points.size() == 0)
{
addPoint2D(from);
}
else if (from != last_point)
{
addLineSegment(PrintFeatureType::NoneType, from, 1.0);
}
}
/*!
* Transfers the currently buffered line segments to the
* CommandSocket layer message storage.
*/
void flushPathSegments();
/*!
* Move the current point of this path to \position.
*/
void setCurrentPosition(Point position)
{
handleInitialPoint(position);
}
/*!
* Adds a single line segment to the current path. The line segment added is from the current last point to point \p to
*/
void sendLineTo(PrintFeatureType print_feature_type, Point to, int width);
/*!
* Adds closed polygon to the current path
*/
void sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef poly, int width);
private:
/*!
* Convert and add a point to the points buffer, each point being represented as two consecutive floats. All members adding a 2D point to the data should use this function.
*/
void addPoint2D(Point point)
{
points.push_back(INT2MM(point.X));
points.push_back(INT2MM(point.Y));
last_point = point;
}
/*!
* Implements the functionality of adding a single 2D line segment to the path data. All member functions adding a 2D line segment should use this functions.
*/
void addLineSegment(PrintFeatureType print_feature_type, Point point, int line_width)
{
addPoint2D(point);
line_types.push_back(print_feature_type);
line_widths.push_back(INT2MM(line_width));
}
};
#endif
CommandSocket::CommandSocket()
#ifdef ARCUS
: private_data(new Private)
, path_comp(new PathCompiler(*private_data))
#endif
{
#ifdef ARCUS
#endif
d->processor = processor;
d->processor->setCommandSocket(this);
}
CommandSocket* CommandSocket::getInstance()
{
return instance;
}
void CommandSocket::instantiate()
{
instance = new CommandSocket();
}
bool CommandSocket::isInstantiated()
{
return instance != nullptr;
}
void CommandSocket::connect(const std::string& ip, int port)
{
#ifdef ARCUS
private_data->socket = new Arcus::Socket();
private_data->socket->addListener(new Listener());
d->socket = new Arcus::Socket();
d->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
d->socket->registerMessageType(2, &Cura::SlicedObjectList::default_instance());
d->socket->registerMessageType(3, &Cura::Progress::default_instance());
d->socket->registerMessageType(4, &Cura::GCodeLayer::default_instance());
d->socket->registerMessageType(5, &Cura::ObjectPrintTime::default_instance());
d->socket->registerMessageType(6, &Cura::SettingList::default_instance());
d->socket->registerMessageType(7, &Cura::GCodePrefix::default_instance());
//private_data->socket->registerMessageType(1, &Cura::ObjectList::default_instance());
private_data->socket->registerMessageType(&cura::proto::Slice::default_instance());
private_data->socket->registerMessageType(&cura::proto::Layer::default_instance());
private_data->socket->registerMessageType(&cura::proto::LayerOptimized::default_instance());
private_data->socket->registerMessageType(&cura::proto::Progress::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodeLayer::default_instance());
private_data->socket->registerMessageType(&cura::proto::PrintTimeMaterialEstimates::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingList::default_instance());
private_data->socket->registerMessageType(&cura::proto::GCodePrefix::default_instance());
private_data->socket->registerMessageType(&cura::proto::SlicingFinished::default_instance());
private_data->socket->registerMessageType(&cura::proto::SettingExtruder::default_instance());
d->socket->connect(ip, port);
private_data->socket->connect(ip, port);
log("Connecting to %s:%i\n", ip.c_str(), port);
while(private_data->socket->getState() != Arcus::SocketState::Connected && private_data->socket->getState() != Arcus::SocketState::Error)
while(d->socket->state() != Arcus::SocketState::Closed && d->socket->state() != Arcus::SocketState::Error)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
log("Connected to %s:%i\n", ip.c_str(), port);
bool slice_another_time = true;
// Start & continue listening as long as socket is not closed and there is no error.
while(private_data->socket->getState() != Arcus::SocketState::Closed && private_data->socket->getState() != Arcus::SocketState::Error && slice_another_time)
{
// Actually start handling messages.
Arcus::MessagePtr message = private_data->socket->takeNextMessage();
/*
* handle a message which consists purely of a SettingList
cura::proto::SettingList* setting_list = dynamic_cast<cura::proto::SettingList*>(message.get());
if (setting_list)
if(d->objectToSlice)
{
handleSettingList(setting_list);
}
*/
/*
* handle a message which consists purely of an ObjectList
cura::proto::ObjectList* object_list = dynamic_cast<cura::proto::ObjectList*>(message.get());
if (object_list)
{
handleObjectList(object_list);
}
*/
// Handle the main Slice message
cura::proto::Slice* slice = dynamic_cast<cura::proto::Slice*>(message.get()); // See if the message is of the message type Slice; returns nullptr otherwise
if (slice)
{
logDebug("Received a Slice message\n");
const cura::proto::SettingList& global_settings = slice->global_settings();
for (auto setting : global_settings.settings())
{
FffProcessor::getInstance()->setSetting(setting.name(), setting.value());
}
// Reset object counts
private_data->object_count = 0;
for (auto object : slice->object_lists())
{
handleObjectList(&object, slice->extruders());
}
//For every object, set the extruder fallbacks from the limit_to_extruder.
for (const cura::proto::SettingExtruder setting_extruder : slice->limit_to_extruder())
{
const int32_t extruder_nr = setting_extruder.extruder(); //Implicit cast from Protobuf's int32 to normal int32.
for (std::shared_ptr<MeshGroup> meshgroup : private_data->objects_to_slice)
{
if (extruder_nr < 0 || extruder_nr >= meshgroup->getExtruderCount()) //We obtained an invalid value from the front-end. Ignore.
{
continue;
}
const ExtruderTrain* settings_base = meshgroup->getExtruderTrain(extruder_nr); //The extruder train that the setting should fall back to.
for (Mesh& mesh : meshgroup->meshes)
{
mesh.setSettingInheritBase(setting_extruder.name(), *settings_base);
}
}
}
logDebug("Done reading Slice message\n");
}
//If there is an object to slice, do so.
if (private_data->objects_to_slice.size())
{
int object_count = private_data->objects_to_slice.size();
logDebug("Slicing %i objects\n", object_count);
FffProcessor::getInstance()->resetMeshGroupNumber();
int i = 1;
for (auto object : private_data->objects_to_slice)
{
logDebug("Slicing object %i of %i\n", i, object_count);
if (!FffProcessor::getInstance()->processMeshGroup(object.get()))
{
logError("Slicing mesh group failed!");
}
i++;
}
logDebug("Done slicing objects\n");
private_data->objects_to_slice.clear();
FffProcessor::getInstance()->finalize();
flushGcode();
sendPrintTimeMaterialEstimates();
sendFinishedSlicing();
slice_another_time = false; // TODO: remove this when multiple slicing with CuraEngine is safe
//TODO: Support all-at-once/one-at-a-time printing
//private_data->processor->processModel(private_data->object_to_slice.get());
//private_data->object_to_slice.reset();
//private_data->processor->resetFileNumber();
d->processor->processModel(d->objectToSlice.get());
d->objectToSlice.reset();
d->processor->resetFileNumber();
//sendPrintTimeMaterialEstimates();
sendPrintTime();
}
Arcus::MessagePtr message = d->socket->takeNextMessage();
Cura::SettingList* settingList = dynamic_cast<Cura::SettingList*>(message.get());
if(settingList)
{
handleSettingList(settingList);
}
Cura::ObjectList* objectList = dynamic_cast<Cura::ObjectList*>(message.get());
if(objectList)
{
handleObjectList(objectList);
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if(!d->socket->errorString().empty()) {
logError("%s\n", d->socket->errorString().data());
d->socket->clearError();
}
}
log("Closing connection\n");
private_data->socket->close();
#endif
}
#ifdef ARCUS
void CommandSocket::handleObjectList(cura::proto::ObjectList* list, const google::protobuf::RepeatedPtrField<cura::proto::Extruder> settings_per_extruder_train)
void CommandSocket::handleObjectList(Cura::ObjectList* list)
{
if (list->objects_size() <= 0)
{
return;
}
FMatrix3x3 matrix;
//private_data->object_count = 0;
//private_data->object_ids.clear();
private_data->objects_to_slice.push_back(std::make_shared<MeshGroup>(FffProcessor::getInstance()));
MeshGroup* meshgroup = private_data->objects_to_slice.back().get();
d->object_count = 0;
d->objectIds.clear();
// load meshgroup settings
for (auto setting : list->settings())
d->objectToSlice = std::make_shared<PrintObject>(d->processor);
for(auto object : list->objects())
{
meshgroup->setSetting(setting.name(), setting.value());
}
d->objectToSlice->meshes.emplace_back(d->objectToSlice.get()); //Construct a new mesh and put it into printObject's mesh list.
Mesh& mesh = d->objectToSlice->meshes.back();
{ // load extruder settings
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
}
for (auto extruder : settings_per_extruder_train)
{
int extruder_nr = extruder.id();
ExtruderTrain* train = meshgroup->getExtruderTrain(extruder_nr);
for (auto setting : extruder.settings().settings())
{
train->setSetting(setting.name(), setting.value());
}
}
}
for (auto object : list->objects())
{
int bytes_per_face = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int face_count = object.vertices().size() / bytes_per_face;
if (face_count <= 0)
{
logWarning("Got an empty mesh, ignoring it!");
continue;
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("solid Cura_out\n");
// Check to which extruder train this object belongs
int extruder_train_nr = 0; // assume extruder 0 if setting wasn't supplied
for (auto setting : object.settings())
{
if (setting.name() == "extruder_nr")
{
extruder_train_nr = std::stoi(setting.value());
break;
}
}
SettingsBase* extruder_train = meshgroup->getExtruderTrain(extruder_train_nr);
meshgroup->meshes.push_back(extruder_train); //Construct a new mesh (with the corresponding extruder train as settings parent object) and put it into MeshGroup's mesh list.
Mesh& mesh = meshgroup->meshes.back();
for (int i = 0; i < face_count; ++i)
int bytesPerFace = BYTES_PER_FLOAT * FLOATS_PER_VECTOR * VECTORS_PER_FACE;
int faceCount = object.vertices().size() / bytesPerFace;
for(int i = 0; i < faceCount; ++i)
{
//TODO: Apply matrix
std::string data = object.vertices().substr(i * bytes_per_face, bytes_per_face);
const FPoint3* float_vertices = reinterpret_cast<const FPoint3*>(data.data());
std::string data = object.vertices().substr(i * bytesPerFace, bytesPerFace);
const FPoint3* floatVerts = reinterpret_cast<const FPoint3*>(data.data());
Point3 verts[3];
verts[0] = matrix.apply(float_vertices[0]);
verts[1] = matrix.apply(float_vertices[1]);
verts[2] = matrix.apply(float_vertices[2]);
verts[0] = matrix.apply(floatVerts[0]);
verts[1] = matrix.apply(floatVerts[1]);
verts[2] = matrix.apply(floatVerts[2]);
mesh.addFace(verts[0], verts[1], verts[2]);
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" facet normal -1 0 0\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" outer loop\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[0].x) <<" " << INT2MM(verts[0].y) <<" " << INT2MM(verts[0].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[1].x) <<" " << INT2MM(verts[1].y) <<" " << INT2MM(verts[1].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" vertex "<<INT2MM(verts[2].x) <<" " << INT2MM(verts[2].y) <<" " << INT2MM(verts[2].z) << "\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endloop\n");
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR(" endfacet\n");
}
DEBUG_OUTPUT_OBJECT_STL_THROUGH_CERR("endsolid Cura_out\n");
for (auto setting : object.settings())
for(auto setting : object.settings())
{
mesh.setSetting(setting.name(), setting.value());
}
d->objectIds.push_back(object.id());
mesh.finish();
}
private_data->object_count++;
meshgroup->finalize();
}
#endif
void CommandSocket::sendOptimizedLayerInfo(int layer_nr, int32_t z, int32_t height)
{
#ifdef ARCUS
std::shared_ptr<cura::proto::LayerOptimized> layer = private_data->getOptimizedLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
#endif
d->object_count++;
d->objectToSlice->finalize();
}
void CommandSocket::sendPolygons(PrintFeatureType type, const Polygons& polygons, int line_width)
void CommandSocket::handleSettingList(Cura::SettingList* list)
{
#ifdef ARCUS
if (polygons.size() == 0)
for(auto setting : list->settings())
{
d->processor->setSetting(setting.name(), setting.value());
}
}
void CommandSocket::sendLayerInfo(int layer_nr, int32_t z, int32_t height)
{
if(!d->currentSlicedObject)
{
return;
}
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
for (unsigned int i = 0; i < polygons.size(); ++i)
{
path_comp->sendPolygon(type, polygons[i], line_width);
}
}
#endif
Cura::Layer* layer = d->getLayerById(layer_nr);
layer->set_height(z);
layer->set_thickness(height);
}
void CommandSocket::sendPolygon(PrintFeatureType type, ConstPolygonRef polygon, int line_width)
void CommandSocket::sendPolygons(PolygonType type, int layer_nr, Polygons& polygons, int line_width)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
if(!d->currentSlicedObject)
return;
if (polygons.size() == 0)
return;
Cura::Layer* layer = d->getLayerById(layer_nr);
for(unsigned int i = 0; i < polygons.size(); ++i)
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->sendPolygon(type, polygon, line_width);
Cura::Polygon* p = layer->add_polygons();
p->set_type(static_cast<Cura::Polygon_Type>(type));
std::string polydata;
polydata.append(reinterpret_cast<const char*>(polygons[i].data()), polygons[i].size() * sizeof(Point));
p->set_points(polydata);
p->set_line_width(line_width);
}
#endif
}
void CommandSocket::sendLineTo(cura::PrintFeatureType type, Point to, int line_width)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->sendLineTo(type, to, line_width);
}
#endif
}
void CommandSocket::setSendCurrentPosition(Point position)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setCurrentPosition(position);
}
#endif
}
void CommandSocket::setLayerForSend(int layer_nr)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setLayer(layer_nr);
}
#endif
}
void CommandSocket::setExtruderForSend(int extruder)
{
#ifdef ARCUS
if (CommandSocket::isInstantiated())
{
auto& path_comp = CommandSocket::getInstance()->path_comp;
path_comp->setExtruder(extruder);
}
#endif
}
void CommandSocket::sendProgress(float amount)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::Progress>();
amount /= private_data->object_count;
amount += private_data->optimized_layers.sliced_objects * (1. / private_data->object_count);
auto message = std::make_shared<Cura::Progress>();
message->set_amount(amount);
private_data->socket->sendMessage(message);
#endif
d->socket->sendMessage(message);
}
void CommandSocket::sendProgressStage(Progress::Stage stage)
void CommandSocket::sendPrintTime()
{
// TODO
}
void CommandSocket::sendPrintTimeMaterialEstimates()
{
#ifdef ARCUS
logDebug("Sending print time and material estimates.\n");
auto message = std::make_shared<cura::proto::PrintTimeMaterialEstimates>();
message->set_time(FffProcessor::getInstance()->getTotalPrintTime());
int num_extruders = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
for (int extruder_nr (0); extruder_nr < num_extruders; ++extruder_nr)
{
cura::proto::MaterialEstimates* material_message = message->add_materialestimates();
material_message->set_id(extruder_nr);
material_message->set_material_amount(FffProcessor::getInstance()->getTotalFilamentUsed(extruder_nr));
}
private_data->socket->sendMessage(message);
logDebug("Done sending print time and material estimates.\n");
#endif
auto message = std::make_shared<Cura::ObjectPrintTime>();
message->set_time(d->processor->getTotalPrintTime());
message->set_material_amount(d->processor->getTotalFilamentUsed(0));
d->socket->sendMessage(message);
}
void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float print_time)
@@ -626,202 +208,67 @@ void CommandSocket::sendPrintMaterialForObject(int index, int extruder_nr, float
// socket.sendFloat32(print_time);
}
void CommandSocket::sendLayerData()
void CommandSocket::beginSendSlicedObject()
{
#ifdef ARCUS
#endif
#ifdef ARCUS
auto& data = private_data->sliced_layers;
data.sliced_objects++;
data.current_layer_offset = data.current_layer_count;
// log("End sliced object called. Sending %d layers.", data.current_layer_count);
// Only send the data to the front end when all mesh groups have been processed.
if (data.sliced_objects >= private_data->object_count)
if(!d->slicedObjectList)
{
for (std::pair<const int, std::shared_ptr<cura::proto::Layer>> entry : data.slice_data) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
data.sliced_objects = 0;
data.current_layer_count = 0;
data.current_layer_offset = 0;
data.slice_data.clear();
d->slicedObjectList = std::make_shared<Cura::SlicedObjectList>();
}
#endif
d->currentSlicedObject = d->slicedObjectList->add_objects();
d->currentSlicedObject->set_id(d->objectIds[d->slicedObjects]);
}
void CommandSocket::sendOptimizedLayerData()
void CommandSocket::endSendSlicedObject()
{
#ifdef ARCUS
path_comp->flushPathSegments(); // make sure the last path segment has been flushed from the compiler
auto& data = private_data->optimized_layers;
data.sliced_objects++;
data.current_layer_offset = data.current_layer_count;
log("End sliced object called. Sending %d layers.", data.current_layer_count);
if (data.sliced_objects >= private_data->object_count)
d->slicedObjects++;
if(d->slicedObjects >= d->object_count)
{
for (std::pair<const int, std::shared_ptr<cura::proto::LayerOptimized>> entry : data.slice_data) //Note: This is in no particular order!
{
private_data->socket->sendMessage(entry.second); //Send the actual layers.
}
data.sliced_objects = 0;
data.current_layer_count = 0;
data.current_layer_offset = 0;
data.slice_data.clear();
d->socket->sendMessage(d->slicedObjectList);
d->slicedObjects = 0;
d->slicedObjectList.reset();
d->currentSlicedObject = nullptr;
}
#endif
}
void CommandSocket::sendFinishedSlicing()
{
#ifdef ARCUS
logDebug("Sending Slicing Finished message.\n");
std::shared_ptr<cura::proto::SlicingFinished> done_message = std::make_shared<cura::proto::SlicingFinished>();
private_data->socket->sendMessage(done_message);
logDebug("Done sending Slicing Finished message.\n");
#endif
}
void CommandSocket::beginGCode()
{
#ifdef ARCUS
FffProcessor::getInstance()->setTargetStream(&private_data->gcode_output_stream);
#endif
d->processor->setTargetStream(&d->gcode_output_stream);
}
void CommandSocket::flushGcode()
void CommandSocket::sendGCodeLayer()
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodeLayer>();
message->set_data(private_data->gcode_output_stream.str());
private_data->socket->sendMessage(message);
auto message = std::make_shared<Cura::GCodeLayer>();
message->set_id(d->objectIds[0]);
message->set_data(d->gcode_output_stream.str());
d->socket->sendMessage(message);
private_data->gcode_output_stream.str("");
#endif
d->gcode_output_stream.str("");
}
void CommandSocket::sendGCodePrefix(std::string prefix)
{
#ifdef ARCUS
auto message = std::make_shared<cura::proto::GCodePrefix>();
auto message = std::make_shared<Cura::GCodePrefix>();
message->set_data(prefix);
private_data->socket->sendMessage(message);
#endif
d->socket->sendMessage(message);
}
#ifdef ARCUS
std::shared_ptr<cura::proto::Layer> CommandSocket::Private::getLayerById(int id)
Cura::Layer* CommandSocket::Private::getLayerById(int id)
{
id += sliced_layers.current_layer_offset;
auto itr = std::find_if(currentSlicedObject->mutable_layers()->begin(), currentSlicedObject->mutable_layers()->end(), [id](Cura::Layer& l) { return l.id() == id; });
auto itr = sliced_layers.slice_data.find(id);
std::shared_ptr<cura::proto::Layer> layer;
if (itr != sliced_layers.slice_data.end())
Cura::Layer* layer = nullptr;
if(itr != currentSlicedObject->mutable_layers()->end())
{
layer = itr->second;
layer = &(*itr);
}
else
{
layer = std::make_shared<cura::proto::Layer>();
layer = currentSlicedObject->add_layers();
layer->set_id(id);
sliced_layers.current_layer_count++;
sliced_layers.slice_data[id] = layer;
}
return layer;
}
#endif
#ifdef ARCUS
std::shared_ptr<cura::proto::LayerOptimized> CommandSocket::Private::getOptimizedLayerById(int id)
{
id += optimized_layers.current_layer_offset;
auto itr = optimized_layers.slice_data.find(id);
std::shared_ptr<cura::proto::LayerOptimized> layer;
if (itr != optimized_layers.slice_data.end())
{
layer = itr->second;
}
else
{
layer = std::make_shared<cura::proto::LayerOptimized>();
layer->set_id(id);
optimized_layers.current_layer_count++;
optimized_layers.slice_data[id] = layer;
}
return layer;
}
#endif
#ifdef ARCUS
void CommandSocket::PathCompiler::flushPathSegments()
{
if (line_types.size() > 0 && CommandSocket::isInstantiated())
{
std::shared_ptr<cura::proto::LayerOptimized> proto_layer = _cs_private_data.getOptimizedLayerById(_layer_nr);
cura::proto::PathSegment* p = proto_layer->add_path_segment();
p->set_extruder(extruder);
p->set_point_type(data_point_type);
std::string line_type_data;
line_type_data.append(reinterpret_cast<const char*>(line_types.data()), line_types.size()*sizeof(PrintFeatureType));
p->set_line_type(line_type_data);
std::string polydata;
polydata.append(reinterpret_cast<const char*>(points.data()), points.size() * sizeof(float));
p->set_points(polydata);
std::string line_width_data;
line_width_data.append(reinterpret_cast<const char*>(line_widths.data()), line_widths.size()*sizeof(float));
p->set_line_width(line_width_data);
}
points.clear();
line_widths.clear();
line_types.clear();
}
void CommandSocket::PathCompiler::sendLineTo(PrintFeatureType print_feature_type, Point to, int width)
{
assert(points.size() > 0 && "A point must already be in the buffer for sendLineTo(.) to function properly");
if (to != last_point)
{
addLineSegment(print_feature_type, to, width);
}
}
void CommandSocket::PathCompiler::sendPolygon(PrintFeatureType print_feature_type, ConstPolygonRef polygon, int width)
{
if (polygon.size() < 2)
{
return;
}
auto it = polygon.begin();
handleInitialPoint(*it);
const auto it_end = polygon.end();
while (++it != it_end)
{
// Ignore zero-length segments.
if (*it != last_point)
{
addLineSegment(print_feature_type, *it, width);
}
}
// Make sure the polygon is closed
if (*polygon.begin() != polygon.back())
{
addLineSegment(print_feature_type, *polygon.begin(), width);
}
}
#endif
}//namespace cura
+14 -127
Ver Arquivo
@@ -3,154 +3,41 @@
#include "utils/socket.h"
#include "utils/polygon.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "PrintFeature.h"
#include "settings.h"
#include <memory>
#ifdef ARCUS
#include "Cura.pb.h"
#endif
namespace cura
{
namespace cura {
class fffProcessor;
class CommandSocket
{
private:
static CommandSocket* instance; //!< May be a nullptr in case it hasn't been instantiated.
CommandSocket(); //!< The single constructor is known only privately, since this class is similar to a singleton class (except the single object doesn't need to be instantiated)
public:
static CommandSocket* getInstance(); //!< Get the CommandSocket instance, or nullptr if it hasn't been instantiated.
CommandSocket(fffProcessor* processor);
static void instantiate(); //!< Instantiate the CommandSocket.
static bool isInstantiated(); //!< Check whether the singleton is instantiated
/*!
* Connect with the GUI
* This creates and initialises the arcus socket and then continues listening for messages.
* \param ip string containing the ip to connect with
* \param port int of the port to connect with.
*/
void connect(const std::string& ip, int port);
#ifdef ARCUS
/*!
* Handler for ObjectList message.
* Loads all objects from the message and starts the slicing process
*
* Also handles meshgroup settings and extruder settings.
*
* \param[in] list The list of objects to slice
* \param[in] settings_per_extruder_train The extruder train settings to load into the meshgroup
*/
void handleObjectList(cura::proto::ObjectList* list, const google::protobuf::RepeatedPtrField<cura::proto::Extruder> settings_per_extruder_train);
#endif
/*!
* Send info on an optimized layer to be displayed by the forntend: set the z and the thickness of the layer.
*/
void sendOptimizedLayerInfo(int layer_nr, int32_t z, int32_t height);
/*!
* Send a polygon to the front-end. This is used for the layerview in the GUI
*/
static void sendPolygons(cura::PrintFeatureType type, const cura::Polygons& polygons, int line_width);
/*!
* Send a polygon to the front-end. This is used for the layerview in the GUI
*/
static void sendPolygon(cura::PrintFeatureType type, ConstPolygonRef polygon, int line_width);
/*!
* Send a line to the front-end. This is used for the layerview in the GUI
*/
static void sendLineTo(cura::PrintFeatureType type, Point to, int line_width);
/*!
* Set the current position of the path compiler to \p position. This is used for the layerview in the GUI
*/
static void setSendCurrentPosition(Point position);
/*!
* Set which layer is being used for the following calls to SendPolygons, SendPolygon and SendLineTo.
*/
static void setLayerForSend(int layer_nr);
/*!
* Set which extruder is being used for the following calls to SendPolygons, SendPolygon and SendLineTo.
*/
static void setExtruderForSend(int extruder);
/*!
* Send a polygon to the front-end if the command socket is instantiated. This is used for the layerview in the GUI
*/
static void sendPolygonsToCommandSocket(cura::PrintFeatureType type, int layer_nr, const cura::Polygons& polygons, int line_width);
/*!
* Send progress to GUI
*/
void handleObjectList(Cura::ObjectList* list);
void handleSettingList(Cura::SettingList* list);
void sendLayerInfo(int layer_nr, int32_t z, int32_t height);
void sendPolygons(cura::PolygonType type, int layer_nr, cura::Polygons& polygons, int line_width);
void sendProgress(float amount);
/*!
* Send the current stage of the process to the GUI (starting, slicing infill, etc)
*/
void sendProgressStage(Progress::Stage stage);
/*!
* Send time estimate of how long print would take.
*/
void sendPrintTimeMaterialEstimates();
/*!
* Does nothing at the moment
*/
void sendPrintTime();
void sendPrintMaterialForObject(int index, int extruder_nr, float material_amount);
/*!
* Send the slices of the model as polygons to the GUI.
*
* The GUI may use this to visualize the early result of the slicing
* process.
*/
void sendLayerData();
/*!
* Send the sliced layer data to the GUI after the optimization is done and
* the actual order in which to print has been set.
*
* The GUI may use this to visualize the g-code, so that the user can
* inspect the result of slicing.
*/
void sendOptimizedLayerData();
/*!
* \brief Sends a message to indicate that all the slicing is done.
*
* This should indicate that no more data (g-code, prefix/postfix, metadata
* or otherwise) should be sent any more regarding the latest slice job.
*/
void sendFinishedSlicing();
void beginSendSlicedObject();
void endSendSlicedObject();
void beginGCode();
/*!
* Flush the gcode in gcode_output_stream into a message queued in the socket.
*/
void flushGcode();
void sendGCodeLayer();
void sendGCodePrefix(std::string prefix);
#ifdef ARCUS
private:
class Private;
const std::unique_ptr<Private> private_data;
class PathCompiler;
const std::unique_ptr<PathCompiler> path_comp;
#endif
const std::unique_ptr<Private> d;
};
}//namespace cura
+61
Ver Arquivo
@@ -0,0 +1,61 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <string.h>
#define __FILE_NAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define DEBUG_HERE std::cerr << __FILE_NAME__ << " : " << __LINE__ << std::endl
#define DEBUG 1
#define DEBUG_SHOW_LINE 0
#if DEBUG_SHOW_LINE == 1
#define DEBUG_FILE_LINE __FILE_NAME__ << "." << __LINE__ << ": "
#else
#define DEBUG_FILE_LINE ""
#endif
#if DEBUG == 1
# define DEBUG_DO(x) do { x } while (0)
# define DEBUG_SHOW(x) do { std::cerr << DEBUG_FILE_LINE << #x << " = " << x << std::endl; } while (0)
# define DEBUG_PRINTLN(x) do { std::cerr << DEBUG_FILE_LINE << x << std::endl; } while (0)
#else
# define DEBUG_DO(x)
# define DEBUG_SHOW(x)
# define DEBUG_PRINTLN(x)
#endif
#include <sstream>
#if 0==1
#define ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT};
#endif
#define ENUM(name, ...) enum class name { __VA_ARGS__}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
else if(str[i] == ',') { \
strings.push_back(temp.str()); \
temp.str(std::string());\
} \
else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;}
#endif // DEBUG_H
+1189
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+254 -712
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+123 -315
Ver Arquivo
@@ -4,388 +4,196 @@
#include <stdio.h>
#include <deque> // for extrusionAmountAtPreviousRetractions
#include <sstream> // for stream.str()
#include "settings/settings.h"
#include "settings.h"
#include "utils/intpoint.h"
#include "utils/NoCopy.h"
#include "timeEstimate.h"
#include "MeshGroup.h"
#include "commandSocket.h"
#include "RetractionConfig.h"
#include "multithreadOpenMP.h"
namespace cura {
/*!
* Coasting configuration used during printing.
* Can differ per extruder.
*
* Might be used in the future to have different coasting per feature, e.g. outer wall only.
*/
struct CoastingConfig
class RetractionConfig
{
bool coasting_enable; //!< Whether coasting is enabled on the extruder to which this config is attached
double coasting_volume; //!< The volume leeked when printing without feeding
double coasting_speed; //!< A modifier (0-1) on the last used travel speed to move slower during coasting
double coasting_min_volume; //!< The minimal volume printed to build up enough pressure to leek the coasting_volume
public:
double amount; //!< The amount
int speed;
int primeSpeed;
double primeAmount;
int zHop;
};
//The GCodePathConfig is the configuration for moves/extrusion actions. This defines at which width the line is printed and at which speed.
class GCodePathConfig
{
private:
int speed;
int line_width;
int flow;
int layer_thickness;
double extrusion_mm3_per_mm;
public:
const char* name;
bool spiralize;
RetractionConfig* retraction_config;
GCodePathConfig() : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(nullptr), spiralize(false), retraction_config(nullptr) {}
GCodePathConfig(RetractionConfig* retraction_config, const char* name) : speed(0), line_width(0), extrusion_mm3_per_mm(0.0), name(name), spiralize(false), retraction_config(retraction_config) {}
void setSpeed(int speed)
{
this->speed = speed;
}
void setLineWidth(int line_width)
{
this->line_width = line_width;
calculateExtrusion();
}
void setLayerHeight(int layer_height)
{
this->layer_thickness = layer_height;
calculateExtrusion();
}
void setFlow(int flow)
{
this->flow = flow;
calculateExtrusion();
}
void smoothSpeed(int min_speed, int layer_nr, int max_speed_layer)
{
speed = (speed*layer_nr)/max_speed_layer + (min_speed*(max_speed_layer-layer_nr)/max_speed_layer);
}
double getExtrusionMM3perMM()
{
return extrusion_mm3_per_mm;
}
int getSpeed()
{
return speed;
}
int getLineWidth()
{
return line_width;
}
private:
void calculateExtrusion()
{
extrusion_mm3_per_mm = INT2MM(line_width) * INT2MM(layer_thickness) * double(flow) / 100.0;
}
};
//The GCodeExport class writes the actual GCode. This is the only class that knows how GCode looks and feels.
// Any customizations on GCodes flavors are done in this class.
class GCodeExport : public NoCopy
class GCodeExport
{
private:
struct ExtruderTrainAttributes
{
Point3 prime_pos; //!< The location this nozzle is primed before printing
bool prime_pos_is_abs; //!< Whether the prime position is absolute, rather than relative to the last given position
bool is_primed; //!< Whether this extruder has currently already been primed in this print
bool is_used; //!< Whether this extruder train is actually used during the printing of all meshgroups
int nozzle_size; //!< The nozzle size label of the nozzle (e.g. 0.4mm; irrespective of tolerances)
Point nozzle_offset;
char extruderCharacter;
std::string material_guid; //!< The GUID for the material used by this extruder
std::string start_code;
std::string end_code;
double filament_area; //!< in mm^2 for non-volumetric, cylindrical filament
double totalFilament; //!< total filament used per extruder in mm^3
int currentTemperature;
int initial_temp; //!< Temperature this nozzle needs to be at the start of the print.
double retraction_e_amount_current; //!< The current retracted amount (in mm or mm^3), or zero(i.e. false) if it is not currently retracted (positive values mean retracted amount, so negative impact on E values)
double retraction_e_amount_at_e_start; //!< The ExtruderTrainAttributes::retraction_amount_current value at E0, i.e. the offset (in mm or mm^3) from E0 to the situation where the filament is at the tip of the nozzle.
double prime_volume; //!< Amount of material (in mm^3) to be primed after an unretration (due to oozing and/or coasting)
double last_retraction_prime_speed; //!< The last prime speed (in mm/s) of the to-be-primed amount
std::deque<double> extruded_volume_at_previous_n_retractions; // in mm^3
ExtruderTrainAttributes()
: prime_pos(0, 0, 0)
, prime_pos_is_abs(false)
, is_primed(false)
, is_used(false)
, nozzle_offset(0,0)
, extruderCharacter(0)
, start_code("")
, end_code("")
, filament_area(0)
, totalFilament(0)
, currentTemperature(0)
, initial_temp(0)
, retraction_e_amount_current(0.0)
, retraction_e_amount_at_e_start(0.0)
, prime_volume(0.0)
, last_retraction_prime_speed(0.0)
{ }
};
ExtruderTrainAttributes extruder_attr[MAX_EXTRUDERS];
unsigned int extruder_count;
bool use_extruder_offset_to_offset_coords;
Point3 machine_dimensions;
std::string machine_name;
std::ostream* output_stream;
#ifdef _OPENMP
omp_nest_lock_type output_stream_lock;
#endif
std::string new_line;
double current_e_value; //!< The last E value written to gcode (in mm or mm^3)
double extrusion_amount; // in mm or mm^3
double extruderSwitchRetraction;
int extruderSwitchRetractionSpeed;
int extruderSwitchPrimeSpeed;
double retraction_extrusion_window;
int retraction_count_max;
std::deque<double> extrusion_amount_at_previous_n_retractions; // in mm or mm^3
Point3 currentPosition;
double currentSpeed; //!< The current speed (F values / 60) in mm/s
double current_acceleration; //!< The current acceleration in the XY direction (in mm/s^2)
double current_jerk; //!< The current jerk in the XY direction (in mm/s^3)
double current_max_z_feedrate; //!< The current max z speed
AABB3D total_bounding_box; //!< The bounding box of all g-code.
/*!
* The z position to be used on the next xy move, if the head wasn't in the correct z position yet.
*
* \see GCodeExport::writeMove(Point, double, double)
*
* \note After GCodeExport::writeMove(Point, double, double) has been called currentPosition.z coincides with this value
*/
int current_layer_z;
int isZHopped; //!< The amount by which the print head is currently z hopped, or zero if it is not z hopped. (A z hop is used during travel moves to avoid collision with other layer parts)
Point3 startPosition;
Point extruderOffset[MAX_EXTRUDERS];
char extruderCharacter[MAX_EXTRUDERS];
int currentTemperature[MAX_EXTRUDERS];
int currentSpeed;
int zPos;
bool isRetracted;
bool isZHopped;
int retractionPrimeSpeed;
int current_extruder;
int currentFanSpeed;
EGCodeFlavor flavor;
double totalPrintTime; //!< The total estimated print time in seconds
std::string preSwitchExtruderCode[MAX_EXTRUDERS];
std::string postSwitchExtruderCode[MAX_EXTRUDERS];
double totalFilament[MAX_EXTRUDERS]; // in mm^3
double filament_diameter[MAX_EXTRUDERS]; // in mm^3
double totalPrintTime;
TimeEstimateCalculator estimateCalculator;
bool is_volumatric;
bool firmware_retract; //!< whether retractions are done in the firmware, or hardcoded in E values.
unsigned int layer_nr; //!< for sending travel data
int initial_bed_temp; //!< bed temperature at the beginning of the print.
protected:
/*!
* Convert an E value to a value in mm (if it wasn't already in mm) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param e the value to convert
* \return the value converted to mm
*/
double eToMm(double e);
/*!
* Convert a volume value to an E value (which might be volumetric as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm3 the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mm3ToE(double mm3);
/*!
* Convert a distance value to an E value (which might be linear/distance based as well) for the current extruder.
*
* E values are either in mm or in mm^3
* The current extruder is used to determine the filament area to make the conversion.
*
* \param mm the value to convert
* \return the value converted to mm or mm3 depending on whether the E axis is volumetric
*/
double mmToE(double mm);
public:
GCodeExport();
~GCodeExport();
/*!
* Get the gcode file header (e.g. ";FLAVOR:UltiGCode\n")
*
* \param print_time The total print time in seconds of the whole gcode (if known)
* \param filament_used The total mm^3 filament used for each extruder or a vector of the wrong size of unknown
* \param mat_ids The material GUIDs for each material.
* \return The string representing the file header
*/
std::string getFileHeader(const double* print_time = nullptr, const std::vector<double>& filament_used = std::vector<double>(), const std::vector<std::string>& mat_ids = std::vector<std::string>());
void setLayerNr(unsigned int layer_nr);
void setOutputStream(std::ostream* stream);
#ifdef _OPENMP
omp_nest_lock_type& getOutputStreamLock()
{
return output_stream_lock;
}
#endif
bool getExtruderIsUsed(const int extruder_nr) const; //!< Returns whether the extruder with the given index is used up until the current meshgroup
int getNozzleSize(const int extruder_nr) const;
Point getExtruderOffset(const int id) const;
std::string getMaterialGUID(const int extruder_nr) const; //!< returns the GUID of the material used for the nozzle with id \p extruder_nr
Point getGcodePos(const int64_t x, const int64_t y, const int extruder_train) const;
void setExtruderOffset(int id, Point p);
Point getExtruderOffset(int id);
void setSwitchExtruderCode(int id, std::string preSwitchExtruderCode, std::string postSwitchExtruderCode);
void setFlavor(EGCodeFlavor flavor);
EGCodeFlavor getFlavor();
void setRetractionSettings(int extruderSwitchRetraction, int extruderSwitchRetractionSpeed, int extruderSwitchPrimeSpeed, int minimalExtrusionBeforeRetraction, int retraction_count_max);
void setZ(int z);
void addLastCoastedVolume(double last_coasted_volume)
{
extruder_attr[current_extruder].prime_volume += last_coasted_volume;
}
Point3 getPosition();
Point getPositionXY();
void resetStartPosition();
Point getStartPositionXY();
int getPositionZ();
int getExtruderNr();
void setFilamentDiameter(unsigned int n, int diameter);
double getFilamentArea(unsigned int extruder);
double getCurrentExtrudedVolume();
double getExtrusionAmountMM3(unsigned int extruder);
double getTotalFilamentUsed(int e);
/*!
* Get the total extruded volume for a specific extruder in mm^3
*
* Retractions and unretractions don't contribute to this.
*
* \param extruder_nr The extruder number for which to get the total netto extruded volume
* \return total filament printed in mm^3
*/
double getTotalFilamentUsed(int extruder_nr);
/*!
* Get the total estimated print time in seconds
*
* \return total print time in seconds
*/
double getTotalPrintTime();
void updateTotalPrintTime();
void resetTotalPrintTimeAndFilament();
void writeComment(std::string comment);
void writeTypeComment(PrintFeatureType type);
/*!
* Write a comment saying what (estimated) time has passed up to this point
*
* \param time The time passed up till this point
*/
void writeTimeComment(const double time);
void writeTypeComment(const char* type);
void writeLayerComment(int layer_nr);
void writeLayerCountComment(int layer_count);
void writeLine(const char* line);
/*!
* Reset the current_e_value to prevent too high E values.
*
* The current extruded volume is added to the current extruder_attr.
*/
void resetExtrusionValue();
void writeDelay(double timeAmount);
void writeMove(Point p, double speed, double extrusion_mm3_per_mm);
void writeMove(Point p, int speed, double extrusion_per_mm);
void writeMove(Point3 p, double speed, double extrusion_mm3_per_mm);
void writeMove(Point3 p, int speed, double extrusion_per_mm);
private:
void writeMove(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
/*!
* The writeMove when flavor == BFB
*/
void writeMoveBFB(int x, int y, int z, double speed, double extrusion_mm3_per_mm);
void writeMove(int x, int y, int z, int speed, double extrusion_per_mm);
public:
void writeRetraction(const RetractionConfig& config, bool force = false, bool extruder_switch = false);
/*!
* Start a z hop with the given \p hop_height
*
* \param hop_height The height to move above the current layer
*/
void writeZhopStart(int hop_height);
/*!
* End a z hop: go back to the layer height
*
*/
void writeZhopEnd();
/*!
* Start the new_extruder:
* - set new extruder
* - zero E value
* - write extruder start gcode
*
* \param new_extruder The extruder to start with
*/
void startExtruder(int new_extruder);
/*!
* Switch to the new_extruder:
* - perform neccesary retractions
* - fiddle with E-values
* - write extruder end gcode
* - set new extruder
* - write extruder start gcode
*
* \param new_extruder The extruder to switch to
* \param retraction_config_old_extruder The extruder switch retraction config of the old extruder, to perform the extruder switch retraction with.
*/
void switchExtruder(int new_extruder, const RetractionConfig& retraction_config_old_extruder);
void writeRetraction(RetractionConfig* config, bool force=false);
void switchExtruder(int newExtruder);
void writeCode(const char* str);
/*!
* Write the gcode for priming the current extruder train so that it can be used.
*
* \param travel_speed The travel speed when priming involves a movement
*/
void writePrimeTrain(double travel_speed);
void writeFanCommand(int speed);
void writeFanCommand(double speed);
void writeTemperatureCommand(int extruder, int temperature, bool wait = false);
void writeBedTemperatureCommand(int temperature, bool wait = false);
void writeTemperatureCommand(int extruder, double temperature, bool wait = false);
void writeBedTemperatureCommand(double temperature, bool wait = false);
/*!
* Write the command for setting the acceleration to a specific value
*/
void writeAcceleration(double acceleration);
/*!
* Write the command for setting the jerk to a specific value
*/
void writeJerk(double jerk);
/*!
* Write the command for setting the maximum z feedrate to a specific value
*/
void writeMaxZFeedrate(double max_z_feedrate);
/*!
* Get the last set max z feedrate value sent in the gcode.
*
* Returns a value <= 0 when no value is set.
*/
double getCurrentMaxZFeedrate();
/*!
* Set member variables using the settings in \p settings
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
*/
void preSetup(const MeshGroup* settings);
/*!
* Handle the initial (bed/nozzle) temperatures before any gcode is processed.
* These temperatures are set in the pre-print setup in the firmware.
*
* See FffGcodeWriter::processStartingCode
*
* \param settings The meshgroup to get the global bed temp from and to get the extruder trains from which to get the nozzle temperatures
* \param start_extruder_nr The extruder with which to start this print
*/
void setInitialTemps(const MeshGroup& settings, const unsigned int start_extruder_nr);
/*!
* Override or set an initial nozzle temperature as written by GCodeExport::setInitialTemps
* This is used primarily during better specification of temperatures in LayerPlanBuffer::insertPreheatCommand
*
* \warning This function must be called before any of the layers in the meshgroup are written to file!
* That's because it sets the current temperature in the gcode!
*
* \param extruder_nr The extruder number for which to better specify the temp
* \param temp The temp at which the nozzle should be at startup
*/
void setInitialTemp(int extruder_nr, double temp);
/*!
* Finish the gcode: turn fans off, write end gcode and flush all gcode left in the buffer.
*
* \param endCode The end gcode to be appended at the very end.
*/
void finalize(const char* endCode);
void finalize(int maxObjectHeight, int moveSpeed, const char* endCode);
};
}
#endif//GCODEEXPORT_H
+191 -1026
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+83 -498
Ver Arquivo
@@ -1,539 +1,124 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef GCODE_PLANNER_H
#define GCODE_PLANNER_H
#include <vector>
#include "gcodeExport.h"
#include "pathPlanning/Comb.h"
#include "pathPlanning/GCodePath.h"
#include "pathPlanning/NozzleTempInsert.h"
#include "pathPlanning/TimeMaterialEstimates.h"
#include "comb.h"
#include "utils/polygon.h"
#include "utils/logoutput.h"
#include "wallOverlap.h"
#include "commandSocket.h"
#include "FanSpeedLayerTime.h"
#include "SpaceFillType.h"
#include "GCodePathConfig.h"
#include "utils/optional.h"
namespace cura
{
class SliceDataStorage;
class GCodePlanner; // forward declaration so that ExtruderPlan can be a friend
class LayerPlanBuffer; // forward declaration so that ExtruderPlan can be a friend
/*!
* An extruder plan contains all planned paths (GCodePath) pertaining to a single extruder train.
*
* It allows for temperature command inserts which can be inserted in between paths.
*/
class ExtruderPlan
class GCodePath
{
friend class GCodePlanner; // TODO: GCodePlanner still does a lot which should actually be handled in this class.
friend class LayerPlanBuffer; // TODO: LayerPlanBuffer handles paths directly
protected:
std::list<GCodePath> paths_list; //!< The paths planned for this extruder
std::vector<GCodePath> paths_vector; //!< The paths planned for this extruder
std::list<NozzleTempInsert> inserts; //!< The nozzle temperature command inserts, to be inserted in between paths
bool is_paths_vector_initialised; //!< Keeps information if content of \p paths_list has been copied to \p paths_vector
int extruder; //!< The extruder used for this paths in the current plan.
double heated_pre_travel_time; //!< The time at the start of this ExtruderPlan during which the head travels and has a temperature of initial_print_temperature
double initial_printing_temperature; //!< The required temperature at the start of this extruder plan.
double printing_temperature; //!< The normal temperature for printing this extruder plan. That start and end of this extruder plan may deviate because of the initial and final print temp
std::optional<std::list<NozzleTempInsert>::iterator> printing_temperature_command; //!< The command to heat from the printing temperature of this extruder plan to the printing temperature of the next extruder plan (if it has the same extruder).
std::optional<double> prev_extruder_standby_temp; //!< The temperature to which to set the previous extruder. Not used if the previous extruder plan was the same extruder.
TimeMaterialEstimates estimates; //!< Accumulated time and material estimates for all planned paths within this extruder plan.
public:
/*!
* Simple contructor.
*
* \warning Doesn't set the required temperature yet.
*
* \param extruder The extruder number for which this object is a plan.
* \param start_position The position the head is when this extruder plan starts
*/
ExtruderPlan(int extruder, Point start_position, int layer_nr, bool is_initial_layer, int layer_thickness, FanSpeedLayerTimeSettings& fan_speed_layer_time_settings, const RetractionConfig& retraction_config);
/*!
* Add a new Insert, constructed with the given arguments
*
* \see NozzleTempInsert
*
* \param contructor_args The arguments for the constructor of an insert
*/
template<typename... Args>
void insertCommand(Args&&... contructor_args)
{
inserts.emplace_back(contructor_args...);
}
/*!
* Insert the inserts into gcode which should be inserted before \p path_idx
*
* \param path_idx The index into ExtruderPlan::paths which is currently being consider for temperature command insertion
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleInserts(unsigned int& path_idx, GCodeExport& gcode)
{
while ( ! inserts.empty() && path_idx >= inserts.front().path_idx)
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
inserts.front().write(gcode);
inserts.pop_front();
}
}
/*!
* Insert all remaining temp inserts into gcode, to be called at the end of an extruder plan
*
* Inserts temperature commands which should be inserted _after_ the last path.
* Also inserts all temperatures which should have been inserted earlier,
* but for which ExtruderPlan::handleInserts hasn't been called correctly.
*
* \param gcode The gcode exporter to which to write the temperature command.
*/
void handleAllRemainingInserts(GCodeExport& gcode)
{
while ( ! inserts.empty() )
{ // handle the Insert to be inserted before this path_idx (and all inserts not handled yet)
NozzleTempInsert& insert = inserts.front();
assert(insert.path_idx == getPaths().size());
insert.write(gcode);
inserts.pop_front();
}
}
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*
* \param force_minimal_layer_time Whether we should apply speed changes and perhaps a head lift in order to meet the minimal layer time
*/
void processFanSpeedAndMinimalLayerTime(bool force_minimal_layer_time);
/*!
* Set the extrude speed factor. This is used for printing slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the extrusion move speed
*/
void setExtrudeSpeedFactor(double speedFactor);
/*!
* Get the extrude speed factor. This is used for printing slower than normal.
*
* \return The factor by which to alter the extrusion move speed
*/
double getExtrudeSpeedFactor();
/*!
* Set the travel speed factor. This is used for performing non-extrusion travel moves slower than normal.
*
* Leaves the extrusion speed as is for values of 1.0
*
* \param speedFactor The factor by which to alter the non-extrusion move speed
*/
void setTravelSpeedFactor(double speedFactor);
/*!
* Get the travel speed factor. This is used for travelling slower than normal.
*
* Limited to at most 1.0
*
* \return The factor by which to alter the non-extrusion move speed
*/
double getTravelSpeedFactor();
/*!
* Get the fan speed computed for this extruder plan
*
* \warning assumes ExtruderPlan::processFanSpeedAndMinimalLayerTime has already been called
*
* \return The fan speed computed in processFanSpeedAndMinimalLayerTime
*/
double getFanSpeed();
/*!
* Move the paths data from the input list to the vector container
*
* \warning empties the \p paths_list which will no longer contain data. No references to the paths in \p paths_list should be kept.
*/
void convertListToVector();
/*!
* Get the paths in a list container
*
* \warning should not be called after paths_list has been converted to paths variable
*
* \return The paths as a list
*/
std::list<GCodePath>& getPathsList()
{
assert(!is_paths_vector_initialised);
return paths_list;
}
/*!
* Get the paths in a vector container
*
* \warning should not be called before paths_list has been converted to paths variable
*
* \return The paths as a vector
*/
std::vector<GCodePath>& getPaths()
{
assert(is_paths_vector_initialised);
return paths_vector;
}
protected:
Point start_position; //!< The position the print head was at at the start of this extruder plan
int layer_nr; //!< The layer number at which we are currently printing.
bool is_initial_layer; //!< Whether this extruder plan is printed on the very first layer (which might be raft)
int layer_thickness; //!< The thickness of this layer in Z-direction
FanSpeedLayerTimeSettings& fan_speed_layer_time_settings; //!< The fan speed and layer time settings used to limit this extruder plan
const RetractionConfig& retraction_config; //!< The retraction settings for the extruder of this plan
double extrudeSpeedFactor; //!< The factor by which to alter the extrusion move speed
double travelSpeedFactor; //!< The factor by which to alter the non-extrusion move speed
double extraTime; //!< Extra waiting time at the and of this extruder plan, so that the filament can cool
double totalPrintTime; //!< The total naive time estimate for this extruder plan
double fan_speed; //!< The fan speed to be used during this extruder plan
/*!
* Set the fan speed to be used while printing this extruder plan
*
* \param fan_speed The speed for the fan
*/
void setFanSpeed(double fan_speed);
/*!
* Force the minimal layer time to hold by slowing down and lifting the head if required.
*
*/
void forceMinimalLayerTime(double minTime, double minimalSpeed, double travelTime, double extrusionTime);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
GCodePathConfig* config;
bool retract;
int extruder;
std::vector<Point> points;
bool done;//Path is finished, no more moves should be added, and a new path should be started instead of any appending done to this one.
};
class LayerPlanBuffer; // forward declaration to prevent circular dependency
/*!
* The GCodePlanner class stores multiple moves that are planned.
*
*
* It facilitates the combing to keep the head inside the print.
* It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
*
* A GCodePlanner is also knows as a 'layer plan'.
*
*/
class GCodePlanner : public NoCopy
//The GCodePlanner class stores multiple moves that are planned.
// It facilitates the combing to keep the head inside the print.
// It also keeps track of the print time estimate for this planning so speed adjustments can be made for the minimal-layer-time.
class GCodePlanner
{
friend class LayerPlanBuffer;
friend class GCodePlannerTest;
private:
SliceDataStorage& storage; //!< The polygon data obtained from FffPolygonProcessor
GCodeExport& gcode;
int layer_nr; //!< The layer number of this layer plan
int is_initial_layer; //!< Whether this is the first layer (which might be raft)
int z;
int layer_thickness;
Point start_position;
Point lastPosition;
std::vector<ExtruderPlan> extruder_plans; //!< should always contain at least one ExtruderPlan
int last_extruder_previous_layer; //!< The last id of the extruder with which was printed in the previous layer
SettingsBaseVirtual* last_planned_extruder_setting_base; //!< The setting base of the last planned extruder.
bool was_inside; //!< Whether the last planned (extrusion) move was inside a layer part
bool is_inside; //!< Whether the destination of the next planned travel move is inside a layer part
Polygons comb_boundary_inside; //!< The boundary within which to comb, or to move into when performing a retraction.
std::vector<GCodePath> paths;
Comb* comb;
std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder;
GCodePathConfig travelConfig;
int extrudeSpeedFactor;
int travelSpeedFactor;
int currentExtruder;
int retractionMinimalDistance;
bool forceRetraction;
bool alwaysRetract;
double extraTime;
double totalPrintTime;
int gcode_written;
private:
/*!
* Either create a new path with the given config or return the last path if it already had that config.
* If GCodePlanner::forceNewPathStart has been called a new path will always be returned.
*
* \param config The config used for the path returned
* \param space_fill_type The type of space filling which this path employs
* \param flow (optional) A ratio for the extrusion speed
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
* \return A path with the given config which is now the last path in GCodePlanner::paths
*/
GCodePath* getLatestPathWithConfig(const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
public:
/*!
* Force GCodePlanner::getLatestPathWithConfig to return a new path.
*
* This function is introduced because in some cases
* GCodePlanner::getLatestPathWithConfig is called consecutively with the same config pointer,
* though the content of the config has changed.
*
* Example cases:
* - when changing extruder, the same travel config is used, but its extruder field is changed.
*/
GCodePath* getLatestPathWithConfig(GCodePathConfig* config);
void forceNewPathStart();
/*!
*
* \param fan_speed_layer_time_settings_per_extruder The fan speed and layer time settings for each extruder.
* \param travel_avoid_other_parts Whether to avoid other layer parts when travaeling through air.
* \param travel_avoid_distance The distance by which to avoid other layer parts when traveling through air.
* \param last_position The position of the head at the start of this gcode layer
* \param combing_mode Whether combing is enabled and full or within infill only.
*/
GCodePlanner(SliceDataStorage& storage, int layer_nr, int z, int layer_height, Point last_position, int current_extruder, bool is_inside_mesh, std::vector<FanSpeedLayerTimeSettings>& fan_speed_layer_time_settings_per_extruder, CombingMode combing_mode, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance);
public:
GCodePlanner(GCodeExport& gcode, RetractionConfig* retraction_config, int travelSpeed, int retractionMinimalDistance);
~GCodePlanner();
void overrideFanSpeeds(double speed);
/*!
* Get the settings base of the last extruder planned.
* \return the settings base of the last extruder planned.
*/
SettingsBaseVirtual* getLastPlannedExtruderTrainSettings();
private:
/*!
* Compute the boundary within which to comb, or to move into when performing a retraction.
* \param combing_mode Whether combing is enabled and full or within infill only.
* \return the comb_boundary_inside
*/
Polygons computeCombBoundaryInside(CombingMode combing_mode);
public:
int getLayerNr()
bool setExtruder(int extruder)
{
return layer_nr;
}
Point getLastPosition()
{
return lastPosition;
if (extruder == currentExtruder)
return false;
currentExtruder = extruder;
return true;
}
/*!
* return whether the last position planned was inside the mesh (used in combing)
*/
bool getIsInsideMesh()
{
return was_inside;
}
/*!
* send a line segment through the command socket from the previous point to the given point \p to
*/
void sendLineTo(PrintFeatureType print_feature_type, Point to, int line_width)
{
CommandSocket::sendLineTo(print_feature_type, to, line_width);
}
/*!
* Set whether the next destination is inside a layer part or not.
*
* Features like infill, walls, skin etc. are considered inside.
* Features like prime tower and support are considered outside.
*/
void setIsInside(bool going_to_comb);
/*!
* Plan a switch to a new extruder
*
* \param extruder The extruder number to which to switch
* \return whether the extruder has changed
*/
bool setExtruder(int extruder);
/*!
* Get the last planned extruder.
*/
int getExtruder()
{
return extruder_plans.back().extruder;
return currentExtruder;
}
/*!
* Add a travel path to a certain point, retract if needed and when avoiding boundary crossings:
* avoiding obstacles and comb along the boundary of parts.
*
* \param p The point to travel to
*/
GCodePath& addTravel(Point p);
/*!
* Add a travel path to a certain point and retract if needed.
*
* No combing is performed.
*
* \param p The point to travel to
* \param path (optional) The travel path to which to add the point \p p
*/
GCodePath& addTravel_simple(Point p, GCodePath* path = nullptr);
/*!
* Plan a prime poop at the current location.
*
* \warning A nonretracted move is introduced so that the LayerPlanBuffer classifies this move as an extrusion move.
*/
void planPrime();
/*!
* Add an extrusion move to a certain point, optionally with a different flow than the one in the \p config.
*
* \param p The point to extrude to
* \param config The config with which to extrude
* \param space_fill_type Of what space filling type this extrusion move is a part
* \param flow A modifier of the extrusion width which would follow from the \p config
* \param spiralize Whether to gradually increase the z while printing. (Note that this path may be part of a sequence of spiralized paths, forming one polygon)
*/
void addExtrusionMove(Point p, const GCodePathConfig* config, SpaceFillType space_fill_type, float flow = 1.0, bool spiralize = false);
/*!
* Add polygon to the gcode starting at vertex \p startIdx
* \param polygon The polygon
* \param startIdx The index of the starting vertex of the \p polygon
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param wall_0_wipe_dist The distance to travel along the polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over this polygon
*/
void addPolygon(ConstPolygonRef polygon, int startIdx, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add polygons to the gcode with optimized order.
*
* When \p spiralize is true, each polygon will gradually increase from a z corresponding to this layer to the z corresponding to the next layer.
* Doing this for each polygon means there is a chance for the print head to crash into already printed parts,
* but doing it for the last polygon only would mean you are printing half of the layer in non-spiralize mode,
* while each layer starts with a different part.
* Two towers would result in alternating spiralize and non-spiralize layers.
*
* \param polygons The polygons
* \param config The config with which to print the polygon lines
* \param wall_overlap_computation The wall overlap compensation calculator for each given segment (optionally nullptr)
* \param z_seam_type The seam type / poly start optimizer
* \param z_seam_pos The location near where to start each part in case \p z_seam_type is 'back'
* \param wall_0_wipe_dist The distance to travel along each polygon after it has been laid down, in order to wipe the start and end of the wall together
* \param spiralize Whether to gradually increase the z height from the normal layer height to the height of the next layer over each polygon printed
*/
void addPolygonsByOptimizer(const Polygons& polygons, const GCodePathConfig* config, WallOverlapComputation* wall_overlap_computation = nullptr, EZSeamType z_seam_type = EZSeamType::SHORTEST, Point z_seam_pos = Point(0, 0), coord_t wall_0_wipe_dist = 0, bool spiralize = false);
/*!
* Add lines to the gcode with optimized order.
* \param polygons The lines
* \param config The config of the lines
* \param space_fill_type The type of space filling used to generate the line segments (should be either Lines or PolyLines!)
* \param wipe_dist (optional) the distance wiped without extruding after laying down a line.
*/
void addLinesByOptimizer(const Polygons& polygons, const GCodePathConfig* config, SpaceFillType space_fill_type, int wipe_dist = 0);
/*!
* Compute naive time estimates (without accounting for slow down at corners etc.) and naive material estimates (without accounting for MergeInfillLines)
* and store them in each ExtruderPlan and each GCodePath.
*
* \warning This function recomputes values which are already computed if you've called processFanSpeedAndMinimalLayerTime
*
* \return the total estimates of this layer
*/
TimeMaterialEstimates computeNaiveTimeEstimates();
/*!
* Write the planned paths to gcode
*
* \param gcode The gcode to write the planned paths to
*/
void writeGCode(GCodeExport& gcode);
/*!
* Has the planned paths been written to gcode
*/
int isGCodeWritten()
void setCombBoundary(Polygons* polygons)
{
int gcode_written_tmp;
#pragma omp atomic read
gcode_written_tmp = gcode_written;
return gcode_written_tmp;
if (comb)
delete comb;
if (polygons)
comb = new Comb(*polygons);
else
comb = nullptr;
}
/*!
* Complete all GcodePathConfigs by
* - altering speeds to conform to speed_print_layer_0 and
* speed_travel_layer_0
* - setting the layer_height (and thereby computing the extrusionMM3perMM)
*/
void completeConfigs();
/*!
* Interpolate between the initial layer speeds and the eventual speeds.
*/
void processInitialLayersSpeedup();
/*!
* Whether the current retracted path is to be an extruder switch retraction.
* This function is used to avoid a G10 S1 after a G10.
*
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index of the current retracted path
* \return Whether the path should be an extgruder switch retracted path
*/
bool makeRetractSwitchRetract(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx);
/*!
* Writes a path to GCode and performs coasting, or returns false if it did nothing.
*
* Coasting replaces the last piece of an extruded path by move commands and uses the oozed material to lay down lines.
*
* \param gcode The gcode to write the planned paths to
* \param extruder_plan_idx The index of the current extruder plan
* \param path_idx The index into GCodePlanner::paths for the next path to be written to GCode.
* \param layerThickness The height of the current layer.
* \param coasting_volume The volume otherwise leaked during a normal move.
* \param coasting_speed The speed at which to move during move-coasting.
* \param coasting_min_volume The minimal volume a path should have (before starting to coast) which builds up enough pressure to ooze as much as \p coasting_volume.
* \return Whether any GCode has been written for the path.
*/
bool writePathWithCoasting(GCodeExport& gcode, unsigned int extruder_plan_idx, unsigned int path_idx, int64_t layerThickness, double coasting_volume, double coasting_speed, double coasting_min_volume);
/*!
* Applying speed corrections for minimal layer times and determine the fanSpeed.
*/
void processFanSpeedAndMinimalLayerTime();
/*!
* Add a travel move to the layer plan to move inside the current layer part by a given distance away from the outline.
* This is supposed to be called when the nozzle is around the boundary of a layer part, not when the nozzle is in the middle of support, or in the middle of the air.
*
* \param distance The distance to the comb boundary after we moved inside it.
*/
void setAlwaysRetract(bool alwaysRetract)
{
this->alwaysRetract = alwaysRetract;
}
void forceRetract()
{
forceRetraction = true;
}
void setExtrudeSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->extrudeSpeedFactor = speedFactor;
}
int getExtrudeSpeedFactor()
{
return this->extrudeSpeedFactor;
}
void setTravelSpeedFactor(int speedFactor)
{
if (speedFactor < 1) speedFactor = 1;
this->travelSpeedFactor = speedFactor;
}
int getTravelSpeedFactor()
{
return this->travelSpeedFactor;
}
void addTravel(Point p);
void addExtrusionMove(Point p, GCodePathConfig* config);
void moveInsideCombBoundary(int distance);
void addPolygon(PolygonRef polygon, int startIdx, GCodePathConfig* config);
void addPolygonsByOptimizer(Polygons& polygons, GCodePathConfig* config);
void addLinesByOptimizer(Polygons& polygons, GCodePathConfig* config);
void forceMinimalLayerTime(double minTime, int minimalSpeed, double travelTime, double extrusionTime);
void getTimes(double& travelTime, double& extrudeTime);
void writeGCode(bool liftHeadIfNeeded, int layerThickness);
};
}//namespace cura
+396 -293
Ver Arquivo
@@ -2,233 +2,185 @@
#include "infill.h"
#include "functional"
#include "utils/polygonUtils.h"
#include "utils/logoutput.h"
namespace cura {
int Infill::computeScanSegmentIdx(int x, int line_width)
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
{
if (x < 0)
while(outline.size() > 0)
{
return (x + 1) / line_width - 1;
// - 1 because -1 belongs to scansegment -1
// + 1 because -line_width belongs to scansegment -1
}
return x / line_width;
}
void Infill::generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh)
{
if (in_outline.size() == 0) return;
if (line_distance == 0) return;
Polygons outline_offsetted;
switch(pattern)
{
case EFillMethod::GRID:
generateGridInfill(result_lines);
break;
case EFillMethod::LINES:
generateLineInfill(result_lines, line_distance, fill_angle, 0);
break;
case EFillMethod::CUBIC:
generateCubicInfill(result_lines);
break;
case EFillMethod::TETRAHEDRAL:
generateTetrahedralInfill(result_lines);
break;
case EFillMethod::TRIANGLES:
generateTriangleInfill(result_lines);
break;
case EFillMethod::CONCENTRIC:
generateConcentricInfill(result_polygons, line_distance);
break;
case EFillMethod::CONCENTRIC_3D:
generateConcentric3DInfill(result_polygons);
break;
case EFillMethod::ZIG_ZAG:
generateZigZagInfill(result_lines, line_distance, fill_angle, connected_zigzags, use_endpieces);
break;
case EFillMethod::CUBICSUBDIV:
if (!mesh)
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
logError("Cannot generate Cubic Subdivision infill without a mesh!\n");
break;
PolygonRef r = outline[polyNr];
result.add(r);
}
generateCubicSubDivInfill(result_lines, *mesh);
break;
default:
logError("Fill pattern has unknown value.\n");
break;
}
Polygons next_outline;
offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
outline = next_outline;
}
}
void Infill::generateConcentricInfill(Polygons& result, int inset_value)
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value)
{
Polygons first_concentric_wall = in_outline.offset(outline_offset - line_distance + infill_line_width / 2); // - infill_line_width / 2 cause generateConcentricInfill expects [outline] to be the outer most polygon instead of the outer outline
if (perimeter_gaps)
while(outline.size() > 0)
{
const Polygons inner = first_concentric_wall.offset(infill_line_width / 2 + perimeter_gaps_extra_offset);
const Polygons gaps_here = in_outline.difference(inner);
perimeter_gaps->add(gaps_here);
}
generateConcentricInfill(first_concentric_wall, result, inset_value);
}
void Infill::generateConcentricInfill(Polygons& first_concentric_wall, Polygons& result, int inset_value)
{
result.add(first_concentric_wall);
Polygons* prev_inset = &first_concentric_wall;
Polygons next_inset;
while (prev_inset->size() > 0)
{
next_inset = prev_inset->offset(-inset_value);
result.add(next_inset);
if (perimeter_gaps)
for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
{
const Polygons outer = prev_inset->offset(-infill_line_width / 2 - perimeter_gaps_extra_offset);
const Polygons inner = next_inset.offset(infill_line_width / 2);
const Polygons gaps_here = outer.difference(inner);
perimeter_gaps->add(gaps_here);
PolygonRef r = outline[polyNr];
result.add(r);
}
prev_inset = &next_inset;
}
outline = outline.offset(-inset_value);
}
}
void Infill::generateConcentric3DInfill(Polygons& result)
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, int infillOverlap,
double rotation)
{
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
Polygons first_wall;
// in contrast to concentric infill we dont do "- infill_line_width / 2" cause this is already handled by the max two lines above
first_wall = in_outline.offset(outline_offset - shift);
generateConcentricInfill(first_wall, result, period);
first_wall = in_outline.offset(outline_offset - period + shift);
generateConcentricInfill(first_wall, result, period);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 90);
}
void Infill::generateGridInfill(Polygons& result)
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result,
int extrusionWidth, int lineSpacing, int infillOverlap,
double rotation)
{
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 90, 0);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 60);
generateLineInfill(in_outline, outlineOffset, result, extrusionWidth, lineSpacing,
infillOverlap, rotation + 120);
}
void Infill::generateCubicInfill(Polygons& result)
{
int64_t shift = one_over_sqrt_2 * z;
generateLineInfill(result, line_distance, fill_angle, shift);
generateLineInfill(result, line_distance, fill_angle + 120, shift);
generateLineInfill(result, line_distance, fill_angle + 240, shift);
}
void Infill::generateTetrahedralInfill(Polygons& result)
{
int period = line_distance * 2;
int shift = int64_t(one_over_sqrt_2 * z) % period;
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
shift = std::min(shift, period / 2 - infill_line_width / 2); // don't put lines too close to each other
shift = std::max(shift, infill_line_width / 2); // don't put lines too close to each other
generateLineInfill(result, period, fill_angle, shift);
generateLineInfill(result, period, fill_angle, -shift);
generateLineInfill(result, period, fill_angle + 90, shift);
generateLineInfill(result, period, fill_angle + 90, -shift);
}
void Infill::generateTriangleInfill(Polygons& result)
{
generateLineInfill(result, line_distance, fill_angle, 0);
generateLineInfill(result, line_distance, fill_angle + 60, 0);
generateLineInfill(result, line_distance, fill_angle + 120, 0);
}
void Infill::generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh)
{
Polygons uncropped;
mesh.base_subdiv_cube->generateSubdivisionLines(z, uncropped);
addLineSegmentsInfill(result, uncropped);
}
void Infill::addLineSegmentsInfill(Polygons& result, Polygons& input)
{
ClipperLib::PolyTree interior_segments_tree = in_outline.lineSegmentIntersection(input);
ClipperLib::Paths interior_segments;
ClipperLib::OpenPathsFromPolyTree(interior_segments_tree, interior_segments);
for (uint64_t idx = 0; idx < interior_segments.size(); idx++)
{
result.addLine(interior_segments[idx][0], interior_segments[idx][1]);
}
}
void Infill::addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t shift)
void addLineInfill(Polygons& result, PointMatrix matrix, int scanline_min_idx, int lineSpacing, AABB boundary, std::vector<std::vector<int64_t> > cutList, int extrusionWidth)
{
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
auto compare_int64_t = [](const void* a, const void* b)
{
int64_t n = (*(int64_t*)a) - (*(int64_t*)b);
if (n < 0)
{
return -1;
}
if (n > 0)
{
return 1;
}
if (n < 0) return -1;
if (n > 0) return 1;
return 0;
};
int scanline_idx = 0;
for(int64_t x = scanline_min_idx * line_distance + shift; x < boundary.max.X; x += line_distance)
for(int64_t x = scanline_min_idx * lineSpacing; x < boundary.max.X; x += lineSpacing)
{
std::vector<int64_t>& crossings = cut_list[scanline_idx];
qsort(crossings.data(), crossings.size(), sizeof(int64_t), compare_int64_t);
for(unsigned int crossing_idx = 0; crossing_idx + 1 < crossings.size(); crossing_idx += 2)
qsort(cutList[scanline_idx].data(), cutList[scanline_idx].size(), sizeof(int64_t), compare_int64_t);
for(unsigned int i = 0; i + 1 < cutList[scanline_idx].size(); i+=2)
{
if (crossings[crossing_idx + 1] - crossings[crossing_idx] < infill_line_width / 5)
{ // segment is too short to create infill
if (cutList[scanline_idx][i+1] - cutList[scanline_idx][i] < extrusionWidth / 5)
continue;
}
result.addLine(rotation_matrix.unapply(Point(x, crossings[crossing_idx])), rotation_matrix.unapply(Point(x, crossings[crossing_idx + 1])));
addLine(Point(x, cutList[scanline_idx][i]), Point(x, cutList[scanline_idx][i+1]));
}
scanline_idx += 1;
}
}
void Infill::generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t shift)
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p lineSpacing
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
*
* algorithm:
* 1) for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2) for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
*/
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
PointMatrix rotation_matrix(fill_angle);
NoZigZagConnectorProcessor lines_processor(rotation_matrix, result);
bool connected_zigzags = false;
generateLinearBasedInfill(outline_offset, result, line_distance, rotation_matrix, lines_processor, connected_zigzags, shift);
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 + outlineOffset);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int poly_idx=0; poly_idx < outline.size(); poly_idx++)
{
Point p0 = outline[poly_idx][outline[poly_idx].size()-1];
for(unsigned int i=0; i < outline[poly_idx].size(); i++)
{
Point p1 = outline[poly_idx][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
}
p0 = p1;
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
void Infill::generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces)
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces)
{
PointMatrix rotation_matrix(fill_angle);
if (use_endpieces)
{
if (connected_zigzags)
{
ZigzagConnectorProcessorConnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
else
{
ZigzagConnectorProcessorDisconnectedEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
}
else
{
ZigzagConnectorProcessorNoEndPieces zigzag_processor(rotation_matrix, result);
generateLinearBasedInfill(outline_offset - infill_line_width / 2, result, line_distance, rotation_matrix, zigzag_processor, connected_zigzags, 0);
}
if (use_endPieces) return generateZigZagInfill_endPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation, connect_zigzags);
else return generateZigZagInfill_noEndPieces(in_outline, result, extrusionWidth, lineSpacing, infillOverlap, rotation);
}
/*
/*!
* adapted from generateLineInfill(.)
*
* generate lines within the area of [in_outline], at regular intervals of [lineSpacing]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline]
* sigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
@@ -237,137 +189,288 @@ void Infill::generateZigZagInfill(Polygons& result, const int line_distance, con
* sort the associated intersections
* and connect them using the even-odd rule
*
* rough explanation of the zigzag algorithm:
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* (see infill/ZigzagConnectorProcessor.h for actual implementation details)
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connect_zigzags])
*
*
* we call the areas between two consecutive scanlines a 'scansegment'.
* Scansegment x is the area between scanline x and scanline x+1
* Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment,
* while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment.
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
*
* start boundary from even scanline! :D
*
*
* _____
* | | | ,
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
*/
void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift)
void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags)
{
if (line_distance == 0)
{
return;
}
if (in_outline.size() == 0)
{
return;
}
int shift = extra_shift + this->shift;
Polygons outline;
if (outline_offset != 0)
{
outline = in_outline.offset(outline_offset);
if (perimeter_gaps)
{
perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset)));
}
}
else
{
outline = in_outline;
}
outline = outline.offset(infill_overlap);
if (outline.size() == 0)
{
return;
}
outline.applyMatrix(rotation_matrix);
if (shift < 0)
{
shift = line_distance - (-shift) % line_distance;
}
else
{
shift = shift % line_distance;
}
// if (in_outline.size() == 0) return;
// Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
Polygons empty;
Polygons outline = in_outline.difference(empty); // copy
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance);
int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx;
std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments
for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++)
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
cut_list.push_back(std::vector<int64_t>());
}
for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++)
{
PolygonRef poly = outline[poly_idx];
Point p0 = poly.back();
zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type
for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
std::vector<Point> firstBoundarySegment;
std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
Point lastPoint = p0;
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = poly[point_idx];
if (p1.X == p0.X)
{
zigzag_connector_processor.registerVertex(p1);
// TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors)
// note: this is already a problem for normal infill, but hasn't really cothered anyone so far.
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
lastPoint = p1;
p0 = p1;
continue;
}
int scanline_idx0;
int scanline_idx1;
// this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline:
// in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created
// otherwise only 1 will be created, counting as an actual intersection
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X < p1.X)
if (p0.X > p1.X)
{
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >)
}
else
{
direction = -1;
scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >)
scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment
}
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction)
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * line_distance + shift;
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!");
cut_list[scanline_idx - scanline_min_idx].push_back(y);
Point scanline_linesegment_intersection(x, y);
zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment))
addLine(lastPoint, Point(x,y));
else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment
{ // add whole unevenBoundarySegment (including the just obtained point)
for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++)
{
addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]);
}
addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y));
unevenBoundarySegment.clear();
}
if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment)
unevenBoundarySegment.push_back(Point(x,y));
else
unevenBoundarySegment.clear();
}
lastPoint = Point(x,y);
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
}
}
zigzag_connector_processor.registerVertex(p1);
if (!isFirstBoundarySegment)
{
if (isEvenScanSegment)
addLine(lastPoint, p1);
else if (connect_zigzags)
unevenBoundarySegment.push_back(p1);
}
lastPoint = p1;
p0 = p1;
}
zigzag_connector_processor.registerPolyFinished();
}
if (cut_list.size() == 0)
{
return;
}
if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2)
{
return; // don't add connection if boundary already contains whole outline!
}
addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift);
if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
{
if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
else if (!firstBoundarySegmentEndsInEven)
addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]);
}
if (cutList.size() == 0) return;
if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline!
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation)
{
if (in_outline.size() == 0) return;
Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2);
if (outline.size() == 0) return;
PointMatrix matrix(rotation);
outline.applyMatrix(matrix);
auto addLine = [&](Point from, Point to)
{
PolygonRef p = result.newPoly();
p.add(matrix.unapply(from));
p.add(matrix.unapply(to));
};
AABB boundary(outline);
int scanline_min_idx = boundary.min.X / lineSpacing;
int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx;
std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments
for(int n=0; n<lineCount; n++)
cutList.push_back(std::vector<int64_t>());
for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++)
{
std::vector<Point> firstBoundarySegment;
std::vector<Point> boundarySegment;
bool isFirstBoundarySegment = true;
bool firstBoundarySegmentEndsInEven;
bool isEvenScanSegment = false;
Point p0 = outline[polyNr][outline[polyNr].size()-1];
for(unsigned int i=0; i < outline[polyNr].size(); i++)
{
Point p1 = outline[polyNr][i];
int64_t xMin = p1.X, xMax = p0.X;
if (xMin == xMax) {
p0 = p1;
continue;
}
if (xMin > xMax) { xMin = p0.X; xMax = p1.X; }
int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ...
int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number)
int direction = 1;
if (p0.X > p1.X)
{
direction = -1;
scanline_idx1 += 1; // only consider the scanlines in between the scansegments
} else scanline_idx0 += 1; // only consider the scanlines in between the scansegments
if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0);
else boundarySegment.push_back(p0);
for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction)
{
int x = scanline_idx * lineSpacing;
int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X);
cutList[scanline_idx - scanline_min_idx].push_back(y);
bool last_isEvenScanSegment = isEvenScanSegment;
if (scanline_idx % 2 == 0) isEvenScanSegment = true;
else isEvenScanSegment = false;
if (!isFirstBoundarySegment)
{
if (last_isEvenScanSegment && !isEvenScanSegment)
{ // add whole boundarySegment (including the just obtained point)
for (unsigned int p = 1; p < boundarySegment.size(); p++)
{
addLine(boundarySegment[p-1], boundarySegment[p]);
}
addLine(boundarySegment[boundarySegment.size()-1], Point(x,y));
boundarySegment.clear();
}
else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment
{
boundarySegment.clear();
boundarySegment.emplace_back(x,y);
} else
boundarySegment.clear();
}
if (isFirstBoundarySegment)
{
firstBoundarySegment.emplace_back(x,y);
firstBoundarySegmentEndsInEven = isEvenScanSegment;
isFirstBoundarySegment = false;
boundarySegment.emplace_back(x,y);
}
}
if (!isFirstBoundarySegment && isEvenScanSegment)
boundarySegment.push_back(p1);
p0 = p1;
}
if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven)
{
for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++)
addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]);
}
}
addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth);
}
}//namespace cura
+9 -247
Ver Arquivo
@@ -3,255 +3,17 @@
#define INFILL_H
#include "utils/polygon.h"
#include "settings/settings.h"
// #include "ZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessor.h"
#include "infill/NoZigZagConnectorProcessor.h"
#include "infill/ActualZigzagConnectorProcessor.h"
#include "infill/ZigzagConnectorProcessorNoEndPieces.h"
#include "infill/ZigzagConnectorProcessorEndPieces.h"
#include "infill/ZigzagConnectorProcessorConnectedEndPieces.h"
#include "infill/ZigzagConnectorProcessorDisconnectedEndPieces.h"
#include "infill/SubDivCube.h"
#include "utils/intpoint.h"
#include "utils/AABB.h"
namespace cura
{
class Infill
{
static constexpr int perimeter_gaps_extra_offset = 15; // extra offset so that the perimeter gaps aren't created everywhere due to rounding errors
EFillMethod pattern; //!< the space filling pattern of the infill to generate
const Polygons& in_outline; //!< a reference polygon for getting the actual area within which to generate infill (see outline_offset)
int outline_offset; //!< Offset from Infill::in_outline to get the actual area within which to generate infill
int infill_line_width; //!< The line width of the infill lines to generate
int line_distance; //!< The distance between two infill lines / polygons
int infill_overlap; //!< the distance by which to overlap with the actual area within which to generate infill
double fill_angle; //!< for linear infill types: the angle of the infill lines (or the angle of the grid)
int64_t z; //!< height of the layer for which we generate infill
int64_t shift; //!< shift of the scanlines in the direction perpendicular to the fill_angle
Polygons* perimeter_gaps; //!< (optional output) The areas in between consecutive insets when Concentric infill is used.
bool connected_zigzags; //!< (ZigZag) Whether endpieces of zigzag infill should be connected to the nearest infill line on both sides of the zigzag connector
bool use_endpieces; //!< (ZigZag) Whether to include endpieces: zigzag connector segments from one infill line to itself
static constexpr double one_over_sqrt_2 = 0.7071067811865475244008443621048490392848359376884740; //!< 1.0 / sqrt(2.0)
public:
/*!
* \warning If \p perimeter_gaps is given, then the difference between the \p in_outline
* and the polygons which result from offsetting it by the \p outline_offset
* and then expanding it again by half the \p infill_line_width
* is added to the \p perimeter_gaps
*
* \param[out] perimeter_gaps (optional output) The areas in between consecutive insets when Concentric infill is used.
*/
Infill(EFillMethod pattern
, const Polygons& in_outline
, int outline_offset
, int infill_line_width
, int line_distance
, int infill_overlap
, double fill_angle
, int64_t z
, int64_t shift
, Polygons* perimeter_gaps = nullptr
, bool connected_zigzags = false
, bool use_endpieces = false
)
: pattern(pattern)
, in_outline(in_outline)
, outline_offset(outline_offset)
, infill_line_width(infill_line_width)
, line_distance(line_distance)
, infill_overlap(infill_overlap)
, fill_angle(fill_angle)
, z(z)
, shift(shift)
, perimeter_gaps(perimeter_gaps)
, connected_zigzags(connected_zigzags)
, use_endpieces(use_endpieces)
{
}
/*!
* Generate the infill.
*
* \param result_polygons (output) The resulting polygons (from concentric infill)
* \param result_lines (output) The resulting line segments (from linear infill types)
* \param mesh The mesh for which to geenrate infill (should only be used for non-helper objects)
*/
void generate(Polygons& result_polygons, Polygons& result_lines, const SliceMeshStorage* mesh = nullptr);
private:
/*!
* Function which returns the scanline_idx for a given x coordinate
*
* For negative \p x this is different from simple division.
*
* \warning \p line_distance is assumed to be positive
*
* \param x the point to get the scansegment index for
* \param line_distance the width of the scan segments
*/
static inline int computeScanSegmentIdx(int x, int line_distance);
/*!
* Generate sparse concentric infill
*
* Also adds \ref Inifll::perimeter_gaps between \ref Infill::in_outline and the first wall
*
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill starting from a specific outer wall
* \param first_wall The outer wall from which to start
* \param result (output) The resulting polygons
* \param inset_value The offset between each consecutive two polygons
*/
void generateConcentricInfill(Polygons& first_wall, Polygons& result, int inset_value);
/*!
* Generate sparse concentric infill
* \param result (output) The resulting polygons
*/
void generateConcentric3DInfill(Polygons& result);
/*!
* Generate a rectangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateGridInfill(Polygons& result);
/*!
* Generate a shifting triangular grid of infill lines, which combine with consecutive layers into a cubic pattern
* \param result (output) The resulting lines
*/
void generateCubicInfill(Polygons& result);
/*!
* Generate a double shifting square grid of infill lines, which combine with consecutive layers into a tetrahedral pattern
* \param result (output) The resulting lines
*/
void generateTetrahedralInfill(Polygons& result);
/*!
* Generate a triangular grid of infill lines
* \param result (output) The resulting lines
*/
void generateTriangleInfill(Polygons& result);
/*!
* Generate a 3d pattern of subdivided cubes on their points
* \param[out] result The resulting lines
* \param[in] mesh Where the Cubic Subdivision Infill precomputation is stored
*/
void generateCubicSubDivInfill(Polygons& result, const SliceMeshStorage& mesh);
/*!
* Convert a mapping from scanline to line_segment-scanline-intersections (\p cut_list) into line segments, using the even-odd rule
* \param result (output) The resulting lines
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param scanline_min_idx The lowest index of all scanlines crossing the polygon
* \param line_distance The distance between two lines which are in the same direction
* \param boundary The axis aligned boundary box within which the polygon is
* \param cut_list A mapping of each scanline to all y-coordinates (in the space transformed by rotation_matrix) where the polygons are crossing the scanline
* \param total_shift total shift of the scanlines in the direction perpendicular to the fill_angle.
*/
void addLineInfill(Polygons& result, const PointMatrix& rotation_matrix, const int scanline_min_idx, const int line_distance, const AABB boundary, std::vector<std::vector<int64_t>>& cut_list, int64_t total_shift);
/*!
* Crop line segments by the infill polygon using Clipper
* \param result (output) The resulting lines
* \param input The line segments to be cropped
*/
void addLineSegmentsInfill(Polygons& result, Polygons& input);
/*!
* generate lines within the area of \p in_outline, at regular intervals of \p line_distance
*
* idea:
* intersect a regular grid of 'scanlines' with the area inside \p in_outline
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLineInfill(Polygons& result, int line_distance, const double& fill_angle, int64_t extra_shift);
/*!
* Function for creating linear based infill types (Lines, ZigZag).
*
* This function implements the basic functionality of Infill::generateLineInfill (see doc of that function),
* but makes calls to a ZigzagConnectorProcessor which handles what to do with each line segment - scanline intersection.
*
* It is called only from Infill::generateLineinfill and Infill::generateZigZagInfill.
*
* \param outline_offset An offset from the reference polygon (Infill::in_outline) to get the actual outline within which to generate infill
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param rotation_matrix The rotation matrix (un)applied to enforce the angle of the infill
* \param zigzag_connector_processor The processor used to generate zigzag connectors
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param extra_shift extra shift of the scanlines in the direction perpendicular to the fill_angle
*/
void generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift);
/*!
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* <--
* ___
* | | |
* | | |
* | |___|
* -->
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
*
*
* v disconnected end piece: leave out last line segment
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
*
*
* v connected end piece
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
*
* \param result (output) The resulting lines
* \param line_distance The distance between two lines which are in the same direction
* \param fill_angle The angle of the generated lines
* \param connected_zigzags Whether to connect the endpiece zigzag segments on both sides to the same infill line
* \param use_endpieces Whether to include zigzag segments connecting a scanline to itself
*/
void generateZigZagInfill(Polygons& result, const int line_distance, const double& fill_angle, const bool connected_zigzags, const bool use_endpieces);
};
namespace cura {
void generateConcentricInfill(Polygons outline, Polygons& result, int inset_value);
void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters);
void generateGridInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateTriangleInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateLineInfill(const Polygons& in_outline, int outlineOffset, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
void generateZigZagInfill(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags, bool use_endPieces);
void generateZigZagInfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation, bool connect_zigzags);
void generateZigZagInfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, int infillOverlap, double rotation);
}//namespace cura
#endif//INFILL_H
-47
Ver Arquivo
@@ -1,47 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
/*!
* In contrast to NoZigZagConnectorProcessor
*/
class ActualZigzagConnectorProcessor : public ZigzagConnectorProcessor
{
protected:
/*!
* The line segments belonging the zigzag connector to which the very first vertex belongs.
* This will be combined with the last handled zigzag_connector, which combine to a whole zigzag connector.
*
* Because the boundary polygon may start in in the middle of a zigzag connector,
*/
std::vector<Point> first_zigzag_connector;
/*!
* The currently built up zigzag connector (not the first/last) or end piece or discarded boundary segment
*/
std::vector<Point> zigzag_connector;
bool is_first_zigzag_connector; //!< Whether we're still in the first zigzag connector
bool first_zigzag_connector_ends_in_even_scanline; //!< Whether the first zigzag connector ends in an even scanline
bool last_scanline_is_even; //!< Whether the last seen scanline-boundary intersection was with an even scanline
ActualZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
, is_first_zigzag_connector(true)
, first_zigzag_connector_ends_in_even_scanline(true)
, last_scanline_is_even(false)
{
}
};
} // namespace cura
#endif // INFILL_ACTUAL_ZIGZAG_CONNECTOR_PROCESSOR_H
-25
Ver Arquivo
@@ -1,25 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "NoZigZagConnectorProcessor.h"
namespace cura
{
void NoZigZagConnectorProcessor::registerVertex(const Point& vertex)
{
}
void NoZigZagConnectorProcessor::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
}
void NoZigZagConnectorProcessor::registerPolyFinished()
{
}
} // namespace cura
-28
Ver Arquivo
@@ -1,28 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessor.h"
namespace cura
{
class NoZigZagConnectorProcessor : public ZigzagConnectorProcessor
{
public:
NoZigZagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_NO_ZIGZAG_CONNECTOR_PROCESSOR_H
-282
Ver Arquivo
@@ -1,282 +0,0 @@
#include "SubDivCube.h"
#include <functional>
#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/math.h"
#define ONE_OVER_SQRT_2 0.7071067811865475244008443621048490392848359376884740 //1 / sqrt(2)
#define ONE_OVER_SQRT_3 0.577350269189625764509148780501957455647601751270126876018 //1 / sqrt(3)
#define ONE_OVER_SQRT_6 0.408248290463863016366214012450981898660991246776111688072 //1 / sqrt(6)
#define SQRT_TWO_THIRD 0.816496580927726032732428024901963797321982493552223376144 //sqrt(2 / 3)
namespace cura
{
std::vector<SubDivCube::CubeProperties> SubDivCube::cube_properties_per_recursion_step;
double SubDivCube::radius_multiplier = 1;
int32_t SubDivCube::radius_addition = 0;
Point3Matrix SubDivCube::rotation_matrix;
PointMatrix SubDivCube::infill_rotation_matrix;
SubDivCube::~SubDivCube()
{
for (int child_idx = 0; child_idx < 8; child_idx++)
{
if (children[child_idx])
{
delete children[child_idx];
}
}
}
void SubDivCube::precomputeOctree(SliceMeshStorage& mesh)
{
radius_multiplier = mesh.getSettingAsRatio("sub_div_rad_mult");
radius_addition = mesh.getSettingInMicrons("sub_div_rad_add");
double infill_angle = M_PI / 4.0;
coord_t furthest_dist_from_origin = std::sqrt(square(mesh.getSettingInMicrons("machine_height")) + square(mesh.getSettingInMicrons("machine_depth") / 2) + square(mesh.getSettingInMicrons("machine_width") / 2));
coord_t max_side_length = furthest_dist_from_origin * 2;
int curr_recursion_depth = 0;
const int64_t infill_line_distance = mesh.getSettingInMicrons("infill_line_distance");
if (infill_line_distance > 0)
{
for (int64_t curr_side_length = infill_line_distance * 2; curr_side_length < max_side_length * 2; curr_side_length *= 2)
{
cube_properties_per_recursion_step.emplace_back();
CubeProperties& cube_properties_here = cube_properties_per_recursion_step.back();
cube_properties_here.side_length = curr_side_length;
cube_properties_here.height = sqrt(3) * curr_side_length;
cube_properties_here.square_height = sqrt(2) * curr_side_length;
cube_properties_here.max_draw_z_diff = ONE_OVER_SQRT_3 * curr_side_length;
cube_properties_here.max_line_offset = ONE_OVER_SQRT_6 * curr_side_length;
curr_recursion_depth++;
}
}
Point3 center(0, 0, 0);
Point3Matrix tilt; // rotation matrix to get from axis aligned cubes to cubes standing on their tip
// The Z axis is transformed to go in positive Y direction
//
// cross section in a horizontal plane horizontal plane showing
// looking down at the origin O positive X and positive Y
// Z .
// /:\ Y .
// / : \ ^ .
// / : \ | .
// / .O. \ | .
// /.~' '~.\ O---->X .
// X """"""""""" Y .
tilt.matrix[0] = -ONE_OVER_SQRT_2; tilt.matrix[1] = ONE_OVER_SQRT_2; tilt.matrix[2] = 0;
tilt.matrix[3] = -ONE_OVER_SQRT_6; tilt.matrix[4] = -ONE_OVER_SQRT_6; tilt.matrix[5] = SQRT_TWO_THIRD ;
tilt.matrix[6] = ONE_OVER_SQRT_3; tilt.matrix[7] = ONE_OVER_SQRT_3; tilt.matrix[8] = ONE_OVER_SQRT_3;
infill_rotation_matrix = PointMatrix(infill_angle);
Point3Matrix infill_angle_mat(infill_rotation_matrix);
rotation_matrix = infill_angle_mat.compose(tilt);
mesh.base_subdiv_cube = new SubDivCube(mesh, center, curr_recursion_depth - 1);
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result)
{
if (cube_properties_per_recursion_step.empty()) //Infill is set to 0%.
{
return;
}
Polygons directional_line_groups[3];
generateSubdivisionLines(z, result, directional_line_groups);
for (int dir_idx = 0; dir_idx < 3; dir_idx++)
{
Polygons& line_group = directional_line_groups[dir_idx];
for (unsigned int line_idx = 0; line_idx < line_group.size(); line_idx++)
{
result.addLine(line_group[line_idx][0], line_group[line_idx][1]);
}
}
}
void SubDivCube::generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3])
{
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
int32_t z_diff = std::abs(z - center.z); //!< the difference between the cube center and the target layer.
if (z_diff > cube_properties.height / 2) //!< this cube does not touch the target layer. Early exit.
{
return;
}
if (z_diff < cube_properties.max_draw_z_diff) //!< this cube has lines that need to be drawn.
{
Point relative_a, relative_b; //!< relative coordinates of line endpoints around cube center
Point a, b; //!< absolute coordinates of line endpoints
relative_a.X = (cube_properties.square_height / 2) * (cube_properties.max_draw_z_diff - z_diff) / cube_properties.max_draw_z_diff;
relative_b.X = -relative_a.X;
relative_a.Y = cube_properties.max_line_offset - ((z - (center.z - cube_properties.max_draw_z_diff)) * ONE_OVER_SQRT_2);
relative_b.Y = relative_a.Y;
rotatePointInitial(relative_a);
rotatePointInitial(relative_b);
for (int dir_idx = 0; dir_idx < 3; dir_idx++)//!< draw the line, then rotate 120 degrees.
{
a.X = center.x + relative_a.X;
a.Y = center.y + relative_a.Y;
b.X = center.x + relative_b.X;
b.Y = center.y + relative_b.Y;
addLineAndCombine(directional_line_groups[dir_idx], a, b);
if (dir_idx < 2)
{
rotatePoint120(relative_a);
rotatePoint120(relative_b);
}
}
}
for (int idx = 0; idx < 8; idx++) //!< draws the eight children
{
if (children[idx] != nullptr)
{
children[idx]->generateSubdivisionLines(z, result, directional_line_groups);
}
}
}
SubDivCube::SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth)
{
this->depth = depth;
this->center = center;
if (depth == 0) // lowest layer, no need for subdivision, exit.
{
return;
}
if (depth >= cube_properties_per_recursion_step.size()) //Depth is out of bounds of what we pre-computed.
{
return;
}
CubeProperties cube_properties = cube_properties_per_recursion_step[depth];
Point3 child_center;
coord_t radius = double(radius_multiplier * double(cube_properties.height)) / 4.0 + radius_addition;
int child_nr = 0;
std::vector<Point3> rel_child_centers;
rel_child_centers.emplace_back(1, 1, 1); // top
rel_child_centers.emplace_back(-1, 1, 1); // top three
rel_child_centers.emplace_back(1, -1, 1);
rel_child_centers.emplace_back(1, 1, -1);
rel_child_centers.emplace_back(-1, -1, -1); // bottom
rel_child_centers.emplace_back(1, -1, -1); // bottom three
rel_child_centers.emplace_back(-1, 1, -1);
rel_child_centers.emplace_back(-1, -1, 1);
for (Point3 rel_child_center : rel_child_centers)
{
child_center = center + rotation_matrix.apply(rel_child_center * int32_t(cube_properties.side_length / 4));
if (isValidSubdivision(mesh, child_center, radius))
{
children[child_nr] = new SubDivCube(mesh, child_center, depth - 1);
child_nr++;
}
}
}
bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius)
{
int64_t distance2;
coord_t sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer
bool inside_somewhere = false;
bool outside_somewhere = false;
int inside;
double part_dist;//what percentage of the radius the target layer is away from the center along the z axis. 0 - 1
const coord_t layer_height = mesh.getSettingInMicrons("layer_height");
int bottom_layer = (center.z - radius) / layer_height;
int top_layer = (center.z + radius) / layer_height;
for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain.
{
part_dist = (double)(test_layer * layer_height - center.z) / radius;
sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist));
Point loc(center.x, center.y);
inside = distanceFromPointToMesh(mesh, test_layer, loc, &distance2);
if (inside == 1)
{
inside_somewhere = true;
}
else
{
outside_somewhere = true;
}
if (outside_somewhere && inside_somewhere)
{
return true;
}
if ((inside != 2) && distance2 < sphere_slice_radius2)
{
return true;
}
}
return false;
}
int SubDivCube::distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2)
{
if (layer_nr < 0 || (unsigned int)layer_nr >= mesh.layers.size()) //!< this layer is outside of valid range
{
return 2;
}
Polygons collide;
mesh.layers[layer_nr].getSecondOrInnermostWalls(collide);
Point centerpoint = location;
bool inside = collide.inside(centerpoint);
ClosestPolygonPoint border_point = PolygonUtils::moveInside2(collide, centerpoint);
Point diff = border_point.location - location;
*distance2 = vSize2(diff);
if (inside)
{
return 1;
}
return 0;
}
void SubDivCube::rotatePointInitial(Point& target)
{
target = infill_rotation_matrix.apply(target);
}
void SubDivCube::rotatePoint120(Point& target)
{
constexpr double sqrt_three_fourths = 0.8660254037844386467637231707529361834714026269051903; //!< sqrt(3.0 / 4.0) = sqrt(3) / 2
int64_t x;
x = (-0.5) * target.X - sqrt_three_fourths * target.Y;
target.Y = (-0.5)*target.Y + sqrt_three_fourths * target.X;
target.X = x;
}
void SubDivCube::addLineAndCombine(Polygons& group, Point from, Point to)
{
int epsilon = 10; // the smallest distance of two points which are viewed as coincident (dist > 0 due to rounding errors)
for (unsigned int idx = 0; idx < group.size(); idx++)
{
if (std::abs(from.X - group[idx][1].X) < epsilon && std::abs(from.Y - group[idx][1].Y) < epsilon)
{
from = group[idx][0];
group.remove(idx);
idx--;
continue;
}
if (std::abs(to.X - group[idx][0].X) < epsilon && std::abs(to.Y - group[idx][0].Y) < epsilon)
{
to = group[idx][1];
group.remove(idx);
idx--;
continue;
}
}
group.addLine(from, to);
}
}//namespace cura
-98
Ver Arquivo
@@ -1,98 +0,0 @@
#ifndef INFILL_SUBDIVCUBE_H
#define INFILL_SUBDIVCUBE_H
#include "../sliceDataStorage.h"
namespace cura
{
class Infill;
class SubDivCube
{
public:
/*!
* Constructor for SubDivCube. Recursively calls itself eight times to flesh out the octree.
* \param mesh contains infill layer data and settings
* \param my_center the center of the cube
* \param depth the recursion depth of the cube (0 is most recursed)
*/
SubDivCube(SliceMeshStorage& mesh, Point3& center, unsigned int depth);
~SubDivCube(); //!< destructor (also destroys children
/*!
* Precompute the octree of subdivided cubes
* \param mesh contains infill layer data and settings
*/
static void precomputeOctree(SliceMeshStorage& mesh);
/*!
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
*/
void generateSubdivisionLines(int64_t z, Polygons& result);
private:
/*!
* Generates the lines of subdivision of the specific cube at the specific layer. It recursively calls itself, so it ends up drawing all the subdivision lines of sub-cubes too.
* \param z the specified layer height
* \param result (output) The resulting lines
* \param directional_line_groups Array of 3 times a polylines. Used to keep track of line segments that are all pointing the same direction for line segment combining
*/
void generateSubdivisionLines(int64_t z, Polygons& result, Polygons (&directional_line_groups)[3]);
struct CubeProperties
{
int64_t side_length; //!< side length of cubes
int64_t height; //!< height of cubes based. This is the distance from one point of a cube to its 3d opposite.
int64_t square_height; //!< square cut across lengths. This is the diagonal distance across a face of the cube.
int64_t max_draw_z_diff; //!< maximum draw z differences. This is the maximum difference in z at which lines need to be drawn.
int64_t max_line_offset; //!< maximum line offsets. This is the maximum distance at which subdivision lines should be drawn from the 2d cube center.
};
/*!
* Rotates a point 120 degrees about the origin.
* \param target the point to rotate.
*/
static void rotatePoint120(Point& target);
/*!
* Rotates a point to align it with the orientation of the infill.
* \param target the point to rotate.
*/
static void rotatePointInitial(Point& target);
/*!
* Determines if a described theoretical cube should be subdivided based on if a sphere that encloses the cube touches the infill mesh.
* \param mesh contains infill layer data and settings
* \param center the center of the described cube
* \param radius the radius of the enclosing sphere
* \return the described cube should be subdivided
*/
static bool isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius);
/*!
* Finds the distance to the infill border at the specified layer from the specified point.
* \param mesh contains infill layer data and settings
* \param layer_nr the number of the specified layer
* \param location the location of the specified point
* \param[out] distance2 the squared distance to the infill border
* \return Code 0: outside, 1: inside, 2: boundary does not exist at specified layer
*/
static int distanceFromPointToMesh(SliceMeshStorage& mesh, int layer_nr, Point& location, int64_t* distance2);
/*!
* Adds the defined line to the specified polygons. It assumes that the specified polygons are all parallel lines. Combines line segments with touching ends closer than epsilon.
* \param[out] group the polygons to add the line to
* \param from the first endpoint of the line
* \param to the second endpoint of the line
*/
void addLineAndCombine(Polygons& group, Point from, Point to);
unsigned int depth; //!< the recursion depth of the cube (0 is most recursed)
Point3 center; //!< center location of the cube in absolute coordinates
SubDivCube* children[8] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; //!< pointers to this cube's eight octree children
static std::vector<CubeProperties> cube_properties_per_recursion_step; //!< precomputed array of basic properties of cubes based on recursion depth.
static double radius_multiplier; //!< multiplier for the bounding radius when determining if a cube should be subdivided
static Point3Matrix rotation_matrix; //!< The rotation matrix to get from axis aligned cubes to cubes standing on a corner point aligned with the infill_angle
static PointMatrix infill_rotation_matrix; //!< Horizontal rotation applied to infill
static int32_t radius_addition; //!< addition to the bounding radius when determining if a cube should be subdivided
};
}
#endif //INFILL_SUBDIVCUBE_H
-152
Ver Arquivo
@@ -1,152 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
#include "../utils/polygon.h"
namespace cura
{
/*!
* Processor class for processing the connections between lines which makes the infill a zigzag pattern.
*
* During the creation of the infill lines, calls are made to a ZigzagConnectorProcessor so that the zigzag connector segments are created
* at the same time as the lines are created.
*
* generate lines within the area of [in_outline], at regular intervals of [line_distance]
* idea:
* intersect a regular grid of 'scanlines' with the area inside [in_outline] (see generateLineInfill)
* zigzag:
* include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_|
*
* we call the areas between two consecutive scanlines a 'scansegment'
*
* algorithm:
* 1. for each line segment of each polygon:
* store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections
* (zigzag): add boundary segments to result
* 2. for each scanline:
* sort the associated intersections
* and connect them using the even-odd rule
*
* zigzag algorithm:
* while walking around (each) polygon (1.)
* if polygon intersects with even scanline
* start boundary segment (add each following segment to the [result])
* when polygon intersects with a scanline again
* stop boundary segment (stop adding segments to the [result])
* if polygon intersects with even scanline again (instead of odd)
* dont add the last line segment to the boundary (unless [connected_zigzags])
*
* Note that ZigZag consists of 3 types:
* - without endpieces
* - with disconnected endpieces
* - with connected endpieces
*
* Each of these has a base class for which ZigzagConnectorProcessor is an ancestor.
* The inheritance structure is as such:
* ZigzagConnectorProcessor
* / \ .
* / \ .
* ActualZigzagConnectorProcessor NoZigZagConnectorProcessor
* / \ for lines infill .
* / \ .
* ZigzagConnectorProcessorEndPieces ZigzagConnectorProcessorNoEndPieces
* / \ for zigzag infill (without end pieces) .
* / \ .
* ZigzagConnectorProcessorConnectedEndPieces ZigzagConnectorProcessorDisconnectedEndPieces
* for zigzag support with normal endpieces for zigzag support with disconnected endpieces for more easy removability
*
* v v zigzag connectors
* <--
* :___: : < scanlines
* | | |
* | | | < infill lines along scanlines
* | |___|
* : : :
* --> winding order of polygon
*
* ^ = even scanline
* ^ ^ no endpieces
*
* start boundary from even scanline! :D
* include only a boundary segment if it starts in an even scanline and ends in an odd scanline
*
* ________
* | | | \ .
* | | | |
* |_____| |__/ .
*
* ^ ^ ^ scanlines
* ^ connected end piece
* include a boundary segment also if it starts in an odd scanline and ends odd,
* or starts in an even scanline and ends in an even scanline,
* but not when it starts in an odd and ends in an even scanline (see top left or bottom middle).
*
* _____
* | | | \ .
* | | | |
* |_____| |__/
*
* ^ ^ ^ scanlines
* ^ disconnected end piece
* Leave out the last line segment of the boundary polygon: from a vertex to the linesegment-scanline intersection.
*/
class ZigzagConnectorProcessor
{
protected:
const PointMatrix& rotation_matrix; //!< The rotation matrix used to enforce the infill angle
Polygons& result; //!< The result of the computation
virtual ~ZigzagConnectorProcessor()
{}
/*!
* Add a line to the result bu unapplying the rotation rotation_matrix.
*
* \param from The one end of the line segment
* \param to The other end of the line segment
*/
void addLine(Point from, Point to)
{
result.addLine(rotation_matrix.unapply(from), rotation_matrix.unapply(to));
}
/*!
* Basic constructor. Inheriting children should call this constructor.
*
* \param rotation_matrix The rotation matrix used to enforce the infill angle
* \param result The resulting line segments (Each line segment is a Polygon with 2 points)
*/
ZigzagConnectorProcessor(const PointMatrix& rotation_matrix, Polygons& result)
: rotation_matrix(rotation_matrix)
, result(result)
{}
public:
/*!
* Handle the next vertex on the outer boundary.
* \param vertex The vertex
*/
virtual void registerVertex(const Point& vertex) = 0;
/*!
* Handle the next intersection between a scanline and the outer boundary.
*
* \param intersection The intersection
* \param scanline_is_even Whether the scanline was even
*/
virtual void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even) = 0;
/*!
* Handle the end of a polygon and prepare for the next.
* This function should reset all member variables.
*/
virtual void registerPolyFinished() = 0;
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_H
@@ -1,75 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorConnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorConnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even)
{ // when a boundary segment starts in an even scanline it is either a normal zigzag connector or an endpiece, so it should be included anyway
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorConnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
|| is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size(); point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,27 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorConnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorConnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_CONNECTED_END_PIECES_H
@@ -1,79 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorDisconnectedEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorDisconnectedEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even;
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // if we left from an even scanline, but not if this is the line segment connecting that zigzag_connector to an even scanline
addLine(last_connector_point, intersection);
}
else if (!previous_scanline_is_even && !this_scanline_is_even) // if we end an odd boundary in an odd segment
{ // add whole oddBoundarySegment (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
// skip the last segment to the [intersection]
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new (odd) zigzag connector, so clear the old one
if (!this_scanline_is_even) // we are either in an end piece or an boundary segment starting in an odd scanline
{ // only when a boundary segment starts in an odd scanline it depends on whether it ends in an odd scanline for whether this segment should be included or not
zigzag_connector.push_back(intersection);
}
last_connector_point = intersection;
}
void ZigzagConnectorProcessorDisconnectedEndPieces::registerPolyFinished()
{
// write end segment if needed (first half of start/end-crossing segment)
if (!last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
}
// write begin segment if needed (second half of start/end-crossing segment)
if (last_scanline_is_even || is_first_zigzag_connector)
{
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() - 1; point_idx++) // -1 cause skipping very last line segment!
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// write very last line segment if needed
if (last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only add last element if boundary segment ends in odd scanline
addLine(first_zigzag_connector[first_zigzag_connector.size() - 2], first_zigzag_connector[first_zigzag_connector.size() - 1]);
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,26 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
#include "../utils/polygon.h"
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
class ZigzagConnectorProcessorDisconnectedEndPieces : public ZigzagConnectorProcessorEndPieces
{
public:
ZigzagConnectorProcessorDisconnectedEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ZigzagConnectorProcessorEndPieces(rotation_matrix, result)
{
}
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_DISCONNECTED_END_PIECES_H
@@ -1,27 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{ // when a boundary segments starts in an even scanline it's either a normal zigzag connector or an endpiece to be included
// note that for ZigzagConnectorProcessorDisconnectedEndPieces only the last line segment from a boundary vertex to a scanline-boundary intersection is omitted
addLine(last_connector_point, vertex);
}
else
{ // it's yet unclear whether the line segment should be included, so we store it until we know
zigzag_connector.push_back(vertex);
}
last_connector_point = vertex;
}
} // namespace cura
@@ -1,32 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
namespace cura
{
class ZigzagConnectorProcessorEndPieces : public ActualZigzagConnectorProcessor
{
protected:
Point last_connector_point; //!< last registered boundary vertex or scanline-coundary intersection
ZigzagConnectorProcessorEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
, last_connector_point(0,0)
{
}
public:
void registerVertex(const Point& vertex);
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_END_PIECES_H
@@ -1,72 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "ZigzagConnectorProcessorNoEndPieces.h"
namespace cura
{
void ZigzagConnectorProcessorNoEndPieces::registerVertex(const Point& vertex)
{
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(vertex);
}
else if (last_scanline_is_even)
{
zigzag_connector.push_back(vertex);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even)
{
bool previous_scanline_is_even = last_scanline_is_even;
last_scanline_is_even = scanline_is_even;
bool this_scanline_is_even = last_scanline_is_even; // for conceptual clarity
if (is_first_zigzag_connector)
{
first_zigzag_connector.push_back(intersection);
first_zigzag_connector_ends_in_even_scanline = this_scanline_is_even;
is_first_zigzag_connector = false;
}
else
{
if (previous_scanline_is_even && !this_scanline_is_even)
{ // add whole zigzag_connector (including the just obtained point)
for (unsigned int point_idx = 1; point_idx < zigzag_connector.size(); point_idx++)
{
addLine(zigzag_connector[point_idx - 1], zigzag_connector[point_idx]);
}
addLine(zigzag_connector.back(), intersection);
zigzag_connector.clear();
}
}
zigzag_connector.clear(); // we're starting a new zigzag connector, so clear the old one
if (this_scanline_is_even) // only boundary segments starting in an even segment are considered
{
zigzag_connector.push_back(intersection);
}
}
void ZigzagConnectorProcessorNoEndPieces::registerPolyFinished()
{
if (!is_first_zigzag_connector && last_scanline_is_even && !first_zigzag_connector_ends_in_even_scanline)
{ // only if it's a normal zigzag connector; not when the whole boundary didn't cross any scanlines
for (unsigned int point_idx = 1; point_idx < first_zigzag_connector.size() ; point_idx++)
{
addLine(first_zigzag_connector[point_idx - 1], first_zigzag_connector[point_idx]);
}
}
// reset member variables
is_first_zigzag_connector = true;
first_zigzag_connector_ends_in_even_scanline = true;
last_scanline_is_even = false;
first_zigzag_connector.clear();
zigzag_connector.clear();
}
} // namespace cura
@@ -1,29 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#ifndef INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#define INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
#include "../utils/polygon.h"
#include "ActualZigzagConnectorProcessor.h"
#include "../utils/intpoint.h"
namespace cura
{
class ZigzagConnectorProcessorNoEndPieces : public ActualZigzagConnectorProcessor
{
public:
ZigzagConnectorProcessorNoEndPieces(const PointMatrix& rotation_matrix, Polygons& result)
: ActualZigzagConnectorProcessor(rotation_matrix, result)
{
}
void registerVertex(const Point& vertex);
void registerScanlineSegmentIntersection(const Point& intersection, bool scanline_is_even);
void registerPolyFinished();
};
} // namespace cura
#endif // INFILL_ZIGZAG_CONNECTOR_PROCESSOR_NO_ENDPIECES_H
+60
Ver Arquivo
@@ -0,0 +1,60 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "inset.h"
#include "polygonOptimizer.h"
#include "utils/polygonUtils.h"
namespace cura {
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
{
int combBoundaryInset = line_width_x/2; // hard coded value
part->combBoundery = part->outline.offset(-combBoundaryInset);
if (insetCount == 0)
{
part->insets.push_back(part->outline);
return;
}
for(int i=0; i<insetCount; i++)
{
part->insets.push_back(Polygons());
if (i == 0)
{
offsetSafe(part->outline, - line_width_x/2, line_width_x, part->insets[i], avoidOverlappingPerimeters);
} else if (i == 1)
{
offsetExtrusionWidth(part->insets[i-1], true, line_width_0, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
} else
{
offsetExtrusionWidth(part->insets[i-1], true, line_width_x, part->insets[i], &part->perimeterGaps, avoidOverlappingPerimeters);
}
optimizePolygons(part->insets[i]);
if (part->insets[i].size() < 1)
{
part->insets.pop_back();
break;
}
}
}
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters)
{
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
generateInsets(&layer->parts[partNr], line_width_0, line_width_x, insetCount, avoidOverlappingPerimeters);
}
//Remove the parts which did not generate an inset. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 inset line.
for(unsigned int partNr = 0; partNr < layer->parts.size(); partNr++)
{
if (layer->parts[partNr].insets.size() < 1)
{
layer->parts.erase(layer->parts.begin() + partNr);
partNr -= 1;
}
}
}
}//namespace cura
+34
Ver Arquivo
@@ -0,0 +1,34 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef INSET_H
#define INSET_H
#include "sliceDataStorage.h"
namespace cura
{
/*!
* Generates the insets / perimeters for a single layer part.
*
* \param part The part for which to generate the insets.
* \param line_width_0 Line width of the outer wall
* \param line_width_x Line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayerPart* part, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
/*!
* Generates the insets / perimeters for all parts in a layer.
*
* \param layer The layer for which to generate the insets.
* \param line_width_0 Line width of the outer wall
* \param line_width_x Line width of other walls
* \param insetCount The number of insets to to generate
* \param avoidOverlappingPerimeters Whether to remove the parts of two consecutive perimeters where they have overlap (and store the gaps thus created in the \p part)
*/
void generateInsets(SliceLayer* layer, int line_width_0, int line_width_x, int insetCount, bool avoidOverlappingPerimeters);
}//namespace cura
#endif//INSET_H
+37 -50
Ver Arquivo
@@ -1,11 +1,8 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include "layerPart.h"
#include "settings/settings.h"
#include "progress/Progress.h"
#include "multithreadOpenMP.h"
#include "utils/SVG.h" // debug output
#include "settings.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
@@ -14,7 +11,7 @@ each of these groups is called a "part", which sometimes are also known as "isla
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundary of this part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
@@ -23,80 +20,70 @@ namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes)
{
storageLayer.openPolyLines = layer->openPolylines;
storageLayer.openLines = layer->openPolygons;
if (union_all_remove_holes)
{
for(unsigned int i=0; i<layer->polygons.size(); i++)
for(unsigned int i=0; i<layer->polygonList.size(); i++)
{
if (PolygonRef{layer->polygons[i]}.orientation())
PolygonRef{layer->polygons[i]}.reverse();
if (layer->polygonList[i].orientation())
layer->polygonList[i].reverse();
}
}
std::vector<PolygonsPart> result;
result = layer->polygons.splitIntoParts(union_layers || union_all_remove_holes);
std::vector<Polygons> result;
result = layer->polygonList.splitIntoParts(union_layers || union_all_remove_holes);
for(unsigned int i=0; i<result.size(); i++)
{
storageLayer.parts.emplace_back();
storageLayer.parts.push_back(SliceLayerPart());
storageLayer.parts[i].outline = result[i];
storageLayer.parts[i].boundaryBox.calculate(storageLayer.parts[i].outline);
}
}
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes)
{
const auto total_layers = slicer->layers.size();
// mesh.layers.resize(total_layers); TODO: put this back?
assert(mesh.layers.size() == total_layers);
#pragma omp parallel for default(none) shared(mesh,slicer) firstprivate(union_layers,union_all_remove_holes) schedule(dynamic)
for(unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
{ MULTITHREAD_FOR_CATCH_EXCEPTION(
mesh.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
mesh.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(mesh.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
)}
handleMultithreadAbort();
for(unsigned int layer_nr = 0; layer_nr < slicer->layers.size(); layer_nr++)
{
storage.layers.push_back(SliceLayer());
storage.layers[layer_nr].sliceZ = slicer->layers[layer_nr].z;
storage.layers[layer_nr].printZ = slicer->layers[layer_nr].z;
createLayerWithParts(storage.layers[layer_nr], &slicer->layers[layer_nr], union_layers, union_all_remove_holes);
logProgress("layerparts", layer_nr + 1, slicer->layers.size());
}
}
void layerparts2HTML(SliceDataStorage& storage, const char* filename, bool all_layers, int layer_nr)
void dumpLayerparts(SliceDataStorage& storage, const char* filename)
{
FILE* out = fopen(filename, "w");
fprintf(out, "<!DOCTYPE html><html><body>");
Point3 modelSize = storage.model_size;
Point3 modelMin = storage.model_min;
Point model_min_2d = Point(modelMin.x, modelMin.y);
Point model_max_2d = Point(modelSize.x, modelSize.y) + model_min_2d;
AABB aabb(model_min_2d, model_max_2d);
SVG svg(filename, aabb);
for(SliceMeshStorage& mesh : storage.meshes)
{
for(unsigned int layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++)
for(SliceLayer& layer : mesh.layers)
{
if (!(all_layers || int(layer_idx) == layer_nr)) { continue; }
SliceLayer& layer = mesh.layers[layer_idx];
// fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"width: 500px; height:500px\">\n");
for(SliceLayerPart& part : layer.parts)
{
svg.writeAreas(part.outline);
svg.writePoints(part.outline);
// for(unsigned int j=0;j<part.outline.size();j++)
// {
// fprintf(out, "<polygon points=\"");
// for(unsigned int k=0;k<part.outline[j].size();k++)
// fprintf(out, "%f,%f ", float(part.outline[j][k].X - modelMin.x)/modelSize.x*500, float(part.outline[j][k].Y - modelMin.y)/modelSize.y*500);
// if (j == 0)
// fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
// else
// fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
// }
for(unsigned int j=0;j<part.outline.size();j++)
{
fprintf(out, "<polygon points=\"");
for(unsigned int k=0;k<part.outline[j].size();k++)
fprintf(out, "%f,%f ", float(part.outline[j][k].X - modelMin.x)/modelSize.x*500, float(part.outline[j][k].Y - modelMin.y)/modelSize.y*500);
if (j == 0)
fprintf(out, "\" style=\"fill:gray; stroke:black;stroke-width:1\" />\n");
else
fprintf(out, "\" style=\"fill:red; stroke:black;stroke-width:1\" />\n");
}
}
// fprintf(out, "</svg>\n");
fprintf(out, "</svg>\n");
}
}
fprintf(out, "</body></html>");
fclose(out);
}
}//namespace cura
+3 -4
Ver Arquivo
@@ -4,7 +4,6 @@
#include "sliceDataStorage.h"
#include "slicer.h"
#include "commandSocket.h"
/*
The layer-part creation step is the first step in creating actual useful data for 3D printing.
@@ -13,7 +12,7 @@ each of these groups is called a "part", which sometimes are also known as "isla
isolated areas in the 2D layer with possible holes.
Creating "parts" is an important step, as all elements in a single part should be printed before going to another part.
And all every bit inside a single part can be printed without the nozzle leaving the boundary of this part.
And all every bit inside a single part can be printed without the nozzle leaving the boundery of this part.
It's also the first step that stores the result in the "data storage" so all other steps can access it.
*/
@@ -22,9 +21,9 @@ namespace cura {
void createLayerWithParts(SliceLayer& storageLayer, SlicerLayer* layer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& mesh, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void createLayerParts(SliceMeshStorage& storage, Slicer* slicer, bool union_layers, bool union_all_remove_holes);
void layerparts2HTML(SliceDataStorage& mesh, const char* filename, bool all_layers = true, int layer_nr = -1);
void dumpLayerparts(SliceDataStorage& storage, const char* filename);
}//namespace cura
+137 -353
Ver Arquivo
@@ -14,43 +14,30 @@
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "utils/string.h"
#include "sliceDataStorage.h"
#include "FffProcessor.h"
#include "settings/SettingRegistry.h"
#include "modelFile/modelFile.h"
#include "settings.h"
#include "settingRegistry.h"
#include "multiVolumes.h"
#include "polygonOptimizer.h"
#include "slicer.h"
#include "layerPart.h"
#include "inset.h"
#include "skin.h"
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOrderOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#include "fffProcessor.h"
#include "settings/SettingsToGV.h"
namespace cura
{
void print_usage()
{
logAlways("\n");
logAlways("usage:\n");
logAlways("CuraEngine help\n");
logAlways("\tShow this help message\n");
logAlways("\n");
logAlways("CuraEngine connect <host>[:<port>] [-j <settings.def.json>]\n");
logAlways(" --connect <host>[:<port>]\n\tConnect to <host> via a command socket, \n\tinstead of passing information via the command line\n");
logAlways(" -j<settings.def.json>\n\tLoad settings.json file to register all settings and their defaults\n");
logAlways(" -v\n\tIncrease the verbose level (show log messages).\n");
logAlways("\n");
logAlways("CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e<extruder_nr>] [-o <output.gcode>] [-l <model.stl>] [--next]\n");
logAlways(" -v\n\tIncrease the verbose level (show log messages).\n");
logAlways(" -p\n\tLog progress information.\n");
logAlways(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n");
logAlways(" -s <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
logAlways(" -l <model_file>\n\tLoad an STL model. \n");
logAlways(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n");
logAlways(" -e<extruder_nr>\n\tSwitch setting focus to the extruder train with the given number.\n");
logAlways(" --next\n\tGenerate gcode for the previously supplied mesh group and append that to \n\tthe gcode of further models for one-at-a-time printing.\n");
logAlways(" -o <output_file>\n\tSpecify a file to which to write the generated gcode.\n");
logAlways("\n");
logAlways("The settings are appended to the last supplied object:\n");
logAlways("CuraEngine slice [general settings] \n\t-g [current group settings] \n\t-e0 [extruder train 0 settings] \n\t-l obj_inheriting_from_last_extruder_train.stl [object settings] \n\t--next [next group settings]\n\t... etc.\n");
logAlways("\n");
logAlways("In order to load machine definitions from custom locations, you need to create the environment variable CURA_ENGINE_SEARCH_PATH, which should contain all search paths delimited by a (semi-)colon.\n");
logAlways("\n");
cura::logError("usage: CuraEngine [-h] [-v] [-m 3x3matrix] [-c <config file>] [-s <settingkey>=<value>] -o <output.gcode> <model.stl>\n");
}
//Signal handler for a "floating point exception", which can also be integer division by zero errors.
@@ -61,233 +48,6 @@ void signal_FPE(int n)
exit(1);
}
void print_call(int argc, char **argv)
{
cura::logError("Command called:\n");
for (int idx= 0; idx < argc; idx++)
cura::logError("%s ", argv[idx]);
cura::logError("\n");
}
void connect(int argc, char **argv)
{
std::string ip;
int port = 49674;
// parse ip port
std::string ip_port(argv[2]);
if (ip_port.find(':') != std::string::npos)
{
ip = ip_port.substr(0, ip_port.find(':'));
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
for(int argn = 3; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'v':
cura::increaseVerboseLevel();
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], FffProcessor::getInstance()))
{
cura::logError("Failed to load json file: %s\n", argv[argn]);
std::exit(1);
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
break;
}
}
}
}
CommandSocket::instantiate();
CommandSocket::getInstance()->connect(ip, port);
}
void slice(int argc, char **argv)
{
FffProcessor::getInstance()->time_keeper.restart();
FMatrix3x3 transformation; // the transformation applied to a model when loaded
MeshGroup* meshgroup = new MeshGroup(FffProcessor::getInstance());
int extruder_train_nr = 0;
SettingsBase* last_extruder_train = nullptr;
// extruder defaults cannot be loaded yet cause no json has been parsed
SettingsBase* last_settings_object = FffProcessor::getInstance();
for(int argn = 2; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
if (str[1] == '-')
{
if (stringcasecompare(str, "--next") == 0)
{
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
for (int extruder_nr = 0; extruder_nr < FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count"); extruder_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_nr); // create new extruder train objects or use already existing ones
}
meshgroup->finalize();
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
// initialize loading of new meshes
FffProcessor::getInstance()->time_keeper.restart();
delete meshgroup;
meshgroup = new MeshGroup(FffProcessor::getInstance());
last_extruder_train = meshgroup->createExtruderTrain(0);
last_settings_object = meshgroup;
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
}else{
cura::logError("Unknown option: %s\n", str);
}
}else{
for(str++; *str; str++)
{
switch(*str)
{
case 'v':
cura::increaseVerboseLevel();
break;
case 'p':
cura::enableProgressLogging();
break;
case 'j':
argn++;
if (SettingRegistry::getInstance()->loadJSONsettings(argv[argn], last_settings_object))
{
cura::logError("Failed to load json file: %s\n", argv[argn]);
std::exit(1);
}
break;
case 'e':
str++;
extruder_train_nr = int(*str - '0'); // TODO: parse int instead (now "-e10"="-e:" , "-e11"="-e;" , "-e12"="-e<" .. etc)
last_settings_object = meshgroup->createExtruderTrain(extruder_train_nr);
last_extruder_train = last_settings_object;
break;
case 'l':
argn++;
log("Loading %s from disk...\n", argv[argn]);
transformation = last_settings_object->getSettingAsPointMatrix("mesh_rotation_matrix"); // the transformation applied to a model when loaded
if (!last_extruder_train)
{
last_extruder_train = meshgroup->createExtruderTrain(0); // assume a json has already been provided on the command line
}
if (!loadMeshIntoMeshGroup(meshgroup, argv[argn], transformation, last_extruder_train))
{
logError("Failed to load model: %s\n", argv[argn]);
std::exit(1);
}
else
{
last_settings_object = &(meshgroup->meshes.back()); // pointer is valid until a new object is added, so this is OK
}
break;
case 'o':
argn++;
if (!FffProcessor::getInstance()->setTargetFile(argv[argn]))
{
cura::logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 'g':
last_settings_object = meshgroup;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
last_settings_object->setSetting(argv[argn], valuePtr);
}
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
exit(1);
break;
}
}
}
}
else
{
cura::logError("Unknown option: %s\n", argv[argn]);
print_call(argc, argv);
print_usage();
exit(1);
}
}
int extruder_count = FffProcessor::getInstance()->getSettingAsCount("machine_extruder_count");
for (extruder_train_nr = 0; extruder_train_nr < extruder_count; extruder_train_nr++)
{ // initialize remaining extruder trains and load the defaults
meshgroup->createExtruderTrain(extruder_train_nr); // create new extruder train objects or use already existing ones
}
#ifndef DEBUG
try {
#endif
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
meshgroup->finalize();
log("Loaded from disk in %5.3fs\n", FffProcessor::getInstance()->time_keeper.restart());
//start slicing
FffProcessor::getInstance()->processMeshGroup(meshgroup);
#ifndef DEBUG
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
#endif
//Finalize the processor, this adds the end.gcode. And reports statistics.
FffProcessor::getInstance()->finalize();
delete meshgroup;
}
}//namespace cura
using namespace cura;
int main(int argc, char **argv)
@@ -302,125 +62,149 @@ int main(int argc, char **argv)
signal(SIGFPE, signal_FPE);
#endif
Progress::init();
std::cerr << std::boolalpha;
logAlways("\n");
logAlways("Cura_SteamEngine version %s\n", VERSION);
logAlways("Copyright (C) 2014 David Braam\n");
logAlways("\n");
logAlways("This program is free software: you can redistribute it and/or modify\n");
logAlways("it under the terms of the GNU Affero General Public License as published by\n");
logAlways("the Free Software Foundation, either version 3 of the License, or\n");
logAlways("(at your option) any later version.\n");
logAlways("\n");
logAlways("This program is distributed in the hope that it will be useful,\n");
logAlways("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
logAlways("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
logAlways("GNU Affero General Public License for more details.\n");
logAlways("\n");
logAlways("You should have received a copy of the GNU Affero General Public License\n");
logAlways("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
fffProcessor processor;
std::vector<std::string> files;
logCopyright("Cura_SteamEngine version %s\n", VERSION);
logCopyright("Copyright (C) 2014 David Braam\n");
logCopyright("\n");
logCopyright("This program is free software: you can redistribute it and/or modify\n");
logCopyright("it under the terms of the GNU Affero General Public License as published by\n");
logCopyright("the Free Software Foundation, either version 3 of the License, or\n");
logCopyright("(at your option) any later version.\n");
logCopyright("\n");
logCopyright("This program is distributed in the hope that it will be useful,\n");
logCopyright("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
logCopyright("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
logCopyright("GNU Affero General Public License for more details.\n");
logCopyright("\n");
logCopyright("You should have received a copy of the GNU Affero General Public License\n");
logCopyright("along with this program. If not, see <http://www.gnu.org/licenses/>.\n");
if (argc < 2)
CommandSocket* commandSocket = NULL;
std::string ip;
int port = 49674;
for(int argn = 1; argn < argc; argn++)
{
print_usage();
exit(1);
}
if (stringcasecompare(argv[1], "connect") == 0)
{
connect(argc, argv);
}
else if (stringcasecompare(argv[1], "slice") == 0)
{
slice(argc, argv);
}
else if (stringcasecompare(argv[1], "help") == 0)
{
print_usage();
exit(0);
}
else if (stringcasecompare(argv[1], "analyse") == 0)
{ // CuraEngine analyse [json] [output.gv] [engine_settings] -[p|i|e|w]
// p = show parent-child relations
// i = show inheritance function
// e = show error functions
// w = show warning functions
// dot refl_ff.gv -Tpng > rafl_ff_dotted.png
// see meta/HOWTO.txt
bool parent_child_viz = false;
bool inherit_viz = false;
bool warning_viz = false;
bool error_viz = false;
bool global_only_viz = false;
if (argc >= 6)
char* str = argv[argn];
if (str[0] == '-')
{
char* str = argv[5];
if (str[0] == '-')
if (str[1] == '-')
{
if (stringcasecompare(str, "--connect") == 0)
{
commandSocket = new CommandSocket(&processor);
std::string ip_port(argv[argn + 1]);
if (ip_port.find(':') != std::string::npos)
{
ip = ip_port.substr(0, ip_port.find(':'));
port = std::stoi(ip_port.substr(ip_port.find(':') + 1).data());
}
argn += 1;
}
else if (stringcasecompare(str, "--") == 0)
{
try {
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
if (files.size() > 0)
processor.processFiles(files);
files.clear();
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
break;
}else{
cura::logError("Unknown option: %s\n", str);
}
}else{
for(str++; *str; str++)
{
switch(*str)
{
case 'h':
print_usage();
exit(1);
case 'v':
cura::increaseVerboseLevel();
break;
case 'j':
argn++;
if (!SettingRegistry::getInstance()->loadJSON(argv[argn]))
{
cura::logError("ERROR: Failed to load json file: %s\n", argv[argn]);
}
break;
case 'p':
parent_child_viz = true;
cura::enableProgressLogging();
break;
case 'i':
inherit_viz = true;
case 'o':
argn++;
if (!processor.setTargetFile(argv[argn]))
{
cura::logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
break;
case 'e':
error_viz = true;
break;
case 'w':
warning_viz = true;
break;
case 'g':
global_only_viz = true;
case 's':
{
//Parse the given setting and store it.
argn++;
char* valuePtr = strchr(argv[argn], '=');
if (valuePtr)
{
*valuePtr++ = '\0';
processor.setSetting(argv[argn], valuePtr);
}
}
break;
default:
cura::logError("Unknown option: %c\n", *str);
print_call(argc, argv);
print_usage();
break;
}
}
}
}else{
files.push_back(argv[argn]);
}
else
}
if (!SettingRegistry::getInstance()->settingsLoaded())
{
//If no json file has been loaded, try to load the default.
if (!SettingRegistry::getInstance()->loadJSON("fdmprinter.json"))
{
cura::log("\n");
cura::log("usage:\n");
cura::log("CuraEngine analyse <fdmPrinter.def.json> <output.gv> <engine_settings_list> -[p|i|e|w]\n");
cura::log("\tGenerate a grpah to visualize the setting inheritance structure.\n");
cura::log("\t<fdmPrinter.def.json>\n\tThe base seting definitions file.\n");
cura::log("\t<output.gv>\n\tThe output file.\n");
cura::log("\t<engine_settings_list>\n\tA text file with all setting keys used in the engine, separated by newlines.\n");
cura::log("\t-[p|i|e|w]\n\tOptions for what to include in the visualization\n");
cura::log("\t\tp\tVisualize the parent-child relationship.\n");
cura::log("\t\ti\tVisualize inheritance function relationships.\n");
cura::log("\t\te\tVisualize (max/min) error function relationships.\n");
cura::log("\t\tw\tVisualize (max/min) warning function relationships.\n");
cura::log("\n");
logError("ERROR: Failed to load json file: fdmprinter.json\n");
}
}
SettingsToGv gv_out(argv[3], argv[4], parent_child_viz, inherit_viz, error_viz, warning_viz, global_only_viz);
if (gv_out.generate(std::string(argv[2])))
{
cura::logError("Failed to analyse json file: %s\n", argv[2]);
}
exit(0);
if(commandSocket)
{
commandSocket->connect(ip, port);
}
else
{
cura::logError("Unknown command: %s\n", argv[1]);
print_call(argc, argv);
print_usage();
exit(1);
#ifndef DEBUG
try {
#endif
//Catch all exceptions, this prevents the "something went wrong" dialog on windows to pop up on a thrown exception.
// Only ClipperLib currently throws exceptions. And only in case that it makes an internal error.
if (files.size() > 0)
processor.processFiles(files);
#ifndef DEBUG
}catch(...){
cura::logError("Unknown exception\n");
exit(1);
}
#endif
//Finalize the processor, this adds the end.gcode. And reports statistics.
processor.finalize();
}
return 0;
}
}
+38 -37
Ver Arquivo
@@ -1,20 +1,14 @@
#include "mesh.h"
#include "utils/logoutput.h"
namespace cura
{
const int vertex_meld_distance = MM2INT(0.03);
/*!
* returns a hash for the location, but first divides by the vertex_meld_distance,
* so that any point within a box of vertex_meld_distance by vertex_meld_distance would get mapped to the same hash.
*/
static inline uint32_t pointHash(const Point3& p)
static inline uint32_t pointHash(Point3& p)
{
return ((p.x + vertex_meld_distance/2) / vertex_meld_distance) ^ (((p.y + vertex_meld_distance/2) / vertex_meld_distance) << 10) ^ (((p.z + vertex_meld_distance/2) / vertex_meld_distance) << 20);
}
Mesh::Mesh(SettingsBaseVirtual* parent)
Mesh::Mesh(SettingsBase* parent)
: SettingsBase(parent)
{
}
@@ -53,35 +47,40 @@ void Mesh::finish()
for(unsigned int i=0; i<faces.size(); i++)
{
MeshFace& face = faces[i];
// faces are connected via the outside
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i, face.vertex_index[2]);
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i, face.vertex_index[0]);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i, face.vertex_index[1]);
face.connected_face_index[0] = getFaceIdxWithPoints(face.vertex_index[0], face.vertex_index[1], i); // faces are connected via the outside
face.connected_face_index[1] = getFaceIdxWithPoints(face.vertex_index[1], face.vertex_index[2], i);
face.connected_face_index[2] = getFaceIdxWithPoints(face.vertex_index[2], face.vertex_index[0], i);
}
}
Point3 Mesh::min() const
Point3 Mesh::min()
{
return aabb.min;
}
Point3 Mesh::max() const
{
return aabb.max;
}
AABB3D Mesh::getAABB() const
{
return aabb;
}
void Mesh::expandXY(int64_t offset)
{
if (offset)
if (vertices.size() < 1)
return Point3(0, 0, 0);
Point3 ret = vertices[0].p;
for(unsigned int i=0; i<vertices.size(); i++)
{
aabb.expandXY(offset);
ret.x = std::min(ret.x, vertices[i].p.x);
ret.y = std::min(ret.y, vertices[i].p.y);
ret.z = std::min(ret.z, vertices[i].p.z);
}
return ret;
}
Point3 Mesh::max()
{
if (vertices.size() < 1)
return Point3(0, 0, 0);
Point3 ret = vertices[0].p;
for(unsigned int i=0; i<vertices.size(); i++)
{
ret.x = std::max(ret.x, vertices[i].p.x);
ret.y = std::max(ret.y, vertices[i].p.y);
ret.z = std::max(ret.z, vertices[i].p.z);
}
return ret;
}
int Mesh::findIndexOfVertex(const Point3& v)
int Mesh::findIndexOfVertex(Point3& v)
{
uint32_t hash = pointHash(v);
@@ -94,9 +93,6 @@ int Mesh::findIndexOfVertex(const Point3& v)
}
vertex_hash_map[hash].push_back(vertices.size());
vertices.emplace_back(v);
aabb.include(v);
return vertices.size() - 1;
}
@@ -124,13 +120,17 @@ See <a href="http://stackoverflow.com/questions/14066933/direct-way-of-computing
*/
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const
int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx)
{
std::vector<int> candidateFaces; // in case more than two faces meet at an edge, multiple candidates are generated
int notFaceVertexIdx = -1; // index of the third vertex of the face corresponding to notFaceIdx
for(int f : vertices[idx0].connected_faces) // search through all faces connected to the first vertex and find those that are also connected to the second
{
if (f == notFaceIdx)
{
for (int i = 0; i<3; i++) // find the vertex which is not idx0 or idx1
if (faces[f].vertex_index[i] != idx0 && faces[f].vertex_index[i] != idx1)
notFaceVertexIdx = faces[f].vertex_index[i];
continue;
}
if ( faces[f].vertex_index[0] == idx1 // && faces[f].vertex_index[1] == idx0 // next face should have the right direction!
@@ -140,10 +140,12 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
}
if (candidateFaces.size() == 0) { cura::logWarning("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() == 0) { cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() == 1) { return candidateFaces[0]; }
if (notFaceVertexIdx < 0) { cura::logError("Couldn't find third point on face %i.\n", notFaceIdx); return -1; }
if (candidateFaces.size() % 2 == 0) cura::log("Warning! Edge with uneven number of faces connecting it!(%i)\n", candidateFaces.size()+1);
FPoint3 vn = vertices[idx1].p - vertices[idx0].p;
@@ -178,6 +180,7 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
if (angle == 0)
{
cura::log("Warning! Overlapping faces: face %i and face %i.\n", notFaceIdx, candidateFace);
std::cerr<< n.vSize() <<"; "<<n1.vSize()<<";"<<n0.vSize() <<std::endl;
}
if (angle < smallestAngle)
{
@@ -185,8 +188,6 @@ int Mesh::getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVe
bestIdx = candidateFace;
}
}
if (bestIdx < 0) cura::logWarning("Couldn't find face connected to face %i.\n", notFaceIdx);
if (bestIdx < 0) cura::logError("Couldn't find face connected to face %i.\n", notFaceIdx);
return bestIdx;
}
}//namespace cura
+11 -37
Ver Arquivo
@@ -1,11 +1,8 @@
#ifndef MESH_H
#define MESH_H
#include "settings/settings.h"
#include "utils/AABB3D.h"
#include "settings.h"
namespace cura
{
/*!
Vertex type to be used in a Mesh.
@@ -17,7 +14,7 @@ public:
Point3 p; //!< location of the vertex
std::vector<uint32_t> connected_faces; //!< list of the indices of connected faces
MeshVertex(Point3 p) : p(p) {connected_faces.reserve(8);} //!< doesn't set connected_faces
MeshVertex(Point3 p) : p(p) {} //!< doesn't set connected_faces
};
/*! A MeshFace is a 3 dimensional model triangle with 3 points. These points are already converted to integers
@@ -57,52 +54,29 @@ See MeshFace for the specifics of how/when faces are connected.
class Mesh : public SettingsBase // inherits settings
{
//! The vertex_hash_map stores a index reference of each vertex for the hash of that location. Allows for quick retrieval of points with the same location.
std::unordered_map<uint32_t, std::vector<uint32_t> > vertex_hash_map;
AABB3D aabb;
std::map<uint32_t, std::vector<uint32_t> > vertex_hash_map;
public:
std::vector<MeshVertex> vertices;//!< list of all vertices in the mesh
std::vector<MeshFace> faces; //!< list of all faces in the mesh
Mesh(SettingsBaseVirtual* parent); //!< initializes the settings
Mesh(SettingsBase* parent); //!< initializes the settings
void addFace(Point3& v0, Point3& v1, Point3& v2); //!< add a face to the mesh without settings it's connected_faces.
void clear(); //!< clears all data
void finish(); //!< complete the model : set the connected_face_index fields of the faces.
Point3 min() const; //!< min (in x,y and z) vertex of the bounding box
Point3 max() const; //!< max (in x,y and z) vertex of the bounding box
AABB3D getAABB() const; //!< Get the axis aligned bounding box
void expandXY(int64_t offset); //!< Register applied horizontal expansion in the AABB
/*!
* Offset the whole mesh (all vertices and the bounding box).
* \param offset The offset byu which to offset the whole mesh.
*/
void offset(Point3 offset)
{
if (offset == Point3(0,0,0)) { return; }
for(MeshVertex& v : vertices)
v.p += offset;
aabb.offset(offset);
}
Point3 min(); //!< min (in x,y and z) vertex of the bounding box
Point3 max(); //!< max (in x,y and z) vertex of the bounding box
private:
int findIndexOfVertex(const Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
int findIndexOfVertex(Point3& v); //!< find index of vertex close to the given point, or create a new vertex and return its index.
/*!
* Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
*
* In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*
* \param idx0 the first vertex index
* \param idx1 the second vertex index
* \param notFaceIdx the index of a face which shouldn't be returned
* \param notFaceVertexIdx should be the third vertex of face \p notFaceIdx.
* \return the face index of a face sharing the edge from \p idx0 to \p idx1
Get the index of the face connected to the face with index \p notFaceIdx, via vertices \p idx0 and \p idx1.
In case multiple faces connect with the same edge, return the next counter-clockwise face when viewing from \p idx1 to \p idx0.
*/
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx, int notFaceVertexIdx) const;
int getFaceIdxWithPoints(int idx0, int idx1, int notFaceIdx);
};
}//namespace cura
#endif//MESH_H
+150
Ver Arquivo
@@ -0,0 +1,150 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include "modelFile.h"
#include "../utils/logoutput.h"
#include "../utils/string.h"
FILE* binaryMeshBlob = nullptr;
/* Custom fgets function to support Mac line-ends in Ascii STL files. OpenSCAD produces this when used on Mac */
void* fgets_(char* ptr, size_t len, FILE* f)
{
while(len && fread(ptr, 1, 1, f) > 0)
{
if (*ptr == '\n' || *ptr == '\r')
{
*ptr = '\0';
return ptr;
}
ptr++;
len--;
}
return nullptr;
}
bool loadModelSTL_ascii(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rt");
char buffer[1024];
FPoint3 vertex;
int n = 0;
Point3 v0(0,0,0), v1(0,0,0), v2(0,0,0);
while(fgets_(buffer, sizeof(buffer), f))
{
if (sscanf(buffer, " vertex %f %f %f", &vertex.x, &vertex.y, &vertex.z) == 3)
{
n++;
switch(n)
{
case 1:
v0 = matrix.apply(vertex);
break;
case 2:
v1 = matrix.apply(vertex);
break;
case 3:
v2 = matrix.apply(vertex);
mesh->addFace(v0, v1, v2);
n = 0;
break;
}
}
}
fclose(f);
mesh->finish();
return true;
}
bool loadModelSTL_binary(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "rb");
char buffer[80];
uint32_t faceCount;
//Skip the header
if (fread(buffer, 80, 1, f) != 1)
{
fclose(f);
return false;
}
//Read the face count
if (fread(&faceCount, sizeof(uint32_t), 1, f) != 1)
{
fclose(f);
return false;
}
//For each face read:
//float(x,y,z) = normal, float(X,Y,Z)*3 = vertexes, uint16_t = flags
for(unsigned int i=0;i<faceCount;i++)
{
if (fread(buffer, sizeof(float) * 3, 1, f) != 1)
{
fclose(f);
return false;
}
float v[9];
if (fread(v, sizeof(float) * 9, 1, f) != 1)
{
fclose(f);
return false;
}
Point3 v0 = matrix.apply(FPoint3(v[0], v[1], v[2]));
Point3 v1 = matrix.apply(FPoint3(v[3], v[4], v[5]));
Point3 v2 = matrix.apply(FPoint3(v[6], v[7], v[8]));
mesh->addFace(v0, v1, v2);
if (fread(buffer, sizeof(uint16_t), 1, f) != 1)
{
fclose(f);
return false;
}
}
fclose(f);
mesh->finish();
return true;
}
bool loadModelSTL(Mesh* mesh, const char* filename, FMatrix3x3& matrix)
{
FILE* f = fopen(filename, "r");
char buffer[6];
if (f == nullptr)
return false;
if (fread(buffer, 5, 1, f) != 1)
{
fclose(f);
return false;
}
fclose(f);
buffer[5] = '\0';
if (stringcasecompare(buffer, "solid") == 0)
{
bool load_success = loadModelSTL_ascii(mesh, filename, matrix);
if (!load_success)
return false;
// This logic is used to handle the case where the file starts with
// "solid" but is a binary file.
if (mesh->faces.size() < 1)
{
mesh->clear();
return loadModelSTL_binary(mesh, filename, matrix);
}
return true;
}
return loadModelSTL_binary(mesh, filename, matrix);
}
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix)
{
const char* ext = strrchr(filename, '.');
if (ext && (strcmp(ext, ".stl") == 0 || strcmp(ext, ".STL") == 0))
{
object->meshes.emplace_back(object);
return loadModelSTL(&object->meshes[object->meshes.size()-1], filename, matrix);
}
return false;
}
+98
Ver Arquivo
@@ -0,0 +1,98 @@
/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#ifndef MODELFILE_H
#define MODELFILE_H
/**
modelFile contains the model loaders for the slicer. The model loader turns any format that it can read into a list of triangles with 3 X/Y/Z points.
The format returned is a Model class with an array of faces, which have integer points with a resolution of 1 micron. Giving a maximum object size of 4 meters.
**/
#include "../mesh.h"
//A PrintObject is a 3D model with 1 or more 3D meshes.
class PrintObject : public SettingsBase
{
public:
std::vector<Mesh> meshes;
PrintObject(SettingsBase* settings_base)
: SettingsBase(settings_base)
{
}
Point3 min() //! minimal corner of bounding box
{
if (meshes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = meshes[0].min();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].min();
ret.x = std::min(ret.x, v.x);
ret.y = std::min(ret.y, v.y);
ret.z = std::min(ret.z, v.z);
}
return ret;
}
Point3 max() //! maximal corner of bounding box
{
if (meshes.size() < 1)
return Point3(0, 0, 0);
Point3 ret = meshes[0].max();
for(unsigned int i=1; i<meshes.size(); i++)
{
Point3 v = meshes[i].max();
ret.x = std::max(ret.x, v.x);
ret.y = std::max(ret.y, v.y);
ret.z = std::max(ret.z, v.z);
}
return ret;
}
void clear()
{
for(Mesh& m : meshes)
m.clear();
}
void offset(Point3 offset)
{
for(Mesh& m : meshes)
for(MeshVertex& v : m.vertices)
v.p += offset;
}
void finalize()
{
// If a mesh position was given, put the mesh at this position in 3D space.
if (hasSetting("mesh_position_x") || hasSetting("mesh_position_y") || hasSetting("mesh_position_z"))
{
Point3 object_min = min();
Point3 object_max = max();
Point3 object_size = object_max - object_min;
Point3 object_offset = Point3(-object_min.x - object_size.x / 2, -object_min.y - object_size.y / 2, -object_min.z);
if (hasSetting("mesh_position_x"))
object_offset.x += getSettingInMicrons("mesh_position_x");
if (hasSetting("mesh_position_y"))
object_offset.y += getSettingInMicrons("mesh_position_y");
if (hasSetting("mesh_position_z"))
object_offset.z += getSettingInMicrons("mesh_position_z");
offset(object_offset);
}
//If the machine settings have been supplied, offset the given position vertices to the center of vertices (0,0,0) is at the bed center.
if (hasSetting("machine_center_is_zero") && !getSettingBoolean("machine_center_is_zero"))
{
Point3 object_offset = Point3(0, 0, 0);
if (hasSetting("machine_width"))
object_offset.x = getSettingInMicrons("machine_width") / 2;
if (hasSetting("machine_depth"))
object_offset.y = getSettingInMicrons("machine_depth") / 2;
offset(object_offset);
}
}
};
bool loadMeshFromFile(PrintObject* object, const char* filename, FMatrix3x3& matrix);
#endif//MODELFILE_H
+33 -72
Ver Arquivo
@@ -1,97 +1,58 @@
#include "multiVolumes.h"
namespace cura
{
void carveMultipleVolumes(std::vector<Slicer*> &volumes, bool alternate_carve_order)
namespace cura {
void carveMultipleVolumes(std::vector<SliceMeshStorage> &volumes)
{
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for (unsigned int volume_1_idx = 1; volume_1_idx < volumes.size(); volume_1_idx++)
for(unsigned int idx=0; idx < volumes.size(); idx++)
{
Slicer& volume_1 = *volumes[volume_1_idx];
if (volume_1.mesh->getSettingBoolean("infill_mesh")
|| volume_1.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_1.mesh->getSettingBoolean("support_mesh")
)
for(unsigned int idx2=0; idx2<idx; idx2++)
{
continue;
}
for (unsigned int volume_2_idx = 0; volume_2_idx < volume_1_idx; volume_2_idx++)
{
Slicer& volume_2 = *volumes[volume_2_idx];
if (volume_2.mesh->getSettingBoolean("infill_mesh")
|| volume_2.mesh->getSettingBoolean("anti_overhang_mesh")
|| volume_2.mesh->getSettingBoolean("support_mesh")
)
for(unsigned int layerNr=0; layerNr < volumes[idx].layers.size(); layerNr++)
{
continue;
}
if (!volume_1.mesh->getAABB().hit(volume_2.mesh->getAABB()))
{
continue;
}
for (unsigned int layerNr = 0; layerNr < volume_1.layers.size(); layerNr++)
{
SlicerLayer& layer1 = volume_1.layers[layerNr];
SlicerLayer& layer2 = volume_2.layers[layerNr];
if (alternate_carve_order && layerNr % 2 == 0)
SliceLayer* layer1 = &volumes[idx].layers[layerNr];
SliceLayer* layer2 = &volumes[idx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
layer2.polygons = layer2.polygons.difference(layer1.polygons);
}
else
{
layer1.polygons = layer1.polygons.difference(layer2.polygons);
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
layer1->parts[p1].outline = layer1->parts[p1].outline.difference(layer2->parts[p2].outline);
}
}
}
}
}
}
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<Slicer*> &volumes)
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &volumes, int overlap)
{
if (volumes.size() < 2)
if (volumes.size() < 2 || overlap <= 0) return;
for(unsigned int layerNr=0; layerNr < volumes[0].layers.size(); layerNr++)
{
return;
}
int offset_to_merge_other_merged_volumes = 20;
for (Slicer* volume : volumes)
{
int overlap = volume->mesh->getSettingInMicrons("multiple_mesh_overlap");
if (volume->mesh->getSettingBoolean("infill_mesh")
|| volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| volume->mesh->getSettingBoolean("support_mesh")
|| overlap == 0)
Polygons fullLayer;
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
continue;
}
AABB3D aabb(volume->mesh->getAABB());
aabb.expandXY(overlap); // expand to account for the case where two models and their bounding boxes are adjacent along the X or Y-direction
for (unsigned int layer_nr = 0; layer_nr < volume->layers.size(); layer_nr++)
{
Polygons all_other_volumes;
for (Slicer* other_volume : volumes)
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
if (other_volume->mesh->getSettingBoolean("infill_mesh")
|| other_volume->mesh->getSettingBoolean("anti_overhang_mesh")
|| other_volume->mesh->getSettingBoolean("support_mesh")
|| !other_volume->mesh->getAABB().hit(aabb)
|| other_volume == volume
)
{
continue;
}
SlicerLayer& other_volume_layer = other_volume->layers[layer_nr];
all_other_volumes = all_other_volumes.unionPolygons(other_volume_layer.polygons.offset(offset_to_merge_other_merged_volumes));
fullLayer = fullLayer.unionPolygons(layer1->parts[p1].outline.offset(20)); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
}
}
fullLayer = fullLayer.offset(-20); // TODO: put hard coded value in a variable with an explanatory name (and make var a parameter, and perhaps even a setting?)
for(unsigned int volIdx = 0; volIdx < volumes.size(); volIdx++)
{
SliceLayer* layer1 = &volumes[volIdx].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
layer1->parts[p1].outline = fullLayer.intersection(layer1->parts[p1].outline.offset(overlap / 2));
}
SlicerLayer& volume_layer = volume->layers[layer_nr];
volume_layer.polygons = volume_layer.polygons.unionPolygons(all_other_volumes.intersection(volume_layer.polygons.offset(overlap / 2)));
}
}
}
}//namespace cura
+4 -11
Ver Arquivo
@@ -2,22 +2,15 @@
#define MULTIVOLUMES_H
#include "sliceDataStorage.h"
#include "slicer.h"
/* This file contains code to help fixing up and changing layers that are build from multiple volumes. */
namespace cura {
/*!
*
* \param alternate_carve_order Whether to switch which model carves out of which with every layer
*/
void carveMultipleVolumes(std::vector<Slicer*> &meshes, bool alternate_carve_order);
void carveMultipleVolumes(std::vector<SliceMeshStorage> &meshes);
/*!
* Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
* This generates some overlap in dual extrusion, for better bonding in touching parts.
*/
void generateMultipleVolumesOverlap(std::vector<Slicer*> &meshes);
//Expand each layer a bit and then keep the extra overlapping parts that overlap with other volumes.
//This generates some overlap in dual extrusion, for better bonding in touching parts.
void generateMultipleVolumesOverlap(std::vector<SliceMeshStorage> &meshes, int overlap);
}//namespace cura
-21
Ver Arquivo
@@ -1,21 +0,0 @@
/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "multithreadOpenMP.h"
#include <cstdlib>
namespace cura
{
bool abort_execution = false;
#ifdef _OPENMP
void handleMultithreadAbort()
{
if (checkMultithreadAbort())
{
std::exit(17);
}
}
#endif
}//namespace cura

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