1.4.5 features (#319)
* Changes: Improve precise wall Port PS2.6 overhang slowdown feature Implement overhang fan for new overhang slowdown algo Add option to switch between classic/new overhang slowdown implementation Set Arachne as default engine Small adjustment of temp calibration range turn off small perimeter by default Small UI tweaks Change default top_surface_pattern to monotonic Fine tune jerk Signed-off-by: SoftFever <softfeverever@gmail.com> * Disable optimizations for RelWithDebInfo Signed-off-by: SoftFever <softfeverever@gmail.com> * fix an issue that max volumetirc/vfa calibration can't send to print Signed-off-by: SoftFever <softfeverever@gmail.com> #322 * fix build errors Signed-off-by: SoftFever <softfeverever@gmail.com> --------- Signed-off-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
0e0b8d297e
commit
e9613e971d
48 changed files with 3182 additions and 434 deletions
166
.clang-format
Normal file
166
.clang-format
Normal file
|
@ -0,0 +1,166 @@
|
|||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
12
.github/workflows/build_win.yml
vendored
12
.github/workflows/build_win.yml
vendored
|
@ -46,13 +46,13 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}
|
||||
run: .\build_release.bat studio
|
||||
|
||||
- name: pack app
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
shell: cmd
|
||||
run: '"C:/Program Files/7-Zip/7z.exe" a -tzip BambuStudio-SoftFever_dev_build.zip ${{ github.workspace }}/build/BambuStudio-SoftFever'
|
||||
# - name: pack app
|
||||
# working-directory: ${{ github.workspace }}/build
|
||||
# shell: cmd
|
||||
# run: '"C:/Program Files/7-Zip/7z.exe" a -tzip BambuStudio-SoftFever_dev_build.zip ${{ github.workspace }}/build/BambuStudio-SoftFever'
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: BambuStudio-SoftFever_Win64
|
||||
path: ${{ github.workspace }}/build/BambuStudio-SoftFever_dev_build.zip
|
||||
name: BambuStudio-SoftFever_Win64_nightly
|
||||
path: ${{ github.workspace }}/build/BambuStudio-SoftFever
|
|
@ -313,20 +313,30 @@ if (SLIC3R_PROFILE)
|
|||
add_definitions(-DSLIC3R_PROFILE)
|
||||
endif ()
|
||||
|
||||
# Disable optimization even with debugging on.
|
||||
if (0)
|
||||
message(STATUS "Perl compiled without optimization. Disabling optimization for the BambuStudio build.")
|
||||
message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
# Disable optimization for RelWithDebInfo
|
||||
if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES "/O2")
|
||||
string(REGEX REPLACE "/O2" "/Od" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "/O2")
|
||||
string(REGEX REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES "/Ob1")
|
||||
string(REGEX REPLACE "/Ob1" "/Ob0" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
if(CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "/Ob1")
|
||||
string(REGEX REPLACE "/Ob1" "/Ob0" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /RTC1")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /RTC1")
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES CMAKE_C_FLAGS_RELWITHDEBINFO)
|
||||
list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
|
||||
# Find and configure boost
|
||||
if(SLIC3R_STATIC)
|
||||
# Use static boost libraries.
|
||||
|
|
|
@ -3,12 +3,21 @@ cd deps
|
|||
mkdir build
|
||||
cd build
|
||||
set DEPS=%CD%/BambuStudio_dep
|
||||
if "%1"=="studio" (
|
||||
GOTO :studio
|
||||
)
|
||||
echo "building deps.."
|
||||
cmake ../ -G "Visual Studio 16 2019" -DDESTDIR="%CD%/BambuStudio_dep" -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release --target ALL_BUILD -- -m
|
||||
|
||||
if "%1"=="deps" exit /b 0
|
||||
|
||||
:studio
|
||||
echo "building studio..."
|
||||
cd %WP%
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 16 2019" -DBBL_RELEASE_TO_PUBLIC=1 -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./BambuStudio-SoftFever" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWIN10SDK_PATH="C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0"
|
||||
|
||||
cmake .. -G "Visual Studio 16 2019" -DBBL_RELEASE_TO_PUBLIC=1 -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./BambuStudio-SoftFever" -DCMAKE_BUILD_TYPE=Release -DWIN10SDK_PATH="C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0"
|
||||
cmake --build . --config RelWithDebInfo --target ALL_BUILD -- -m
|
||||
@REM cmake --build . --target install --config RelWithDebInfo
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Anker",
|
||||
"version": "01.04.00.01",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "Anker configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Anycubic",
|
||||
"version": "01.04.00.01",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "Anycubic configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Artillery",
|
||||
"version": "01.04.00.01",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "Artillery configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Prusa",
|
||||
"version": "01.04.00.11",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "Prusa configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "RatRig",
|
||||
"version": "01.04.00.03",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "RatRig configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"tree_support_branch_angle": "45",
|
||||
"tree_support_wall_count": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_layers": "3",
|
||||
"top_shell_thickness": "0.8",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Snapmaker",
|
||||
"version": "01.04.00.01",
|
||||
"version": "01.04.04.01",
|
||||
"force_update": "0",
|
||||
"description": "Snapmaker configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Tronxy",
|
||||
"version": "01.04.10.0",
|
||||
"version": "01.04.10.1",
|
||||
"force_update": "0",
|
||||
"description": "Tronxy configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
"tree_support_branch_angle": "45",
|
||||
"tree_support_wall_count": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_layers": "3",
|
||||
"top_shell_thickness": "0.8",
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
"tree_support_branch_angle": "45",
|
||||
"tree_support_wall_count": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_layers": "3",
|
||||
"top_shell_thickness": "0.8",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Voron",
|
||||
"version": "01.04.01.00",
|
||||
"version": "01.04.04.00",
|
||||
"force_update": "0",
|
||||
"description": "Voron configurations",
|
||||
"machine_model_list": [
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
"tree_support_branch_angle": "45",
|
||||
"tree_support_wall_count": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_surface_pattern": "monotonic",
|
||||
"top_surface_line_width": "0.4",
|
||||
"top_shell_layers": "3",
|
||||
"top_shell_thickness": "0.8",
|
||||
|
|
|
@ -607,6 +607,37 @@ namespace detail {
|
|||
return up_sqr_d;
|
||||
}
|
||||
|
||||
template<typename IndexedPrimitivesDistancerType, typename Scalar>
|
||||
static inline void indexed_primitives_within_distance_squared_recurisve(const IndexedPrimitivesDistancerType &distancer,
|
||||
size_t node_idx,
|
||||
Scalar squared_distance_limit,
|
||||
std::vector<size_t> &found_primitives_indices)
|
||||
{
|
||||
const auto &node = distancer.tree.node(node_idx);
|
||||
assert(node.is_valid());
|
||||
if (node.is_leaf()) {
|
||||
Scalar sqr_dist;
|
||||
distancer.closest_point_to_origin(node.idx, sqr_dist);
|
||||
if (sqr_dist < squared_distance_limit) { found_primitives_indices.push_back(node.idx); }
|
||||
} else {
|
||||
size_t left_node_idx = node_idx * 2 + 1;
|
||||
size_t right_node_idx = left_node_idx + 1;
|
||||
const auto &node_left = distancer.tree.node(left_node_idx);
|
||||
const auto &node_right = distancer.tree.node(right_node_idx);
|
||||
assert(node_left.is_valid());
|
||||
assert(node_right.is_valid());
|
||||
|
||||
if (node_left.bbox.squaredExteriorDistance(distancer.origin) < squared_distance_limit) {
|
||||
indexed_primitives_within_distance_squared_recurisve(distancer, left_node_idx, squared_distance_limit,
|
||||
found_primitives_indices);
|
||||
}
|
||||
if (node_right.bbox.squaredExteriorDistance(distancer.origin) < squared_distance_limit) {
|
||||
indexed_primitives_within_distance_squared_recurisve(distancer, right_node_idx, squared_distance_limit,
|
||||
found_primitives_indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Build a balanced AABB Tree over an indexed triangles set, balancing the tree
|
||||
|
@ -793,6 +824,33 @@ inline bool is_any_triangle_in_radius(
|
|||
return hit_point.allFinite();
|
||||
}
|
||||
|
||||
// Returns all triangles within the given radius limit
|
||||
template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType>
|
||||
inline std::vector<size_t> all_triangles_in_radius(
|
||||
// Indexed triangle set - 3D vertices.
|
||||
const std::vector<VertexType> &vertices,
|
||||
// Indexed triangle set - triangular faces, references to vertices.
|
||||
const std::vector<IndexedFaceType> &faces,
|
||||
// AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
|
||||
const TreeType &tree,
|
||||
// Point to which the distances on the indexed triangle set is searched for.
|
||||
const VectorType &point,
|
||||
//Square of maximum distance in which triangles are searched for
|
||||
typename VectorType::Scalar max_distance_squared)
|
||||
{
|
||||
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
|
||||
{ vertices, faces, tree, point };
|
||||
|
||||
if(tree.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<size_t> found_triangles{};
|
||||
detail::indexed_primitives_within_distance_squared_recurisve(distancer, size_t(0), max_distance_squared, found_triangles);
|
||||
return found_triangles;
|
||||
}
|
||||
|
||||
|
||||
// Traverse the tree and return the index of an entity whose bounding box
|
||||
// contains a given point. Returns size_t(-1) when the point is outside.
|
||||
|
|
|
@ -1,112 +1,372 @@
|
|||
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
|
||||
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "libslic3r/AABBTreeIndirect.hpp"
|
||||
#include "libslic3r/Line.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace AABBTreeLines {
|
||||
|
||||
namespace detail {
|
||||
namespace detail {
|
||||
|
||||
template<typename ALineType, typename ATreeType, typename AVectorType>
|
||||
struct IndexedLinesDistancer {
|
||||
using LineType = ALineType;
|
||||
using TreeType = ATreeType;
|
||||
using VectorType = AVectorType;
|
||||
using ScalarType = typename VectorType::Scalar;
|
||||
template <typename ALineType, typename ATreeType, typename AVectorType>
|
||||
struct IndexedLinesDistancer {
|
||||
using LineType = ALineType;
|
||||
using TreeType = ATreeType;
|
||||
using VectorType = AVectorType;
|
||||
using ScalarType = typename VectorType::Scalar;
|
||||
|
||||
const std::vector<LineType> &lines;
|
||||
const TreeType &tree;
|
||||
const std::vector<LineType>& lines;
|
||||
const TreeType& tree;
|
||||
|
||||
const VectorType origin;
|
||||
const VectorType origin;
|
||||
|
||||
inline VectorType closest_point_to_origin(size_t primitive_index,
|
||||
ScalarType &squared_distance) {
|
||||
VectorType nearest_point;
|
||||
const LineType &line = lines[primitive_index];
|
||||
squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point);
|
||||
return nearest_point;
|
||||
}
|
||||
};
|
||||
inline VectorType closest_point_to_origin(size_t primitive_index, ScalarType& squared_distance) const
|
||||
{
|
||||
Vec<LineType::Dim, typename LineType::Scalar> nearest_point;
|
||||
const LineType& line = lines[primitive_index];
|
||||
squared_distance = line_alg::distance_to_squared(line, origin.template cast<typename LineType::Scalar>(), &nearest_point);
|
||||
return nearest_point.template cast<ScalarType>();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Build a balanced AABB Tree over a vector of float lines, balancing the tree
|
||||
// on centroids of the lines.
|
||||
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
||||
// during tree traversal.
|
||||
template<typename LineType>
|
||||
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
|
||||
const std::vector<LineType> &lines,
|
||||
//FIXME do we want to apply an epsilon?
|
||||
const float eps = 0)
|
||||
// returns number of intersections of ray starting in ray_origin and following the specified coordinate line with lines in tree
|
||||
// first number is hits in positive direction of ray, second number hits in negative direction. returns neagtive numbers when ray_origin is
|
||||
// on some line exactly.
|
||||
template <typename LineType, typename TreeType, typename VectorType, int coordinate>
|
||||
inline std::tuple<int, int> coordinate_aligned_ray_hit_count(size_t node_idx,
|
||||
const TreeType& tree,
|
||||
const std::vector<LineType>& lines,
|
||||
const VectorType& ray_origin)
|
||||
{
|
||||
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
||||
// using CoordType = typename TreeType::CoordType;
|
||||
using VectorType = typename TreeType::VectorType;
|
||||
using BoundingBox = typename TreeType::BoundingBox;
|
||||
static constexpr int other_coordinate = (coordinate + 1) % 2;
|
||||
using Scalar = typename LineType::Scalar;
|
||||
using Floating = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, double>::type;
|
||||
const auto& node = tree.node(node_idx);
|
||||
assert(node.is_valid());
|
||||
if (node.is_leaf()) {
|
||||
const LineType& line = lines[node.idx];
|
||||
if (ray_origin[other_coordinate] < std::min(line.a[other_coordinate], line.b[other_coordinate]) || ray_origin[other_coordinate] >= std::max(line.a[other_coordinate], line.b[other_coordinate])) {
|
||||
// the second inequality is nonsharp for a reason
|
||||
// without it, we may count contour border twice when the lines meet exactly at the spot of intersection. this prevents is
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
struct InputType {
|
||||
size_t idx() const {
|
||||
return m_idx;
|
||||
}
|
||||
const BoundingBox& bbox() const {
|
||||
return m_bbox;
|
||||
}
|
||||
const VectorType& centroid() const {
|
||||
return m_centroid;
|
||||
Scalar line_max = std::max(line.a[coordinate], line.b[coordinate]);
|
||||
Scalar line_min = std::min(line.a[coordinate], line.b[coordinate]);
|
||||
if (ray_origin[coordinate] > line_max) {
|
||||
return { 1, 0 };
|
||||
} else if (ray_origin[coordinate] < line_min) {
|
||||
return { 0, 1 };
|
||||
} else {
|
||||
// find intersection of ray with line
|
||||
// that is when ( line.a + t * (line.b - line.a) )[other_coordinate] == ray_origin[other_coordinate]
|
||||
// t = ray_origin[oc] - line.a[oc] / (line.b[oc] - line.a[oc]);
|
||||
// then we want to get value of intersection[ coordinate]
|
||||
// val_c = line.a[c] + t * (line.b[c] - line.a[c]);
|
||||
// Note that ray and line may overlap, when (line.b[oc] - line.a[oc]) is zero
|
||||
// In that case, we return negative number
|
||||
Floating distance_oc = line.b[other_coordinate] - line.a[other_coordinate];
|
||||
Floating t = (ray_origin[other_coordinate] - line.a[other_coordinate]) / distance_oc;
|
||||
Floating val_c = line.a[coordinate] + t * (line.b[coordinate] - line.a[coordinate]);
|
||||
if (ray_origin[coordinate] > val_c) {
|
||||
return { 1, 0 };
|
||||
} else if (ray_origin[coordinate] < val_c) {
|
||||
return { 0, 1 };
|
||||
} else { // ray origin is on boundary
|
||||
return { -1, -1 };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int intersections_above = 0;
|
||||
int intersections_below = 0;
|
||||
size_t left_node_idx = node_idx * 2 + 1;
|
||||
size_t right_node_idx = left_node_idx + 1;
|
||||
const auto& node_left = tree.node(left_node_idx);
|
||||
const auto& node_right = tree.node(right_node_idx);
|
||||
assert(node_left.is_valid());
|
||||
assert(node_right.is_valid());
|
||||
|
||||
if (node_left.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] && node_left.bbox.max()[other_coordinate] >= ray_origin[other_coordinate]) {
|
||||
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(left_node_idx, tree, lines,
|
||||
ray_origin);
|
||||
if (above < 0 || below < 0)
|
||||
return { -1, -1 };
|
||||
intersections_above += above;
|
||||
intersections_below += below;
|
||||
}
|
||||
|
||||
if (node_right.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] && node_right.bbox.max()[other_coordinate] >= ray_origin[other_coordinate]) {
|
||||
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(right_node_idx, tree, lines,
|
||||
ray_origin);
|
||||
if (above < 0 || below < 0)
|
||||
return { -1, -1 };
|
||||
intersections_above += above;
|
||||
intersections_below += below;
|
||||
}
|
||||
return { intersections_above, intersections_below };
|
||||
}
|
||||
}
|
||||
|
||||
size_t m_idx;
|
||||
BoundingBox m_bbox;
|
||||
VectorType m_centroid;
|
||||
template <typename LineType, typename TreeType, typename VectorType>
|
||||
inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(size_t node_idx,
|
||||
const TreeType& tree,
|
||||
const std::vector<LineType>& lines,
|
||||
const LineType& line,
|
||||
const typename TreeType::BoundingBox& line_bb)
|
||||
{
|
||||
const auto& node = tree.node(node_idx);
|
||||
assert(node.is_valid());
|
||||
if (node.is_leaf()) {
|
||||
VectorType intersection_pt;
|
||||
if (line_alg::intersection(line, lines[node.idx], &intersection_pt)) {
|
||||
return { std::pair<VectorType, size_t>(intersection_pt, node.idx) };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
size_t left_node_idx = node_idx * 2 + 1;
|
||||
size_t right_node_idx = left_node_idx + 1;
|
||||
const auto& node_left = tree.node(left_node_idx);
|
||||
const auto& node_right = tree.node(right_node_idx);
|
||||
assert(node_left.is_valid());
|
||||
assert(node_right.is_valid());
|
||||
|
||||
std::vector<std::pair<VectorType, size_t>> result;
|
||||
|
||||
if (node_left.bbox.intersects(line_bb)) {
|
||||
std::vector<std::pair<VectorType, size_t>> intersections = get_intersections_with_line<LineType, TreeType, VectorType>(left_node_idx, tree, lines, line, line_bb);
|
||||
result.insert(result.end(), intersections.begin(), intersections.end());
|
||||
}
|
||||
|
||||
if (node_right.bbox.intersects(line_bb)) {
|
||||
std::vector<std::pair<VectorType, size_t>> intersections = get_intersections_with_line<LineType, TreeType, VectorType>(right_node_idx, tree, lines, line, line_bb);
|
||||
result.insert(result.end(), intersections.begin(), intersections.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Build a balanced AABB Tree over a vector of lines, balancing the tree
|
||||
// on centroids of the lines.
|
||||
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
||||
// during tree traversal.
|
||||
template <typename LineType>
|
||||
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType>& lines)
|
||||
{
|
||||
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
||||
// using CoordType = typename TreeType::CoordType;
|
||||
using VectorType = typename TreeType::VectorType;
|
||||
using BoundingBox = typename TreeType::BoundingBox;
|
||||
|
||||
struct InputType {
|
||||
size_t idx() const { return m_idx; }
|
||||
const BoundingBox& bbox() const { return m_bbox; }
|
||||
const VectorType& centroid() const { return m_centroid; }
|
||||
|
||||
size_t m_idx;
|
||||
BoundingBox m_bbox;
|
||||
VectorType m_centroid;
|
||||
};
|
||||
|
||||
std::vector<InputType> input;
|
||||
input.reserve(lines.size());
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
const LineType& line = lines[i];
|
||||
InputType n;
|
||||
n.m_idx = i;
|
||||
n.m_centroid = (line.a + line.b) * 0.5;
|
||||
n.m_bbox = BoundingBox(line.a, line.a);
|
||||
n.m_bbox.extend(line.b);
|
||||
input.emplace_back(n);
|
||||
}
|
||||
|
||||
TreeType out;
|
||||
out.build(std::move(input));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Finding a closest line, its closest point and squared distance to the closest point
|
||||
// Returns squared distance to the closest point or -1 if the input is empty.
|
||||
// or no closer point than max_sq_dist
|
||||
template <typename LineType, typename TreeType, typename VectorType>
|
||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
|
||||
const std::vector<LineType>& lines,
|
||||
const TreeType& tree,
|
||||
const VectorType& point,
|
||||
size_t& hit_idx_out,
|
||||
Eigen::PlainObjectBase<VectorType>& hit_point_out,
|
||||
typename VectorType::Scalar max_sqr_dist = std::numeric_limits<typename VectorType::Scalar>::infinity())
|
||||
{
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
if (tree.empty())
|
||||
return Scalar(-1);
|
||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType> { lines, tree, point };
|
||||
return AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_sqr_dist,
|
||||
hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
// Returns all lines within the given radius limit
|
||||
template <typename LineType, typename TreeType, typename VectorType>
|
||||
inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType>& lines,
|
||||
const TreeType& tree,
|
||||
const VectorType& point,
|
||||
typename VectorType::Scalar max_distance_squared)
|
||||
{
|
||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType> { lines, tree, point };
|
||||
|
||||
if (tree.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<size_t> found_lines {};
|
||||
AABBTreeIndirect::detail::indexed_primitives_within_distance_squared_recurisve(distancer, size_t(0), max_distance_squared, found_lines);
|
||||
return found_lines;
|
||||
}
|
||||
|
||||
// return 1 if true, -1 if false, 0 for point on contour (or if cannot be determined)
|
||||
template <typename LineType, typename TreeType, typename VectorType>
|
||||
inline int point_outside_closed_contours(const std::vector<LineType>& lines, const TreeType& tree, const VectorType& point)
|
||||
{
|
||||
if (tree.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 0>(0, tree, lines, point);
|
||||
if (hits_above < 0 || hits_below < 0) {
|
||||
return 0;
|
||||
} else if (hits_above % 2 == 1 && hits_below % 2 == 1) {
|
||||
return -1;
|
||||
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
|
||||
return 1;
|
||||
} else { // this should not happen with closed contours. lets check it in Y direction
|
||||
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 1>(0, tree, lines, point);
|
||||
if (hits_above < 0 || hits_below < 0) {
|
||||
return 0;
|
||||
} else if (hits_above % 2 == 1 && hits_below % 2 == 1) {
|
||||
return -1;
|
||||
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
|
||||
return 1;
|
||||
} else { // both results were unclear
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool sorted, typename VectorType, typename LineType, typename TreeType>
|
||||
inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(const std::vector<LineType>& lines,
|
||||
const TreeType& tree,
|
||||
const LineType& line)
|
||||
{
|
||||
if (tree.empty()) {
|
||||
return {};
|
||||
}
|
||||
auto line_bb = typename TreeType::BoundingBox(line.a, line.a);
|
||||
line_bb.extend(line.b);
|
||||
|
||||
auto intersections = detail::get_intersections_with_line<LineType, TreeType, VectorType>(0, tree, lines, line, line_bb);
|
||||
if (sorted) {
|
||||
using Floating =
|
||||
typename std::conditional<std::is_floating_point<typename LineType::Scalar>::value, typename LineType::Scalar, double>::type;
|
||||
|
||||
std::vector<std::pair<Floating, std::pair<VectorType, size_t>>> points_with_sq_distance {};
|
||||
for (const auto& p : intersections) {
|
||||
points_with_sq_distance.emplace_back((p.first - line.a).template cast<Floating>().squaredNorm(), p);
|
||||
}
|
||||
std::sort(points_with_sq_distance.begin(), points_with_sq_distance.end(),
|
||||
[](const std::pair<Floating, std::pair<VectorType, size_t>>& left,
|
||||
std::pair<Floating, std::pair<VectorType, size_t>>& right) { return left.first < right.first; });
|
||||
for (size_t i = 0; i < points_with_sq_distance.size(); i++) {
|
||||
intersections[i] = points_with_sq_distance[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
template <typename LineType>
|
||||
class LinesDistancer {
|
||||
public:
|
||||
using Scalar = typename LineType::Scalar;
|
||||
using Floating = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, double>::type;
|
||||
|
||||
private:
|
||||
std::vector<LineType> lines;
|
||||
AABBTreeIndirect::Tree<2, Scalar> tree;
|
||||
|
||||
public:
|
||||
explicit LinesDistancer(const std::vector<LineType>& lines)
|
||||
: lines(lines)
|
||||
{
|
||||
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines);
|
||||
}
|
||||
|
||||
explicit LinesDistancer(std::vector<LineType>&& lines)
|
||||
: lines(lines)
|
||||
{
|
||||
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines);
|
||||
}
|
||||
|
||||
LinesDistancer() = default;
|
||||
|
||||
// 1 true, -1 false, 0 cannot determine
|
||||
int outside(const Vec<2, Scalar>& point) const { return point_outside_closed_contours(lines, tree, point); }
|
||||
|
||||
// negative sign means inside
|
||||
template <bool SIGNED_DISTANCE>
|
||||
std::tuple<Floating, size_t, Vec<2, Floating>> distance_from_lines_extra(const Vec<2, Scalar>& point) const
|
||||
{
|
||||
size_t nearest_line_index_out = size_t(-1);
|
||||
Vec<2, Floating> nearest_point_out = Vec<2, Floating>::Zero();
|
||||
Vec<2, Floating> p = point.template cast<Floating>();
|
||||
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, nearest_point_out);
|
||||
|
||||
if (distance < 0) {
|
||||
return { std::numeric_limits<Floating>::infinity(), nearest_line_index_out, nearest_point_out };
|
||||
}
|
||||
distance = sqrt(distance);
|
||||
|
||||
if (SIGNED_DISTANCE) {
|
||||
distance *= outside(point);
|
||||
}
|
||||
|
||||
return { distance, nearest_line_index_out, nearest_point_out };
|
||||
}
|
||||
|
||||
template <bool SIGNED_DISTANCE>
|
||||
Floating distance_from_lines(const Vec<2, typename LineType::Scalar>& point) const
|
||||
{
|
||||
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, typename LineType::Scalar>& point, Floating radius)
|
||||
{
|
||||
return all_lines_in_radius(this->lines, this->tree, point, radius * radius);
|
||||
}
|
||||
|
||||
template <bool sorted>
|
||||
std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType& line) const
|
||||
{
|
||||
return get_intersections_with_line<sorted, Vec<2, Scalar>>(lines, tree, line);
|
||||
}
|
||||
|
||||
const LineType& get_line(size_t line_idx) const { return lines[line_idx]; }
|
||||
|
||||
const std::vector<LineType>& get_lines() const { return lines; }
|
||||
};
|
||||
|
||||
std::vector<InputType> input;
|
||||
input.reserve(lines.size());
|
||||
const VectorType veps(eps, eps);
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
const LineType &line = lines[i];
|
||||
InputType n;
|
||||
n.m_idx = i;
|
||||
n.m_centroid = (line.a + line.b) * 0.5;
|
||||
n.m_bbox = BoundingBox(line.a, line.a);
|
||||
n.m_bbox.extend(line.b);
|
||||
n.m_bbox.min() -= veps;
|
||||
n.m_bbox.max() += veps;
|
||||
input.emplace_back(n);
|
||||
}
|
||||
|
||||
TreeType out;
|
||||
out.build(std::move(input));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Finding a closest line, its closest point and squared distance to the closest point
|
||||
// Returns squared distance to the closest point or -1 if the input is empty.
|
||||
template<typename LineType, typename TreeType, typename VectorType>
|
||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
|
||||
const std::vector<LineType> &lines,
|
||||
const TreeType &tree,
|
||||
const VectorType &point,
|
||||
size_t &hit_idx_out,
|
||||
Eigen::PlainObjectBase<VectorType> &hit_point_out)
|
||||
{
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>
|
||||
{ lines, tree, point };
|
||||
return tree.empty() ?
|
||||
Scalar(-1) :
|
||||
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
|
||||
std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r::AABBTreeLines
|
||||
|
||||
#endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */
|
||||
|
|
|
@ -134,6 +134,7 @@ set(lisbslic3r_sources
|
|||
GCode/GCodeProcessor.hpp
|
||||
GCode/AvoidCrossingPerimeters.cpp
|
||||
GCode/AvoidCrossingPerimeters.hpp
|
||||
GCode/ExtrusionProcessor.hpp
|
||||
GCode.cpp
|
||||
GCode.hpp
|
||||
GCodeReader.cpp
|
||||
|
@ -246,6 +247,10 @@ set(lisbslic3r_sources
|
|||
SlicingAdaptive.hpp
|
||||
SupportMaterial.cpp
|
||||
SupportMaterial.hpp
|
||||
PrincipalComponents2D.cpp
|
||||
PrincipalComponents2D.hpp
|
||||
SupportSpotsGenerator.cpp
|
||||
SupportSpotsGenerator.hpp
|
||||
TreeSupport.hpp
|
||||
TreeSupport.cpp
|
||||
MinimumSpanningTree.hpp
|
||||
|
|
|
@ -84,6 +84,25 @@ public:
|
|||
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
|
||||
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
|
||||
|
||||
inline size_t count_points(const ExPolygons& expolys)
|
||||
{
|
||||
size_t n_points = 0;
|
||||
for (const auto& expoly : expolys) {
|
||||
n_points += expoly.contour.points.size();
|
||||
for (const auto& hole : expoly.holes)
|
||||
n_points += hole.points.size();
|
||||
}
|
||||
return n_points;
|
||||
}
|
||||
|
||||
inline size_t count_points(const ExPolygon& expoly)
|
||||
{
|
||||
size_t n_points = expoly.contour.points.size();
|
||||
for (const auto& hole : expoly.holes)
|
||||
n_points += hole.points.size();
|
||||
return n_points;
|
||||
}
|
||||
|
||||
// Count a nuber of polygons stored inside the vector of expolygons.
|
||||
// Useful for allocating space for polygons when converting expolygons to polygons.
|
||||
inline size_t number_polygons(const ExPolygons &expolys)
|
||||
|
@ -130,6 +149,72 @@ inline Lines to_lines(const ExPolygons &src)
|
|||
}
|
||||
return lines;
|
||||
}
|
||||
// Line is from point index(see to_points) to next point.
|
||||
// Next point of last point in polygon is first polygon point.
|
||||
inline Linesf to_linesf(const ExPolygons& src, uint32_t count_lines = 0)
|
||||
{
|
||||
assert(count_lines == 0 || count_lines == count_points(src));
|
||||
if (count_lines == 0)
|
||||
count_lines = count_points(src);
|
||||
Linesf lines;
|
||||
lines.reserve(count_lines);
|
||||
Vec2d prev_pd;
|
||||
auto to_lines = [&lines, &prev_pd](const Points& pts) {
|
||||
assert(pts.size() >= 3);
|
||||
if (pts.size() < 2)
|
||||
return;
|
||||
bool is_first = true;
|
||||
for (const Point& p : pts) {
|
||||
Vec2d pd = p.cast<double>();
|
||||
if (is_first)
|
||||
is_first = false;
|
||||
else
|
||||
lines.emplace_back(prev_pd, pd);
|
||||
prev_pd = pd;
|
||||
}
|
||||
lines.emplace_back(prev_pd, pts.front().cast<double>());
|
||||
};
|
||||
for (const ExPolygon& expoly : src) {
|
||||
to_lines(expoly.contour.points);
|
||||
for (const Polygon& hole : expoly.holes)
|
||||
to_lines(hole.points);
|
||||
}
|
||||
assert(lines.size() == count_lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
inline Linesf to_unscaled_linesf(const ExPolygons& src)
|
||||
{
|
||||
Linesf lines;
|
||||
lines.reserve(count_points(src));
|
||||
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++it_expoly) {
|
||||
for (size_t i = 0; i <= it_expoly->holes.size(); ++i) {
|
||||
const Points& points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
|
||||
Vec2d unscaled_a = unscaled(points.front());
|
||||
Vec2d unscaled_b = unscaled_a;
|
||||
for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) {
|
||||
unscaled_b = unscaled(*(it));
|
||||
lines.push_back(Linef(unscaled_a, unscaled_b));
|
||||
unscaled_a = unscaled_b;
|
||||
}
|
||||
lines.push_back(Linef(unscaled_a, unscaled(points.front())));
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
inline Points to_points(const ExPolygons& src)
|
||||
{
|
||||
Points points;
|
||||
size_t count = count_points(src);
|
||||
points.reserve(count);
|
||||
for (const ExPolygon& expolygon : src) {
|
||||
append(points, expolygon.contour.points);
|
||||
for (const Polygon& hole : expolygon.holes)
|
||||
append(points, hole.points);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
inline Polylines to_polylines(const ExPolygon &src)
|
||||
{
|
||||
|
|
|
@ -51,8 +51,7 @@ enum ExtrusionLoopRole {
|
|||
inline bool is_perimeter(ExtrusionRole role)
|
||||
{
|
||||
return role == erPerimeter
|
||||
|| role == erExternalPerimeter
|
||||
|| role == erOverhangPerimeter;
|
||||
|| role == erExternalPerimeter;
|
||||
}
|
||||
|
||||
inline bool is_internal_perimeter(ExtrusionRole role)
|
||||
|
@ -217,12 +216,12 @@ public:
|
|||
double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
|
||||
|
||||
void set_overhang_degree(int overhang) {
|
||||
if (is_perimeter(m_role))
|
||||
if (is_perimeter(m_role) || is_bridge(m_role))
|
||||
overhang_degree = (overhang < 0)?0:(overhang > 10 ? 10 : overhang);
|
||||
};
|
||||
int get_overhang_degree() const {
|
||||
// only perimeter has overhang degree. Other return 0;
|
||||
if (is_perimeter(m_role))
|
||||
if (is_perimeter(m_role) || is_bridge(m_role))
|
||||
return overhang_degree;
|
||||
return 0;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "GCode/ExtrusionProcessor.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
|
@ -1661,7 +1662,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
|
||||
if (m_config.default_jerk.value > 0) {
|
||||
double jerk = m_config.outer_wall_jerk.value;
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
}
|
||||
|
||||
calib_pressure_advance pa_test(this);
|
||||
|
@ -2461,6 +2462,7 @@ namespace Skirt {
|
|||
#if 0
|
||||
// Prime just the first printing extruder. This is original Slic3r's implementation.
|
||||
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirt_loops.value);
|
||||
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirt_loops.value);
|
||||
#else
|
||||
// Prime all extruders planned for this layer, see
|
||||
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
|
||||
|
@ -2616,14 +2618,10 @@ GCode::LayerResult GCode::process_layer(
|
|||
gcode += writer().set_temperature(print.calib_params().start - offset);
|
||||
} else if (print.calib_mode() == CalibMode::Calib_VFA_Tower) {
|
||||
auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step;
|
||||
DynamicConfig config;
|
||||
config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||
const_cast<Print*>(&print)->print_regions_mutable()[0]->config_apply_only(config, { "outer_wall_speed" });
|
||||
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||
} else if (print.calib_mode() == CalibMode::Calib_Vol_speed_Tower) {
|
||||
auto _speed = print.calib_params().start + print_z * print.calib_params().step;
|
||||
DynamicConfig config;
|
||||
config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||
const_cast<Print*>(&print)->print_regions_mutable()[0]->config_apply_only(config, { "outer_wall_speed" });
|
||||
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||
}
|
||||
|
||||
//BBS
|
||||
|
@ -2636,7 +2634,7 @@ GCode::LayerResult GCode::process_layer(
|
|||
|
||||
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
|
||||
double jerk = m_config.initial_layer_jerk.value;
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2665,7 +2663,7 @@ GCode::LayerResult GCode::process_layer(
|
|||
|
||||
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
|
||||
double jerk = m_config.default_jerk.value;
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
}
|
||||
|
||||
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
|
||||
|
@ -2699,6 +2697,11 @@ GCode::LayerResult GCode::process_layer(
|
|||
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
|
||||
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
|
||||
|
||||
for (const auto& layer_to_print : layers) {
|
||||
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.object_layer);
|
||||
}
|
||||
|
||||
|
||||
// Group extrusions by an extruder, then by an object, an island and a region.
|
||||
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
|
||||
bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
|
||||
|
@ -3084,6 +3087,8 @@ GCode::LayerResult GCode::process_layer(
|
|||
if (!m_config.use_relative_e_distances)
|
||||
gcode += m_writer.reset_e(true);
|
||||
}
|
||||
m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object);
|
||||
|
||||
// When starting a new object, use the external motion planner for the first travel move.
|
||||
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
||||
|
@ -3450,7 +3455,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
if (m_config.default_acceleration.value > 0)
|
||||
gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5));
|
||||
if (m_config.default_jerk.value > 0)
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)(m_config.default_jerk.value + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(m_config.default_jerk.value);
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
@ -3480,7 +3485,7 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
|
|||
// reset acceleration
|
||||
gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5));
|
||||
if(m_config.default_jerk.value > 0)
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(m_config.default_jerk.value + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(m_config.default_jerk.value);
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
@ -3511,7 +3516,7 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
|
|||
// reset acceleration
|
||||
gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5));
|
||||
if(m_config.default_jerk.value > 0)
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(m_config.default_jerk.value + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(m_config.default_jerk.value);
|
||||
|
||||
}
|
||||
return gcode;
|
||||
|
@ -3698,7 +3703,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
|
||||
// compensate retraction
|
||||
gcode += this->unretract();
|
||||
|
||||
m_config.apply(m_calib_config);
|
||||
// adjust acceleration
|
||||
if (m_config.default_acceleration.value > 0) {
|
||||
double acceleration;
|
||||
|
@ -3739,7 +3744,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
else {
|
||||
jerk = m_config.default_jerk.value;
|
||||
}
|
||||
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
}
|
||||
|
||||
// calculate extrusion length per distance unit
|
||||
|
@ -3752,13 +3757,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
int overhang_degree = path.get_overhang_degree();
|
||||
if (path.role() == erPerimeter) {
|
||||
speed = m_config.get_abs_value("inner_wall_speed");
|
||||
if (m_config.enable_overhang_speed.value && overhang_degree > 0 && overhang_degree <= 5) {
|
||||
if (m_config.overhang_speed_classic.value && m_config.enable_overhang_speed.value && overhang_degree > 0 &&
|
||||
overhang_degree <= 5) {
|
||||
double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str());
|
||||
speed = new_speed == 0.0 ? speed : new_speed;
|
||||
}
|
||||
} else if (path.role() == erExternalPerimeter) {
|
||||
speed = m_config.get_abs_value("outer_wall_speed");
|
||||
if (m_config.enable_overhang_speed.value && overhang_degree > 0 && overhang_degree <= 5) {
|
||||
if (m_config.overhang_speed_classic.value && m_config.enable_overhang_speed.value &&
|
||||
overhang_degree > 0 && overhang_degree <= 5) {
|
||||
double new_speed = m_config.get_abs_value(overhang_speed_key_map[overhang_degree].c_str());
|
||||
speed = new_speed == 0.0 ? speed : new_speed;
|
||||
}
|
||||
|
@ -3812,6 +3819,35 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
bool variable_speed = false;
|
||||
std::vector<ProcessedPoint> new_points {};
|
||||
|
||||
if (m_config.enable_overhang_speed && !m_config.overhang_speed_classic && !this->on_first_layer() &&
|
||||
(is_bridge(path.role()) || is_perimeter(path.role()))) {
|
||||
ConfigOptionPercents overhang_overlap_levels({75, 50, 25, 5});
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{(m_config.get_abs_value("overhang_1_4_speed") < 0.5)
|
||||
? FloatOrPercent{100, true}
|
||||
: FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed"), false},
|
||||
(m_config.get_abs_value("overhang_2_4_speed") < 0.5)
|
||||
? FloatOrPercent{100, true}
|
||||
: FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed"), false},
|
||||
(m_config.get_abs_value("overhang_3_4_speed") < 0.5)
|
||||
? FloatOrPercent{100, true}
|
||||
: FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed"), false},
|
||||
(m_config.get_abs_value("overhang_4_4_speed") < 0.5)
|
||||
? FloatOrPercent{100, true}
|
||||
: FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed"), false}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(
|
||||
path, overhang_overlap_levels, dynamic_overhang_speeds, m_config.get_abs_value("outer_wall_speed"), speed);
|
||||
|
||||
variable_speed = std::any_of(new_points.begin(), new_points.end(),
|
||||
[speed](const ProcessedPoint &p) { return p.speed != speed; });
|
||||
}
|
||||
|
||||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
// extrude arc or line
|
||||
|
@ -3862,108 +3898,150 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
gcode += buf;
|
||||
}
|
||||
|
||||
auto overhang_fan_threshold = EXTRUDER_CONFIG(overhang_fan_threshold);
|
||||
auto enable_overhang_bridge_fan = EXTRUDER_CONFIG(enable_overhang_bridge_fan);
|
||||
|
||||
|
||||
// { "0%", Overhang_threshold_none },
|
||||
// { "5%", Overhang_threshold_1_4 },
|
||||
// { "25%", Overhang_threshold_2_4 },
|
||||
// { "50%", Overhang_threshold_3_4 },
|
||||
// { "75%", Overhang_threshold_4_4 },
|
||||
// { "95%", Overhang_threshold_bridge }
|
||||
auto check_overhang_fan = [&overhang_fan_threshold](float overlap) {
|
||||
switch (overhang_fan_threshold) {
|
||||
case (int)Overhang_threshold_1_4:
|
||||
return overlap <= 0.95f;
|
||||
break;
|
||||
case (int)Overhang_threshold_2_4:
|
||||
return overlap <= 0.75f;
|
||||
break;
|
||||
case (int)Overhang_threshold_3_4:
|
||||
return overlap <= 0.5f;
|
||||
break;
|
||||
case (int)Overhang_threshold_4_4:
|
||||
return overlap <= 0.25f;
|
||||
break;
|
||||
case (int)Overhang_threshold_bridge:
|
||||
return overlap <= 0.05f;
|
||||
break;
|
||||
case (int)Overhang_threshold_none:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::string comment;
|
||||
if (m_enable_cooling_markers) {
|
||||
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
||||
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
||||
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
||||
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
||||
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) {
|
||||
gcode += ";_OVERHANG_FAN_START\n";
|
||||
comment = ";_EXTRUDE_SET_SPEED";
|
||||
} else if (path.get_overhang_degree() > overhang_threshold ||
|
||||
is_bridge(path.role()))
|
||||
gcode += ";_OVERHANG_FAN_START\n";
|
||||
else
|
||||
comment = ";_EXTRUDE_SET_SPEED";
|
||||
}
|
||||
else {
|
||||
comment = ";_EXTRUDE_SET_SPEED";
|
||||
}
|
||||
|
||||
comment = ";_EXTRUDE_SET_SPEED";
|
||||
if (path.role() == erExternalPerimeter)
|
||||
comment += ";_EXTERNAL_PERIMETER";
|
||||
}
|
||||
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
double path_length = 0.;
|
||||
{
|
||||
std::string comment = GCodeWriter::full_gcode_comment ? description : "";
|
||||
//BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode
|
||||
//Attention: G2 and G3 is not supported in spiral_mode mode
|
||||
if (!m_config.enable_arc_fitting ||
|
||||
path.polyline.fitting_result.empty() ||
|
||||
m_config.spiral_mode) {
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
comment, path.is_force_no_extrusion());
|
||||
}
|
||||
} else {
|
||||
// BBS: start to generate gcode from arc fitting data which includes line and arc
|
||||
const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result;
|
||||
for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) {
|
||||
switch (fitting_result[fitting_index].path_type) {
|
||||
case EMovePathType::Linear_move: {
|
||||
size_t start_index = fitting_result[fitting_index].start_point_index;
|
||||
size_t end_index = fitting_result[fitting_index].end_point_index;
|
||||
for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
|
||||
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
comment, path.is_force_no_extrusion());
|
||||
}
|
||||
break;
|
||||
bool is_overhang_fan_on = false;
|
||||
if (!variable_speed) {
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
double path_length = 0.;
|
||||
{
|
||||
if (m_enable_cooling_markers && enable_overhang_bridge_fan && m_config.overhang_speed_classic) {
|
||||
// BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external
|
||||
// perimeter
|
||||
int overhang_threshold = overhang_fan_threshold == Overhang_threshold_none ? Overhang_threshold_none
|
||||
: overhang_fan_threshold - 1;
|
||||
if ((overhang_fan_threshold == Overhang_threshold_none && is_perimeter(path.role())) ||
|
||||
(path.get_overhang_degree() > overhang_threshold || is_bridge(path.role()))) {
|
||||
gcode += ";_OVERHANG_FAN_START\n";
|
||||
is_overhang_fan_on = true;
|
||||
}
|
||||
case EMovePathType::Arc_move_cw:
|
||||
case EMovePathType::Arc_move_ccw: {
|
||||
const ArcSegment& arc = fitting_result[fitting_index].arc_data;
|
||||
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
|
||||
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
|
||||
path_length += arc_length;
|
||||
gcode += m_writer.extrude_arc_to_xy(
|
||||
}
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode
|
||||
// Attention: G2 and G3 is not supported in spiral_mode mode
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode) {
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
|
||||
}
|
||||
} else {
|
||||
// BBS: start to generate gcode from arc fitting data which includes line and arc
|
||||
const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result;
|
||||
for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) {
|
||||
switch (fitting_result[fitting_index].path_type) {
|
||||
case EMovePathType::Linear_move: {
|
||||
size_t start_index = fitting_result[fitting_index].start_point_index;
|
||||
size_t end_index = fitting_result[fitting_index].end_point_index;
|
||||
for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
|
||||
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
e_per_mm * line_length,
|
||||
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EMovePathType::Arc_move_cw:
|
||||
case EMovePathType::Arc_move_ccw: {
|
||||
const ArcSegment& arc = fitting_result[fitting_index].arc_data;
|
||||
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
|
||||
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
|
||||
path_length += arc_length;
|
||||
gcode += m_writer.extrude_arc_to_xy(
|
||||
this->point_to_gcode(arc.end_point),
|
||||
center_offset,
|
||||
e_per_mm * arc_length,
|
||||
arc.direction == ArcDirection::Arc_Dir_CCW,
|
||||
comment, path.is_force_no_extrusion());
|
||||
break;
|
||||
GCodeWriter::full_gcode_comment ? description : "", path.is_force_no_extrusion());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// BBS: should never happen that a empty path_type has been stored
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
//BBS: should never happen that a empty path_type has been stored
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
if (is_overhang_fan_on) {
|
||||
is_overhang_fan_on = false;
|
||||
gcode += ";_OVERHANG_FAN_END\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double last_set_speed = std::max((float)EXTRUDER_CONFIG(slow_down_min_speed), new_points[0].speed) * 60.0;
|
||||
|
||||
gcode += m_writer.set_speed(last_set_speed, "", comment);
|
||||
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
|
||||
for (size_t i = 1; i < new_points.size(); i++) {
|
||||
const ProcessedPoint &processed_point = new_points[i];
|
||||
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
|
||||
const double line_length = (p - prev).norm();
|
||||
if (m_enable_cooling_markers && enable_overhang_bridge_fan) {
|
||||
if (is_bridge(path.role()) || check_overhang_fan(new_points[i - 1].overlap)) {
|
||||
gcode += ";_OVERHANG_FAN_START\n";
|
||||
is_overhang_fan_on = true;
|
||||
}
|
||||
}
|
||||
gcode +=
|
||||
m_writer.extrude_to_xy(p, e_per_mm * line_length, GCodeWriter::full_gcode_comment ? description : "");
|
||||
if (is_overhang_fan_on) {
|
||||
is_overhang_fan_on = false;
|
||||
gcode += ";_OVERHANG_FAN_END\n";
|
||||
}
|
||||
prev = p;
|
||||
double new_speed = std::max((float)EXTRUDER_CONFIG(slow_down_min_speed), processed_point.speed) * 60.0;
|
||||
if (last_set_speed != new_speed) {
|
||||
gcode += m_writer.set_speed(new_speed, "", comment);
|
||||
last_set_speed = new_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_enable_cooling_markers) {
|
||||
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
||||
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
||||
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
||||
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
||||
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) {
|
||||
gcode += ";_EXTRUDE_END\n";
|
||||
gcode += ";_OVERHANG_FAN_END\n";
|
||||
|
||||
} else if (path.get_overhang_degree() > overhang_threshold ||
|
||||
is_bridge(path.role()))
|
||||
gcode += ";_OVERHANG_FAN_END\n";
|
||||
else
|
||||
gcode += ";_EXTRUDE_END\n";
|
||||
}
|
||||
else {
|
||||
gcode += ";_EXTRUDE_END\n";
|
||||
}
|
||||
gcode += ";_EXTRUDE_END\n";
|
||||
}
|
||||
|
||||
this->set_last_pos(path.last_point());
|
||||
return gcode;
|
||||
}
|
||||
|
@ -3986,22 +4064,22 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
|
||||
// SoftFever
|
||||
if (this->on_first_layer()) {
|
||||
if(m_config.default_acceleration.value > 0)
|
||||
{
|
||||
auto jerk = (unsigned int)floor(m_config.initial_layer_jerk.value + 0.5);
|
||||
if (m_config.default_acceleration.value > 0) {
|
||||
auto accel = (unsigned int)floor(m_config.initial_layer_acceleration.value + 0.5);
|
||||
if(jerk > 0)
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
|
||||
if(accel > 0)
|
||||
if (accel > 0)
|
||||
gcode += m_writer.set_acceleration(accel);
|
||||
}
|
||||
if (m_config.default_jerk.value > 0) {
|
||||
auto jerk = m_config.initial_layer_jerk.value;
|
||||
if (jerk > 0)
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_config.default_jerk.value > 0)
|
||||
{
|
||||
auto jerk = (unsigned int)floor(m_config.travel_jerk.value + 0.5);
|
||||
auto jerk = m_config.travel_jerk.value;
|
||||
auto accel = (unsigned int)floor(m_config.travel_acceleration.value + 0.5);
|
||||
if(jerk > 0)
|
||||
gcode += m_writer.set_jerk_xy(jerk);
|
||||
|
@ -4337,6 +4415,13 @@ Point GCode::gcode_to_point(const Vec2d &point) const
|
|||
scale_(point(1) - m_origin(1) + extruder_offset(1)));
|
||||
}
|
||||
|
||||
Vec2d GCode::point_to_gcode_quantized(const Point& point) const
|
||||
{
|
||||
Vec2d p = this->point_to_gcode(point);
|
||||
return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) };
|
||||
}
|
||||
|
||||
|
||||
// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
|
||||
// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
|
||||
// Fills in by_region_per_copy_cache and returns its reference.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "EdgeGrid.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
#include "GCode/ExtrusionProcessor.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
@ -178,6 +179,7 @@ public:
|
|||
const Point& last_pos() const { return m_last_pos; }
|
||||
Vec2d point_to_gcode(const Point &point) const;
|
||||
Point gcode_to_point(const Vec2d &point) const;
|
||||
Vec2d point_to_gcode_quantized(const Point& point) const;
|
||||
const FullPrintConfig &config() const { return m_config; }
|
||||
const Layer* layer() const { return m_layer; }
|
||||
GCodeWriter& writer() { return m_writer; }
|
||||
|
@ -412,11 +414,15 @@ private:
|
|||
// Cache for custom seam enforcers/blockers for each layer.
|
||||
SeamPlacer m_seam_placer;
|
||||
|
||||
ExtrusionQualityEstimator m_extrusion_quality_estimator;
|
||||
|
||||
|
||||
/* Origin of print coordinates expressed in unscaled G-code coordinates.
|
||||
This affects the input arguments supplied to the extrude*() and travel_to()
|
||||
methods. */
|
||||
Vec2d m_origin;
|
||||
FullPrintConfig m_config;
|
||||
DynamicConfig m_calib_config;
|
||||
// scaled G-code resolution
|
||||
double m_scaled_resolution;
|
||||
GCodeWriter m_writer;
|
||||
|
|
329
src/libslic3r/GCode/ExtrusionProcessor.hpp
Normal file
329
src/libslic3r/GCode/ExtrusionProcessor.hpp
Normal file
|
@ -0,0 +1,329 @@
|
|||
#ifndef slic3r_ExtrusionProcessor_hpp_
|
||||
#define slic3r_ExtrusionProcessor_hpp_
|
||||
|
||||
#include "../AABBTreeLines.hpp"
|
||||
//#include "../SupportSpotsGenerator.hpp"
|
||||
#include "../libslic3r.h"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Point.hpp"
|
||||
#include "../SVG.hpp"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Polygon.hpp"
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../Config.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class SlidingWindowCurvatureAccumulator
|
||||
{
|
||||
float window_size;
|
||||
float total_distance = 0; // accumulated distance
|
||||
float total_curvature = 0; // accumulated signed ccw angles
|
||||
deque<float> distances;
|
||||
deque<float> angles;
|
||||
|
||||
public:
|
||||
SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {}
|
||||
|
||||
void add_point(float distance, float angle)
|
||||
{
|
||||
total_distance += distance;
|
||||
total_curvature += angle;
|
||||
distances.push_back(distance);
|
||||
angles.push_back(angle);
|
||||
|
||||
while (distances.size() > 1 && total_distance > window_size) {
|
||||
total_distance -= distances.front();
|
||||
total_curvature -= angles.front();
|
||||
distances.pop_front();
|
||||
angles.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
float get_curvature() const
|
||||
{
|
||||
return total_curvature / window_size;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
total_curvature = 0;
|
||||
total_distance = 0;
|
||||
distances.clear();
|
||||
angles.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class CurvatureEstimator
|
||||
{
|
||||
static const size_t sliders_count = 3;
|
||||
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.0},{4.0}, {10.0}};
|
||||
|
||||
public:
|
||||
void add_point(float distance, float angle)
|
||||
{
|
||||
if (distance < EPSILON)
|
||||
return;
|
||||
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
|
||||
slider.add_point(distance, angle);
|
||||
}
|
||||
}
|
||||
float get_curvature()
|
||||
{
|
||||
float max_curvature = 0.0f;
|
||||
for (const SlidingWindowCurvatureAccumulator &slider : sliders) {
|
||||
if (abs(slider.get_curvature()) > abs(max_curvature)) {
|
||||
max_curvature = slider.get_curvature();
|
||||
}
|
||||
}
|
||||
return max_curvature;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
|
||||
slider.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ExtendedPoint
|
||||
{
|
||||
ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0)
|
||||
: position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature)
|
||||
{}
|
||||
|
||||
Vec2d position;
|
||||
float distance;
|
||||
size_t nearest_prev_layer_line;
|
||||
float curvature;
|
||||
};
|
||||
|
||||
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L>
|
||||
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points,
|
||||
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f)
|
||||
{
|
||||
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
|
||||
if (input_points.empty())
|
||||
return {};
|
||||
float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
|
||||
CurvatureEstimator cestim;
|
||||
auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
|
||||
|
||||
std::vector<ExtendedPoint> points;
|
||||
points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1));
|
||||
|
||||
{
|
||||
ExtendedPoint start_point{maybe_unscale(input_points.front())};
|
||||
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>());
|
||||
start_point.distance = distance + boundary_offset;
|
||||
start_point.nearest_prev_layer_line = nearest_line;
|
||||
points.push_back(start_point);
|
||||
}
|
||||
for (size_t i = 1; i < input_points.size(); i++) {
|
||||
ExtendedPoint next_point{maybe_unscale(input_points[i])};
|
||||
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
|
||||
next_point.distance = distance + boundary_offset;
|
||||
next_point.nearest_prev_layer_line = nearest_line;
|
||||
|
||||
if (ADD_INTERSECTIONS &&
|
||||
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
|
||||
const ExtendedPoint &prev_point = points.back();
|
||||
auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()});
|
||||
for (const auto &intersection : intersections) {
|
||||
points.emplace_back(intersection.first.template cast<double>(), boundary_offset, intersection.second);
|
||||
}
|
||||
}
|
||||
points.push_back(next_point);
|
||||
}
|
||||
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
|
||||
std::vector<ExtendedPoint> new_points;
|
||||
new_points.reserve(points.size()*2);
|
||||
new_points.push_back(points.front());
|
||||
for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) {
|
||||
const ExtendedPoint &curr = points[point_idx];
|
||||
const ExtendedPoint &next = points[point_idx + 1];
|
||||
|
||||
if ((curr.distance > 0 && curr.distance < boundary_offset + 2.0f) ||
|
||||
(next.distance > 0 && next.distance < boundary_offset + 2.0f)) {
|
||||
double line_len = (next.position - curr.position).norm();
|
||||
if (line_len > 4.0f) {
|
||||
double a0 = std::clamp((curr.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
|
||||
double a1 = std::clamp(1.0f - (next.distance + 2 * boundary_offset) / line_len, 0.0, 1.0);
|
||||
double t0 = std::min(a0, a1);
|
||||
double t1 = std::max(a0, a1);
|
||||
|
||||
if (t0 < 1.0) {
|
||||
auto p0 = curr.position + t0 * (next.position - curr.position);
|
||||
auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>());
|
||||
new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l});
|
||||
}
|
||||
if (t1 > 0.0) {
|
||||
auto p1 = curr.position + t1 * (next.position - curr.position);
|
||||
auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>());
|
||||
new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l});
|
||||
}
|
||||
}
|
||||
}
|
||||
new_points.push_back(next);
|
||||
}
|
||||
points = new_points;
|
||||
}
|
||||
|
||||
if (max_line_length > 0) {
|
||||
std::vector<ExtendedPoint> new_points;
|
||||
new_points.reserve(points.size()*2);
|
||||
{
|
||||
for (size_t i = 0; i + 1 < points.size(); i++) {
|
||||
const ExtendedPoint &curr = points[i];
|
||||
const ExtendedPoint &next = points[i + 1];
|
||||
new_points.push_back(curr);
|
||||
double len = (next.position - curr.position).squaredNorm();
|
||||
double t = sqrt((max_line_length * max_line_length) / len);
|
||||
size_t new_point_count = 1.0 / t;
|
||||
for (size_t j = 1; j < new_point_count + 1; j++) {
|
||||
Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t);
|
||||
auto [p_dist, p_near_l,
|
||||
p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.cast<AABBScalar>());
|
||||
new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l});
|
||||
}
|
||||
}
|
||||
new_points.push_back(points.back());
|
||||
}
|
||||
points = new_points;
|
||||
}
|
||||
|
||||
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
|
||||
ExtendedPoint &a = points[point_idx];
|
||||
ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
|
||||
|
||||
int prev_point_idx = point_idx;
|
||||
while (prev_point_idx > 0) {
|
||||
prev_point_idx--;
|
||||
if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; }
|
||||
}
|
||||
|
||||
int next_point_index = point_idx;
|
||||
while (next_point_index < int(points.size()) - 1) {
|
||||
next_point_index++;
|
||||
if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; }
|
||||
}
|
||||
|
||||
if (prev_point_idx != point_idx && next_point_index != point_idx) {
|
||||
float distance = (prev.position - a.position).norm();
|
||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
||||
cestim.add_point(distance, alfa);
|
||||
}
|
||||
|
||||
a.curvature = cestim.get_curvature();
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
struct ProcessedPoint
|
||||
{
|
||||
Point p;
|
||||
float speed = 1.0f;
|
||||
float overlap = 1.0f;
|
||||
};
|
||||
|
||||
class ExtrusionQualityEstimator
|
||||
{
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
|
||||
const PrintObject *current_object;
|
||||
|
||||
public:
|
||||
void set_current_object(const PrintObject *object) { current_object = object; }
|
||||
|
||||
void prepare_for_new_layer(const Layer *layer)
|
||||
{
|
||||
if (layer == nullptr) return;
|
||||
const PrintObject *object = layer->object();
|
||||
prev_layer_boundaries[object] = next_layer_boundaries[object];
|
||||
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
|
||||
}
|
||||
|
||||
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path,
|
||||
const ConfigOptionPercents &overlaps,
|
||||
const ConfigOptionFloatsOrPercents &speeds,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed)
|
||||
{
|
||||
size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
|
||||
std::vector<std::pair<float, float>> speed_sections;
|
||||
for (size_t i = 0; i < speed_sections_count; i++) {
|
||||
float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
|
||||
float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
|
||||
speed_sections.push_back({distance, speed});
|
||||
}
|
||||
std::sort(speed_sections.begin(), speed_sections.end(),
|
||||
[](const std::pair<float, float> &a, const std::pair<float, float> &b) {
|
||||
if (a.first == b.first) {
|
||||
return a.second > b.second;
|
||||
}
|
||||
return a.first < b.first; });
|
||||
|
||||
std::pair<float, float> last_section{INFINITY, 0};
|
||||
for (auto §ion : speed_sections) {
|
||||
if (section.first == last_section.first) {
|
||||
section.second = last_section.second;
|
||||
} else {
|
||||
last_section = section;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExtendedPoint> extended_points =
|
||||
estimate_points_properties<true, true, true, true>(path.polyline.points, prev_layer_boundaries[current_object], path.width);
|
||||
const auto width_inv = 1.0f / path.width;
|
||||
std::vector<ProcessedPoint> processed_points;
|
||||
processed_points.reserve(extended_points.size());
|
||||
for (size_t i = 0; i < extended_points.size(); i++) {
|
||||
const ExtendedPoint &curr = extended_points[i];
|
||||
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
|
||||
|
||||
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
|
||||
float final_speed;
|
||||
if (distance <= speed_sections.front().first) {
|
||||
final_speed = original_speed;
|
||||
} else if (distance >= speed_sections.back().first) {
|
||||
final_speed = speed_sections.back().second;
|
||||
} else {
|
||||
size_t section_idx = 0;
|
||||
while (distance > speed_sections[section_idx + 1].first) {
|
||||
section_idx++;
|
||||
}
|
||||
float t = (distance - speed_sections[section_idx].first) /
|
||||
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
|
||||
}
|
||||
return final_speed;
|
||||
};
|
||||
|
||||
float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
|
||||
float overlap = std::min(1 - curr.distance * width_inv, 1 - next.distance * width_inv);
|
||||
|
||||
processed_points.push_back({ scaled(curr.position), extrusion_speed, overlap });
|
||||
}
|
||||
return processed_points;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_ExtrusionProcessor_hpp_
|
|
@ -28,6 +28,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
|
|||
print_config.gcode_flavor.value == gcfRepRapFirmware;
|
||||
m_max_acceleration = std::lrint(use_mach_limits ? print_config.machine_max_acceleration_extruding.values.front() : 0);
|
||||
m_max_jerk = std::lrint(use_mach_limits ? std::min(print_config.machine_max_jerk_x.values.front(), print_config.machine_max_jerk_y.values.front()) : 0);
|
||||
m_max_jerk_z = print_config.machine_max_jerk_z.values.front();
|
||||
m_max_jerk_e = print_config.machine_max_jerk_e.values.front();
|
||||
}
|
||||
|
||||
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
|
||||
|
@ -190,13 +192,13 @@ std::string GCodeWriter::set_acceleration(unsigned int acceleration)
|
|||
return gcode.str();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::set_jerk_xy(unsigned int jerk)
|
||||
std::string GCodeWriter::set_jerk_xy(double jerk)
|
||||
{
|
||||
// Clamp the jerk to the allowed maximum.
|
||||
if (m_max_jerk > 0 && jerk > m_max_jerk)
|
||||
jerk = m_max_jerk;
|
||||
|
||||
if (jerk < 1 || jerk == m_last_jerk)
|
||||
if (jerk < 0.01 || is_approx(jerk, m_last_jerk))
|
||||
return std::string();
|
||||
|
||||
m_last_jerk = jerk;
|
||||
|
@ -207,6 +209,9 @@ std::string GCodeWriter::set_jerk_xy(unsigned int jerk)
|
|||
else
|
||||
gcode << "M205 X" << jerk << " Y" << jerk;
|
||||
|
||||
if (m_is_bbl_printers)
|
||||
gcode << std::setprecision(2) << " Z" << m_max_jerk_z << " E" << m_max_jerk_e;
|
||||
|
||||
if (GCodeWriter::full_gcode_comment) gcode << " ; adjust jerk";
|
||||
gcode << "\n";
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;
|
||||
std::string set_bed_temperature(int temperature, bool wait = false);
|
||||
std::string set_acceleration(unsigned int acceleration);
|
||||
std::string set_jerk_xy(unsigned int jerk);
|
||||
std::string set_jerk_xy(double jerk);
|
||||
std::string set_pressure_advance(double pa) const;
|
||||
std::string reset_e(bool force = false);
|
||||
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
|
||||
|
@ -107,8 +107,10 @@ private:
|
|||
// Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware.
|
||||
// If set to zero, the limit is not in action.
|
||||
unsigned int m_max_acceleration;
|
||||
unsigned int m_max_jerk;
|
||||
unsigned int m_last_jerk;
|
||||
double m_max_jerk;
|
||||
double m_last_jerk;
|
||||
double m_max_jerk_z;
|
||||
double m_max_jerk_e;
|
||||
|
||||
unsigned int m_travel_acceleration;
|
||||
unsigned int m_travel_jerk;
|
||||
|
@ -170,6 +172,13 @@ public:
|
|||
// static constexpr const int XYZF_EXPORT_DIGITS = 6;
|
||||
// static constexpr const int E_EXPORT_DIGITS = 9;
|
||||
#endif
|
||||
static constexpr const std::array<double, 10> pow_10 { 1., 10., 100., 1000., 10000., 100000., 1000000., 10000000., 100000000., 1000000000. };
|
||||
static constexpr const std::array<double, 10> pow_10_inv { 1. / 1., 1. / 10., 1. / 100., 1. / 1000., 1. / 10000., 1. / 100000., 1. / 1000000., 1. / 10000000., 1. / 100000000., 1. / 1000000000. };
|
||||
|
||||
// Quantize doubles to a resolution of the G-code.
|
||||
static double quantize(double v, size_t ndigits) { return std::round(v * pow_10[ndigits]) * pow_10_inv[ndigits]; }
|
||||
static double quantize_xyzf(double v) { return quantize(v, XYZF_EXPORT_DIGITS); }
|
||||
static double quantize_e(double v) { return quantize(v, E_EXPORT_DIGITS); }
|
||||
|
||||
void emit_axis(const char axis, const double v, size_t digits);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef slic3r_Line_hpp_
|
||||
#define slic3r_Line_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
|
@ -22,149 +22,201 @@ Linef3 transform(const Linef3& line, const Transform3d& t);
|
|||
|
||||
namespace line_alg {
|
||||
|
||||
template<class L, class En = void> struct Traits {
|
||||
static constexpr int Dim = L::Dim;
|
||||
using Scalar = typename L::Scalar;
|
||||
template <class L, class En = void>
|
||||
struct Traits {
|
||||
static constexpr int Dim = L::Dim;
|
||||
using Scalar = typename L::Scalar;
|
||||
|
||||
static Vec<Dim, Scalar>& get_a(L &l) { return l.a; }
|
||||
static Vec<Dim, Scalar>& get_b(L &l) { return l.b; }
|
||||
static const Vec<Dim, Scalar>& get_a(const L &l) { return l.a; }
|
||||
static const Vec<Dim, Scalar>& get_b(const L &l) { return l.b; }
|
||||
};
|
||||
static Vec<Dim, Scalar>& get_a(L& l) { return l.a; }
|
||||
static Vec<Dim, Scalar>& get_b(L& l) { return l.b; }
|
||||
static const Vec<Dim, Scalar>& get_a(const L& l) { return l.a; }
|
||||
static const Vec<Dim, Scalar>& get_b(const L& l) { return l.b; }
|
||||
};
|
||||
|
||||
template<class L> const constexpr int Dim = Traits<remove_cvref_t<L>>::Dim;
|
||||
template<class L> using Scalar = typename Traits<remove_cvref_t<L>>::Scalar;
|
||||
template <class L>
|
||||
const constexpr int Dim = Traits<remove_cvref_t<L>>::Dim;
|
||||
template <class L>
|
||||
using Scalar = typename Traits<remove_cvref_t<L>>::Scalar;
|
||||
|
||||
template<class L> auto get_a(L &&l) { return Traits<remove_cvref_t<L>>::get_a(l); }
|
||||
template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l); }
|
||||
template <class L>
|
||||
auto get_a(L&& l) { return Traits<remove_cvref_t<L>>::get_a(l); }
|
||||
template <class L>
|
||||
auto get_b(L&& l) { return Traits<remove_cvref_t<L>>::get_b(l); }
|
||||
|
||||
// Distance to the closest point of line.
|
||||
template<class L>
|
||||
double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *nearest_point)
|
||||
{
|
||||
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0) {
|
||||
// a == b case
|
||||
*nearest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
}
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
if (t < 0.0) {
|
||||
// beyond the 'a' end of the segment
|
||||
*nearest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
} else if (t > 1.0) {
|
||||
// beyond the 'b' end of the segment
|
||||
*nearest_point = get_b(line);
|
||||
return (point - get_b(line)).template cast<double>().squaredNorm();
|
||||
// Distance to the closest point of line.
|
||||
template <class L>
|
||||
double distance_to_squared(const L& line, const Vec<Dim<L>, Scalar<L>>& point, Vec<Dim<L>, Scalar<L>>* nearest_point)
|
||||
{
|
||||
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0) {
|
||||
// a == b case
|
||||
*nearest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
}
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
if (t <= 0.0) {
|
||||
// beyond the 'a' end of the segment
|
||||
*nearest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
} else if (t >= 1.0) {
|
||||
// beyond the 'b' end of the segment
|
||||
*nearest_point = get_b(line);
|
||||
return (point - get_b(line)).template cast<double>().squaredNorm();
|
||||
}
|
||||
|
||||
*nearest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
*nearest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
// Distance to the closest point of line.
|
||||
template<class L>
|
||||
double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
Vec<Dim<L>, Scalar<L>> nearest_point;
|
||||
return distance_to_squared<L>(line, point, &nearest_point);
|
||||
}
|
||||
|
||||
template<class L>
|
||||
double distance_to(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
return std::sqrt(distance_to_squared(line, point));
|
||||
}
|
||||
|
||||
// Returns a squared distance to the closest point on the infinite.
|
||||
// Returned nearest_point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template<class L>
|
||||
double distance_to_infinite_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *closest_point)
|
||||
{
|
||||
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.) {
|
||||
// a == b case
|
||||
*closest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
// Distance to the closest point of line.
|
||||
template <class L>
|
||||
double distance_to_squared(const L& line, const Vec<Dim<L>, Scalar<L>>& point)
|
||||
{
|
||||
Vec<Dim<L>, Scalar<L>> nearest_point;
|
||||
return distance_to_squared<L>(line, point, &nearest_point);
|
||||
}
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
*closest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
// Returns a squared distance to the closest point on the infinite.
|
||||
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template<class L>
|
||||
double distance_to_infinite_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
Vec<Dim<L>, Scalar<L>> nearest_point;
|
||||
return distance_to_infinite_squared<L>(line, point, &nearest_point);
|
||||
}
|
||||
template <class L>
|
||||
double distance_to(const L& line, const Vec<Dim<L>, Scalar<L>>& point)
|
||||
{
|
||||
return std::sqrt(distance_to_squared(line, point));
|
||||
}
|
||||
|
||||
// Returns a distance to the closest point on the infinite.
|
||||
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template<class L>
|
||||
double distance_to_infinite(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
return std::sqrt(distance_to_infinite_squared(line, point));
|
||||
}
|
||||
// Returns a squared distance to the closest point on the infinite.
|
||||
// Returned nearest_point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template <class L>
|
||||
double distance_to_infinite_squared(const L& line, const Vec<Dim<L>, Scalar<L>>& point, Vec<Dim<L>, Scalar<L>>* closest_point)
|
||||
{
|
||||
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.) {
|
||||
// a == b case
|
||||
*closest_point = get_a(line);
|
||||
return va.squaredNorm();
|
||||
}
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// We find projection of this point onto the line.
|
||||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
*closest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
// Returns a squared distance to the closest point on the infinite.
|
||||
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template <class L>
|
||||
double distance_to_infinite_squared(const L& line, const Vec<Dim<L>, Scalar<L>>& point)
|
||||
{
|
||||
Vec<Dim<L>, Scalar<L>> nearest_point;
|
||||
return distance_to_infinite_squared<L>(line, point, &nearest_point);
|
||||
}
|
||||
|
||||
// Returns a distance to the closest point on the infinite.
|
||||
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
template <class L>
|
||||
double distance_to_infinite(const L& line, const Vec<Dim<L>, Scalar<L>>& point)
|
||||
{
|
||||
return std::sqrt(distance_to_infinite_squared(line, point));
|
||||
}
|
||||
|
||||
template <class L>
|
||||
bool intersection(const L& l1, const L& l2, Vec<Dim<L>, Scalar<L>>* intersection_pt)
|
||||
{
|
||||
using Floating = typename std::conditional<std::is_floating_point<Scalar<L>>::value, Scalar<L>, double>::type;
|
||||
using VecType = const Vec<Dim<L>, Floating>;
|
||||
const VecType v1 = (l1.b - l1.a).template cast<Floating>();
|
||||
const VecType v2 = (l2.b - l2.a).template cast<Floating>();
|
||||
Floating denom = cross2(v1, v2);
|
||||
if (fabs(denom) < EPSILON)
|
||||
#if 0
|
||||
// Lines are collinear. Return true if they are coincident (overlappign).
|
||||
return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
const VecType v12 = (l1.a - l2.a).template cast<Floating>();
|
||||
Floating nume_a = cross2(v2, v12);
|
||||
Floating nume_b = cross2(v1, v12);
|
||||
Floating t1 = nume_a / denom;
|
||||
Floating t2 = nume_b / denom;
|
||||
if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
|
||||
// Get the intersection point.
|
||||
(*intersection_pt) = (l1.a.template cast<Floating>() + t1 * v1).template cast<Scalar<L>>();
|
||||
return true;
|
||||
}
|
||||
return false; // not intersecting
|
||||
}
|
||||
|
||||
} // namespace line_alg
|
||||
|
||||
class Line
|
||||
{
|
||||
class Line {
|
||||
public:
|
||||
Line() {}
|
||||
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
|
||||
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
|
||||
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
||||
void translate(const Point &v) { this->a += v; this->b += v; }
|
||||
void translate(double x, double y) { this->translate(Point(x, y)); }
|
||||
void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
|
||||
void reverse() { std::swap(this->a, this->b); }
|
||||
Line() { }
|
||||
Line(const Point& _a, const Point& _b)
|
||||
: a(_a)
|
||||
, b(_b)
|
||||
{
|
||||
}
|
||||
explicit operator Lines() const
|
||||
{
|
||||
Lines lines;
|
||||
lines.emplace_back(*this);
|
||||
return lines;
|
||||
}
|
||||
void scale(double factor)
|
||||
{
|
||||
this->a *= factor;
|
||||
this->b *= factor;
|
||||
}
|
||||
void translate(const Point& v)
|
||||
{
|
||||
this->a += v;
|
||||
this->b += v;
|
||||
}
|
||||
void translate(double x, double y) { this->translate(Point(x, y)); }
|
||||
void rotate(double angle, const Point& center)
|
||||
{
|
||||
this->a.rotate(angle, center);
|
||||
this->b.rotate(angle, center);
|
||||
}
|
||||
void reverse() { std::swap(this->a, this->b); }
|
||||
double length() const { return (b - a).cast<double>().norm(); }
|
||||
Point midpoint() const { return (this->a + this->b) / 2; }
|
||||
bool intersection_infinite(const Line &other, Point* point) const;
|
||||
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
||||
double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); }
|
||||
double distance_to_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_squared(*this, point, closest_point); }
|
||||
double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); }
|
||||
double distance_to_infinite_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); }
|
||||
double perp_distance_to(const Point &point) const;
|
||||
bool parallel_to(double angle) const;
|
||||
bool parallel_to(const Line& line) const;
|
||||
bool perpendicular_to(double angle) const;
|
||||
bool perpendicular_to(const Line& line) const;
|
||||
Point midpoint() const { return (this->a + this->b) / 2; }
|
||||
bool intersection_infinite(const Line& other, Point* point) const;
|
||||
bool operator==(const Line& rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
||||
double distance_to_squared(const Point& point) const { return distance_to_squared(point, this->a, this->b); }
|
||||
double distance_to_squared(const Point& point, Point* closest_point) const { return line_alg::distance_to_squared(*this, point, closest_point); }
|
||||
double distance_to(const Point& point) const { return distance_to(point, this->a, this->b); }
|
||||
double distance_to_infinite_squared(const Point& point, Point* closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); }
|
||||
double perp_distance_to(const Point& point) const;
|
||||
bool parallel_to(double angle) const;
|
||||
bool parallel_to(const Line& line) const;
|
||||
bool perpendicular_to(double angle) const;
|
||||
bool perpendicular_to(const Line& line) const;
|
||||
double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
|
||||
double orientation() const;
|
||||
double direction() const;
|
||||
Vector vector() const { return this->b - this->a; }
|
||||
Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); }
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
double ccw(const Point& point) const { return point.ccw(*this); }
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
|
||||
bool clip_with_bbox(const BoundingBox &bbox);
|
||||
bool clip_with_bbox(const BoundingBox& bbox);
|
||||
// Extend the line from both sides by an offset.
|
||||
void extend(double offset);
|
||||
void extend(double offset);
|
||||
|
||||
static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||
static inline double distance_to_squared(const Point& point, const Point& a, const Point& b) { return line_alg::distance_to_squared(Line { a, b }, Vec<2, coord_t> { point }); }
|
||||
static double distance_to(const Point& point, const Point& a, const Point& b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||
|
||||
// Returns a distance to the closest point on the infinite.
|
||||
// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment.
|
||||
static inline double distance_to_infinite_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_infinite_squared(Line{a, b}, Vec<2, coord_t>{point}); }
|
||||
static double distance_to_infinite(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_infinite_squared(point, a, b)); }
|
||||
static inline double distance_to_infinite_squared(const Point& point, const Point& a, const Point& b) { return line_alg::distance_to_infinite_squared(Line { a, b }, Vec<2, coord_t> { point }); }
|
||||
static double distance_to_infinite(const Point& point, const Point& a, const Point& b) { return sqrt(distance_to_infinite_squared(point, a, b)); }
|
||||
|
||||
Point a;
|
||||
Point b;
|
||||
|
@ -173,23 +225,43 @@ public:
|
|||
using Scalar = Point::Scalar;
|
||||
};
|
||||
|
||||
class ThickLine : public Line
|
||||
{
|
||||
class ThickLine : public Line {
|
||||
public:
|
||||
ThickLine() : a_width(0), b_width(0) {}
|
||||
ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {}
|
||||
ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {}
|
||||
ThickLine()
|
||||
: a_width(0)
|
||||
, b_width(0)
|
||||
{
|
||||
}
|
||||
ThickLine(const Point& a, const Point& b)
|
||||
: Line(a, b)
|
||||
, a_width(0)
|
||||
, b_width(0)
|
||||
{
|
||||
}
|
||||
ThickLine(const Point& a, const Point& b, double wa, double wb)
|
||||
: Line(a, b)
|
||||
, a_width(wa)
|
||||
, b_width(wb)
|
||||
{
|
||||
}
|
||||
|
||||
double a_width, b_width;
|
||||
};
|
||||
|
||||
class Line3
|
||||
{
|
||||
class Line3 {
|
||||
public:
|
||||
Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {}
|
||||
Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {}
|
||||
Line3()
|
||||
: a(Vec3crd::Zero())
|
||||
, b(Vec3crd::Zero())
|
||||
{
|
||||
}
|
||||
Line3(const Vec3crd& _a, const Vec3crd& _b)
|
||||
: a(_a)
|
||||
, b(_b)
|
||||
{
|
||||
}
|
||||
|
||||
double length() const { return (this->a - this->b).cast<double>().norm(); }
|
||||
double length() const { return (this->a - this->b).cast<double>().norm(); }
|
||||
Vec3crd vector() const { return this->b - this->a; }
|
||||
|
||||
Vec3crd a;
|
||||
|
@ -199,11 +271,18 @@ public:
|
|||
using Scalar = Vec3crd::Scalar;
|
||||
};
|
||||
|
||||
class Linef
|
||||
{
|
||||
class Linef {
|
||||
public:
|
||||
Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
|
||||
Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
|
||||
Linef()
|
||||
: a(Vec2d::Zero())
|
||||
, b(Vec2d::Zero())
|
||||
{
|
||||
}
|
||||
Linef(const Vec2d& _a, const Vec2d& _b)
|
||||
: a(_a)
|
||||
, b(_b)
|
||||
{
|
||||
}
|
||||
|
||||
Vec2d a;
|
||||
Vec2d b;
|
||||
|
@ -211,18 +290,30 @@ public:
|
|||
static const constexpr int Dim = 2;
|
||||
using Scalar = Vec2d::Scalar;
|
||||
};
|
||||
using Linesf = std::vector<Linef>;
|
||||
|
||||
class Linef3
|
||||
{
|
||||
class Linef3 {
|
||||
public:
|
||||
Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {}
|
||||
Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {}
|
||||
Linef3()
|
||||
: a(Vec3d::Zero())
|
||||
, b(Vec3d::Zero())
|
||||
{
|
||||
}
|
||||
Linef3(const Vec3d& _a, const Vec3d& _b)
|
||||
: a(_a)
|
||||
, b(_b)
|
||||
{
|
||||
}
|
||||
|
||||
Vec3d intersect_plane(double z) const;
|
||||
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
||||
Vec3d vector() const { return this->b - this->a; }
|
||||
Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); }
|
||||
double length() const { return vector().norm(); }
|
||||
Vec3d intersect_plane(double z) const;
|
||||
void scale(double factor)
|
||||
{
|
||||
this->a *= factor;
|
||||
this->b *= factor;
|
||||
}
|
||||
Vec3d vector() const { return this->b - this->a; }
|
||||
Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); }
|
||||
double length() const { return vector().norm(); }
|
||||
|
||||
Vec3d a;
|
||||
Vec3d b;
|
||||
|
@ -231,26 +322,31 @@ public:
|
|||
using Scalar = Vec3d::Scalar;
|
||||
};
|
||||
|
||||
BoundingBox get_extents(const Lines &lines);
|
||||
BoundingBox get_extents(const Lines& lines);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
namespace boost {
|
||||
namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Slic3r::Line> { typedef segment_concept type; };
|
||||
struct geometry_concept<Slic3r::Line> {
|
||||
typedef segment_concept type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct segment_traits<Slic3r::Line> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
static inline point_type get(const Slic3r::Line& line, direction_1d dir) {
|
||||
static inline point_type get(const Slic3r::Line& line, direction_1d dir)
|
||||
{
|
||||
return dir.to_int() ? line.b : line.a;
|
||||
}
|
||||
};
|
||||
} }
|
||||
}
|
||||
}
|
||||
// end Boost
|
||||
|
||||
#endif // slic3r_Line_hpp_
|
||||
|
|
|
@ -271,7 +271,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
Polylines remain_polines;
|
||||
|
||||
//BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning
|
||||
if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||
if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {
|
||||
for (auto it = lower_polygons_series->begin();
|
||||
it != lower_polygons_series->end(); it++)
|
||||
{
|
||||
|
@ -635,6 +635,7 @@ void PerimeterGenerator::process_classic()
|
|||
// external perimeters
|
||||
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
||||
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
coord_t ext_perimeter_spacing2;
|
||||
if(config->precise_outer_wall)
|
||||
|
@ -1114,7 +1115,6 @@ void PerimeterGenerator::process_arachne()
|
|||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
||||
|
||||
// overhang perimeters
|
||||
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
||||
|
||||
|
@ -1133,9 +1133,14 @@ void PerimeterGenerator::process_arachne()
|
|||
// we need to process each island separately because we might have different
|
||||
// extra perimeters for each one
|
||||
for (const Surface& surface : this->slices->surfaces) {
|
||||
coord_t bead_width_0 = ext_perimeter_spacing;
|
||||
if (config->precise_outer_wall)
|
||||
bead_width_0 = ext_perimeter_width + this->perimeter_flow.scaled_width() - perimeter_spacing;
|
||||
// detect how many perimeters must be generated for this island
|
||||
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution),
|
||||
config->precise_outer_wall ? -float(ext_perimeter_width / 2. - bead_width_0 / 2.)
|
||||
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
Polygons last_p = to_polygons(last);
|
||||
|
||||
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
||||
|
@ -1156,8 +1161,14 @@ void PerimeterGenerator::process_arachne()
|
|||
input_params.wall_transition_angle = this->object_config->wall_transition_angle.value;
|
||||
input_params.wall_distribution_count = this->object_config->wall_distribution_count.value;
|
||||
}
|
||||
coord_t wall_0_inset = 0;
|
||||
//if (config->precise_outer_wall)
|
||||
// wall_0_inset = 0.5 * (ext_perimeter_width + this->perimeter_flow.scaled_width() - ext_perimeter_spacing -
|
||||
// perimeter_spacing);
|
||||
|
||||
Arachne::WallToolPaths wallToolPaths(last_p, bead_width_0, perimeter_spacing, coord_t(loop_number + 1),
|
||||
wall_0_inset, layer_height, input_params);
|
||||
|
||||
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, input_params);
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||
loop_number = int(perimeters.size()) - 1;
|
||||
|
||||
|
@ -1399,6 +1410,7 @@ std::map<int, Polygons> PerimeterGenerator::generate_lower_polygons_series(float
|
|||
// BBS: increase start_offset a little to avoid to calculate 90 degree as overhang
|
||||
offset_series[0] = start_offset + 0.5 * (end_offset - start_offset) / (overhang_sampling_number - 1);
|
||||
offset_series[overhang_sampling_number - 2] = end_offset;
|
||||
offset_series.back() = 0.1 * nozzle_diameter;
|
||||
|
||||
std::map<int, Polygons> lower_polygons_series;
|
||||
if (this->lower_slices == NULL) {
|
||||
|
|
|
@ -96,6 +96,18 @@ inline typename Derived::Scalar cross2(const Eigen::MatrixBase<Derived> &v1, con
|
|||
template<typename T, int Options>
|
||||
inline Eigen::Matrix<T, 2, 1, Eigen::DontAlign> perp(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v) { return Eigen::Matrix<T, 2, 1, Eigen::DontAlign>(- v.y(), v.x()); }
|
||||
|
||||
// Angle from v1 to v2, returning double atan2(y, x) normalized to <-PI, PI>.
|
||||
template <typename Derived, typename Derived2>
|
||||
inline double angle(const Eigen::MatrixBase<Derived>& v1, const Eigen::MatrixBase<Derived2>& v2)
|
||||
{
|
||||
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "angle(): first parameter is not a 2D vector");
|
||||
static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "angle(): second parameter is not a 2D vector");
|
||||
auto v1d = v1.template cast<double>();
|
||||
auto v2d = v2.template cast<double>();
|
||||
return atan2(cross2(v1d, v2d), v1d.dot(v2d));
|
||||
}
|
||||
|
||||
|
||||
template<class T, int N, int Options>
|
||||
Eigen::Matrix<T, 2, 1, Eigen::DontAlign> to_2d(const Eigen::MatrixBase<Eigen::Matrix<T, N, 1, Options>> &ptN) { return { ptN.x(), ptN.y() }; }
|
||||
|
||||
|
|
|
@ -752,7 +752,8 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "post_process", "travel_acceleration","inner_wall_acceleration",
|
||||
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
|
||||
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer",
|
||||
"print_flow_ratio","seam_gap","role_based_wipe_speed","wipe_speed","accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "bridge_density", "precise_outer_wall"
|
||||
"print_flow_ratio","seam_gap","role_based_wipe_speed","wipe_speed","accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "bridge_density", "precise_outer_wall",
|
||||
"overhang_speed_classic"
|
||||
|
||||
};
|
||||
|
||||
|
|
140
src/libslic3r/PrincipalComponents2D.cpp
Normal file
140
src/libslic3r/PrincipalComponents2D.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "PrincipalComponents2D.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
|
||||
// returns triangle area, first_moment_of_area_xy, second_moment_of_area_xy, second_moment_of_area_covariance
|
||||
// none of the values is divided/normalized by area.
|
||||
// The function computes intgeral over the area of the triangle, with function f(x,y) = x for first moments of area (y is analogous)
|
||||
// f(x,y) = x^2 for second moment of area
|
||||
// and f(x,y) = x*y for second moment of area covariance
|
||||
std::tuple<float, Vec2f, Vec2f, float> compute_moments_of_area_of_triangle(const Vec2f &a, const Vec2f &b, const Vec2f &c)
|
||||
{
|
||||
// based on the following guide:
|
||||
// Denote the vertices of S by a, b, c. Then the map
|
||||
// g:(u,v)↦a+u(b−a)+v(c−a) ,
|
||||
// which in coordinates appears as
|
||||
// g:(u,v)↦{x(u,v)y(u,v)=a1+u(b1−a1)+v(c1−a1)=a2+u(b2−a2)+v(c2−a2) ,(1)
|
||||
// obviously maps S′ bijectively onto S. Therefore the transformation formula for multiple integrals steps into action, and we obtain
|
||||
// ∫Sf(x,y)d(x,y)=∫S′f(x(u,v),y(u,v))∣∣Jg(u,v)∣∣ d(u,v) .
|
||||
// In the case at hand the Jacobian determinant is a constant: From (1) we obtain
|
||||
// Jg(u,v)=det[xuyuxvyv]=(b1−a1)(c2−a2)−(c1−a1)(b2−a2) .
|
||||
// Therefore we can write
|
||||
// ∫Sf(x,y)d(x,y)=∣∣Jg∣∣∫10∫1−u0f~(u,v) dv du ,
|
||||
// where f~ denotes the pullback of f to S′:
|
||||
// f~(u,v):=f(x(u,v),y(u,v)) .
|
||||
// Don't forget taking the absolute value of Jg!
|
||||
|
||||
float jacobian_determinant_abs = std::abs((b.x() - a.x()) * (c.y() - a.y()) - (c.x() - a.x()) * (b.y() - a.y()));
|
||||
|
||||
// coordinate transform: gx(u,v) = a.x + u * (b.x - a.x) + v * (c.x - a.x)
|
||||
// coordinate transform: gy(u,v) = a.y + u * (b.y - a.y) + v * (c.y - a.y)
|
||||
// second moment of area for x: f(x, y) = x^2;
|
||||
// f(gx(u,v), gy(u,v)) = gx(u,v)^2 = ... (long expanded form)
|
||||
|
||||
// result is Int_T func = jacobian_determinant_abs * Int_0^1 Int_0^1-u func(gx(u,v), gy(u,v)) dv du
|
||||
// integral_0^1 integral_0^(1 - u) (a + u (b - a) + v (c - a))^2 dv du = 1/12 (a^2 + a (b + c) + b^2 + b c + c^2)
|
||||
|
||||
Vec2f second_moment_of_area_xy = jacobian_determinant_abs *
|
||||
(a.cwiseProduct(a) + b.cwiseProduct(b) + b.cwiseProduct(c) + c.cwiseProduct(c) +
|
||||
a.cwiseProduct(b + c)) /
|
||||
12.0f;
|
||||
// second moment of area covariance : f(x, y) = x*y;
|
||||
// f(gx(u,v), gy(u,v)) = gx(u,v)*gy(u,v) = ... (long expanded form)
|
||||
//(a_1 + u * (b_1 - a_1) + v * (c_1 - a_1)) * (a_2 + u * (b_2 - a_2) + v * (c_2 - a_2))
|
||||
// == (a_1 + u (b_1 - a_1) + v (c_1 - a_1)) (a_2 + u (b_2 - a_2) + v (c_2 - a_2))
|
||||
|
||||
// intermediate result: integral_0^(1 - u) (a_1 + u (b_1 - a_1) + v (c_1 - a_1)) (a_2 + u (b_2 - a_2) + v (c_2 - a_2)) dv =
|
||||
// 1/6 (u - 1) (-c_1 (u - 1) (a_2 (u - 1) - 3 b_2 u) - c_2 (u - 1) (a_1 (u - 1) - 3 b_1 u + 2 c_1 (u - 1)) + 3 b_1 u (a_2 (u - 1) - 2
|
||||
// b_2 u) + a_1 (u - 1) (3 b_2 u - 2 a_2 (u - 1))) result = integral_0^1 1/6 (u - 1) (-c_1 (u - 1) (a_2 (u - 1) - 3 b_2 u) - c_2 (u -
|
||||
// 1) (a_1 (u - 1) - 3 b_1 u + 2 c_1 (u - 1)) + 3 b_1 u (a_2 (u - 1) - 2 b_2 u) + a_1 (u - 1) (3 b_2 u - 2 a_2 (u - 1))) du =
|
||||
// 1/24 (a_2 (b_1 + c_1) + a_1 (2 a_2 + b_2 + c_2) + b_2 c_1 + b_1 c_2 + 2 b_1 b_2 + 2 c_1 c_2)
|
||||
// result is Int_T func = jacobian_determinant_abs * Int_0^1 Int_0^1-u func(gx(u,v), gy(u,v)) dv du
|
||||
float second_moment_of_area_covariance = jacobian_determinant_abs * (1.0f / 24.0f) *
|
||||
(a.y() * (b.x() + c.x()) + a.x() * (2.0f * a.y() + b.y() + c.y()) + b.y() * c.x() +
|
||||
b.x() * c.y() + 2.0f * b.x() * b.y() + 2.0f * c.x() * c.y());
|
||||
|
||||
float area = jacobian_determinant_abs * 0.5f;
|
||||
|
||||
Vec2f first_moment_of_area_xy = jacobian_determinant_abs * (a + b + c) / 6.0f;
|
||||
|
||||
return {area, first_moment_of_area_xy, second_moment_of_area_xy, second_moment_of_area_covariance};
|
||||
};
|
||||
|
||||
// returns two eigenvectors of the area covered by given polygons. The vectors are sorted by their corresponding eigenvalue, largest first
|
||||
std::tuple<Vec2f, Vec2f> compute_principal_components(const Polygons &polys)
|
||||
{
|
||||
Vec2f centroid_accumulator = Vec2f::Zero();
|
||||
Vec2f second_moment_of_area_accumulator = Vec2f::Zero();
|
||||
float second_moment_of_area_covariance_accumulator = 0.0f;
|
||||
float area = 0.0f;
|
||||
|
||||
for (const Polygon &poly : polys) {
|
||||
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
|
||||
for (size_t i = 2; i < poly.points.size(); i++) {
|
||||
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
|
||||
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
|
||||
|
||||
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
|
||||
|
||||
auto [triangle_area, first_moment_of_area, second_moment_area,
|
||||
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
|
||||
area += sign * triangle_area;
|
||||
centroid_accumulator += sign * first_moment_of_area;
|
||||
second_moment_of_area_accumulator += sign * second_moment_area;
|
||||
second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
|
||||
}
|
||||
}
|
||||
|
||||
if (area <= 0.0) {
|
||||
return {Vec2f::Zero(), Vec2f::Zero()};
|
||||
}
|
||||
|
||||
Vec2f centroid = centroid_accumulator / area;
|
||||
Vec2f variance = second_moment_of_area_accumulator / area - centroid.cwiseProduct(centroid);
|
||||
double covariance = second_moment_of_area_covariance_accumulator / area - centroid.x() * centroid.y();
|
||||
#if 0
|
||||
std::cout << "area : " << area << std::endl;
|
||||
std::cout << "variancex : " << variance.x() << std::endl;
|
||||
std::cout << "variancey : " << variance.y() << std::endl;
|
||||
std::cout << "covariance : " << covariance << std::endl;
|
||||
#endif
|
||||
if (abs(covariance) < EPSILON) {
|
||||
std::tuple<Vec2f, Vec2f> result{Vec2f{variance.x(), 0.0}, Vec2f{0.0, variance.y()}};
|
||||
if (variance.y() > variance.x()) {
|
||||
return {std::get<1>(result), std::get<0>(result)};
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
|
||||
// now we find the first principal component of the covered area by computing max eigenvalue and the correspoding eigenvector of
|
||||
// covariance matrix
|
||||
// covaraince matrix C is : | VarX Cov |
|
||||
// | Cov VarY |
|
||||
// Eigenvalues are solutions to det(C - lI) = 0, where l is the eigenvalue and I unit matrix
|
||||
// Eigenvector for eigenvalue l is any vector v such that Cv = lv
|
||||
|
||||
float eigenvalue_a = 0.5f * (variance.x() + variance.y() +
|
||||
sqrt((variance.x() - variance.y()) * (variance.x() - variance.y()) + 4.0f * covariance * covariance));
|
||||
float eigenvalue_b = 0.5f * (variance.x() + variance.y() -
|
||||
sqrt((variance.x() - variance.y()) * (variance.x() - variance.y()) + 4.0f * covariance * covariance));
|
||||
Vec2f eigenvector_a{(eigenvalue_a - variance.y()) / covariance, 1.0f};
|
||||
Vec2f eigenvector_b{(eigenvalue_b - variance.y()) / covariance, 1.0f};
|
||||
|
||||
#if 0
|
||||
std::cout << "eigenvalue_a: " << eigenvalue_a << std::endl;
|
||||
std::cout << "eigenvalue_b: " << eigenvalue_b << std::endl;
|
||||
std::cout << "eigenvectorA: " << eigenvector_a.x() << " " << eigenvector_a.y() << std::endl;
|
||||
std::cout << "eigenvectorB: " << eigenvector_b.x() << " " << eigenvector_b.y() << std::endl;
|
||||
#endif
|
||||
|
||||
if (eigenvalue_a > eigenvalue_b) {
|
||||
return {eigenvector_a, eigenvector_b};
|
||||
} else {
|
||||
return {eigenvector_b, eigenvector_a};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
src/libslic3r/PrincipalComponents2D.hpp
Normal file
24
src/libslic3r/PrincipalComponents2D.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef slic3r_PrincipalComponents2D_hpp_
|
||||
#define slic3r_PrincipalComponents2D_hpp_
|
||||
|
||||
#include "AABBTreeLines.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include <vector>
|
||||
#include "Polygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// returns triangle area, first_moment_of_area_xy, second_moment_of_area_xy, second_moment_of_area_covariance
|
||||
// none of the values is divided/normalized by area.
|
||||
// The function computes intgeral over the area of the triangle, with function f(x,y) = x for first moments of area (y is analogous)
|
||||
// f(x,y) = x^2 for second moment of area
|
||||
// and f(x,y) = x*y for second moment of area covariance
|
||||
std::tuple<float, Vec2f, Vec2f, float> compute_moments_of_area_of_triangle(const Vec2f &a, const Vec2f &b, const Vec2f &c);
|
||||
|
||||
// returns two eigenvectors of the area covered by given polygons. The vectors are sorted by their corresponding eigenvalue, largest first
|
||||
std::tuple<Vec2f, Vec2f> compute_principal_components(const Polygons &polys);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -670,7 +670,6 @@ public:
|
|||
const PrintConfig& config() const { return m_config; }
|
||||
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
|
||||
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
|
||||
PrintRegionConfig& default_region_config() { return m_default_region_config; }
|
||||
ConstPrintObjectPtrsAdaptor objects() const { return ConstPrintObjectPtrsAdaptor(&m_objects); }
|
||||
PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
|
||||
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
|
||||
|
|
|
@ -659,7 +659,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.emplace_back("75%");
|
||||
def->enum_values.emplace_back("95%");
|
||||
def->enum_labels.emplace_back("0%");
|
||||
def->enum_labels.emplace_back("10%");
|
||||
def->enum_labels.emplace_back("5%");
|
||||
def->enum_labels.emplace_back("25%");
|
||||
def->enum_labels.emplace_back("50%");
|
||||
def->enum_labels.emplace_back("75%");
|
||||
|
@ -718,10 +718,10 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
|
||||
def = this->add("precise_outer_wall",coBool);
|
||||
def->label = L("Precise wall");
|
||||
def->label = L("Precise wall(experimental)");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Improve outer wall precesion by adjusting outer wall spacing");
|
||||
def->set_default_value(new ConfigOptionBool{true});
|
||||
def->tooltip = L("Improve shell precesion by adjusting outer wall spacing. This also improves layer consistency.");
|
||||
def->set_default_value(new ConfigOptionBool{false});
|
||||
|
||||
def = this->add("only_one_wall_top", coBool);
|
||||
def->label = L("Only one wall on top surfaces");
|
||||
|
@ -735,6 +735,13 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Use only one wall on first layer, to give more space to the bottom infill pattern");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("overhang_speed_classic", coBool);
|
||||
def->label = L("Classic mode");
|
||||
def->category = L("Speed");
|
||||
def->tooltip = L("Enable this option to use classic mode");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool{ false });
|
||||
|
||||
def = this->add("enable_overhang_speed", coBool);
|
||||
def->label = L("Slow down for overhang");
|
||||
def->category = L("Speed");
|
||||
|
@ -1019,7 +1026,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipMonotonic));
|
||||
|
||||
def = this->add("bottom_surface_pattern", coEnum);
|
||||
def->label = L("Bottom surface pattern");
|
||||
|
@ -1052,23 +1059,23 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("small_perimeter_speed", coFloatOrPercent);
|
||||
def->label = L("Small perimeters");
|
||||
def->category = L("Speed");
|
||||
def->tooltip = L("This separate setting will affect the speed of perimeters having radius <= 6.5mm "
|
||||
def->tooltip = L("This separate setting will affect the speed of perimeters having radius <= small_perimeter_threshold "
|
||||
"(usually holes). If expressed as percentage (for example: 80%) it will be calculated "
|
||||
"on the outer wall speed setting above. Set to zero for auto.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->ratio_over = "outer_wall_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(100, true));
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||
|
||||
def = this->add("small_perimeter_threshold", coFloat);
|
||||
def->label = L("Small perimeters threshold");
|
||||
def->category = L("Speed");
|
||||
def->tooltip = L("This sets the threshold for small perimeter length. Default threshold is 6.5mm");
|
||||
def->tooltip = L("This sets the threshold for small perimeter length. Default threshold is 0mm");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(6.5));
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("wall_infill_order", coEnum);
|
||||
def->label = L("Order of inner wall/outer wall/infil");
|
||||
|
@ -3280,7 +3287,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Classic"));
|
||||
def->enum_labels.push_back(L("Arachne"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Classic));
|
||||
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
|
||||
|
||||
def = this->add("wall_transition_length", coPercent);
|
||||
def->label = L("Wall transition length");
|
||||
|
|
|
@ -763,6 +763,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, wipe_on_loops))
|
||||
((ConfigOptionEnum<WallInfillOrder>, wall_infill_order))
|
||||
((ConfigOptionBool, precise_outer_wall))
|
||||
((ConfigOptionBool, overhang_speed_classic))
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -826,7 +826,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
//BBS
|
||||
|| opt_key == "enable_overhang_speed"
|
||||
|| opt_key == "detect_thin_wall"
|
||||
|| opt_key == "precise_outer_wall") {
|
||||
|| opt_key == "precise_outer_wall"
|
||||
|| opt_key == "overhang_speed_classic") {
|
||||
steps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (opt_key == "bridge_flow" || opt_key == "bridge_density") {
|
||||
|
|
1267
src/libslic3r/SupportSpotsGenerator.cpp
Normal file
1267
src/libslic3r/SupportSpotsGenerator.cpp
Normal file
File diff suppressed because it is too large
Load diff
159
src/libslic3r/SupportSpotsGenerator.hpp
Normal file
159
src/libslic3r/SupportSpotsGenerator.hpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
|
||||
#define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_
|
||||
/*
|
||||
#include "Layer.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "PrintBase.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace SupportSpotsGenerator {
|
||||
|
||||
struct Params
|
||||
{
|
||||
Params(
|
||||
const std::vector<std::string> &filament_types, float max_acceleration, int raft_layers_count, BrimType brim_type, float brim_width)
|
||||
: max_acceleration(max_acceleration), raft_layers_count(raft_layers_count), brim_type(brim_type), brim_width(brim_width)
|
||||
{
|
||||
if (filament_types.size() > 1) {
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "SupportSpotsGenerator does not currently handle different materials properly, only first will be used";
|
||||
}
|
||||
if (filament_types.empty() || filament_types[0].empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "SupportSpotsGenerator error: empty filament_type";
|
||||
filament_type = std::string("PLA");
|
||||
} else {
|
||||
filament_type = filament_types[0];
|
||||
BOOST_LOG_TRIVIAL(debug) << "SupportSpotsGenerator: applying filament type: " << filament_type;
|
||||
}
|
||||
}
|
||||
|
||||
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
|
||||
const float bridge_distance = 16.0f; // mm
|
||||
const float max_acceleration; // mm/s^2 ; max acceleration of object in XY -- should be applicable only to printers with bed slinger,
|
||||
// however we do not have such info yet. The force is usually small anyway, so not such a big deal to include it everytime
|
||||
const int raft_layers_count;
|
||||
std::string filament_type;
|
||||
|
||||
BrimType brim_type;
|
||||
const float brim_width;
|
||||
|
||||
const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.5, 1.1 };
|
||||
const float max_curled_height_factor = 10.0f;
|
||||
|
||||
const float min_distance_between_support_points = 3.0f; //mm
|
||||
const float support_points_interface_radius = 1.5f; // mm
|
||||
const float min_distance_to_allow_local_supports = 1.0f; //mm
|
||||
|
||||
const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position.
|
||||
const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important
|
||||
const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials.
|
||||
const float standard_extruder_conflict_force = 10.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... );
|
||||
const float malformations_additive_conflict_extruder_force = 65.0f * gravity_constant; // for areas with possible high layered curled filaments
|
||||
|
||||
// MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface
|
||||
double get_bed_adhesion_yield_strength() const {
|
||||
if (raft_layers_count > 0) {
|
||||
return get_support_spots_adhesion_strength() * 2.0;
|
||||
}
|
||||
|
||||
if (filament_type == "PLA") {
|
||||
return 0.02 * 1e6;
|
||||
} else if (filament_type == "PET" || filament_type == "PETG") {
|
||||
return 0.3 * 1e6;
|
||||
} else if (filament_type == "ABS" || filament_type == "ASA") {
|
||||
return 0.1 * 1e6; //TODO do measurements
|
||||
} else { //PLA default value - defensive approach, PLA has quite low adhesion
|
||||
return 0.02 * 1e6;
|
||||
}
|
||||
}
|
||||
|
||||
double get_support_spots_adhesion_strength() const {
|
||||
return 0.016f * 1e6;
|
||||
}
|
||||
};
|
||||
|
||||
enum class SupportPointCause {
|
||||
LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length
|
||||
FloatingBridgeAnchor, // point generated on unsupported bridge endpoint
|
||||
FloatingExtrusion, // point generated on extrusion that does not hold on its own
|
||||
SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a risk of separation (brim may help)
|
||||
UnstableFloatingPart, // point generated for object parts not connected to the bed, holded only by the other support points (brim will not help here)
|
||||
WeakObjectPart // point generated when some part of the object is too weak to hold the upper part and may break (imagine hourglass)
|
||||
};
|
||||
|
||||
// The support points can be sorted into two groups
|
||||
// 1. Local extrusion support for extrusions that are printed in the air and would not
|
||||
// withstand on their own (too long bridges, sharp turns in large overhang, concave bridge holes, etc.)
|
||||
// These points have negative force (-EPSILON) and Vec2f::Zero() direction
|
||||
// The algorithm still expects that these points will be supported and accounts for them in the global stability check.
|
||||
// 2. Global stability support points are generated at each spot, where the algorithm detects that extruding the current line
|
||||
// may cause separation of the object part from the bed and/or its support spots or crack in the weak connection of the object parts.
|
||||
// The generated point's direction is the estimated falling direction of the object part, and the force is equal to te difference
|
||||
// between forces that destabilize the object (extruder conflicts with curled filament, weight if instable center of mass, bed movements etc)
|
||||
// and forces that stabilize the object (bed adhesion, other support spots adhesion, weight if stable center of mass).
|
||||
// Note that the force is only the difference - the amount needed to stabilize the object again.
|
||||
struct SupportPoint
|
||||
{
|
||||
SupportPoint(SupportPointCause cause, const Vec3f &position, float force, float spot_radius, const Vec2f &direction)
|
||||
: cause(cause), position(position), force(force), spot_radius(spot_radius), direction(direction)
|
||||
{}
|
||||
|
||||
bool is_local_extrusion_support() const
|
||||
{
|
||||
return cause == SupportPointCause::LongBridge || cause == SupportPointCause::FloatingExtrusion;
|
||||
}
|
||||
bool is_global_object_support() const { return !is_local_extrusion_support(); }
|
||||
|
||||
SupportPointCause cause; // reason why this support point was generated. Used for the user alerts
|
||||
// position is in unscaled coords. The z coordinate is aligned with the layers bottom_z coordiantes
|
||||
Vec3f position;
|
||||
// force that destabilizes the object to the point of falling/breaking. g*mm/s^2 units
|
||||
// It is valid only for global_object_support. For local extrusion support points, the force is -EPSILON
|
||||
// values gathered from large XL model: Min : 0 | Max : 18713800 | Average : 1361186 | Median : 329103
|
||||
// For reference 18713800 is weight of 1.8 Kg object, 329103 is weight of 0.03 Kg
|
||||
// The final sliced object weight was approx 0.5 Kg
|
||||
float force;
|
||||
// Expected spot size. The support point strength is calculated from the area defined by this value.
|
||||
// Currently equal to the support_points_interface_radius parameter above
|
||||
float spot_radius;
|
||||
// direction of the fall of the object (z part is neglected)
|
||||
Vec2f direction;
|
||||
};
|
||||
|
||||
using SupportPoints = std::vector<SupportPoint>;
|
||||
|
||||
struct Malformations {
|
||||
std::vector<Lines> layers; //for each layer
|
||||
};
|
||||
|
||||
struct PartialObject
|
||||
{
|
||||
PartialObject(Vec3f centroid, float volume, bool connected_to_bed)
|
||||
: centroid(centroid), volume(volume), connected_to_bed(connected_to_bed)
|
||||
{}
|
||||
|
||||
Vec3f centroid;
|
||||
float volume;
|
||||
bool connected_to_bed;
|
||||
};
|
||||
|
||||
using PartialObjects = std::vector<PartialObject>;
|
||||
|
||||
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms);
|
||||
|
||||
void estimate_supports_malformations(std::vector<SupportLayer *> &layers, float supports_flow_width, const Params ¶ms);
|
||||
void estimate_malformations(std::vector<Layer *> &layers, const Params ¶ms);
|
||||
|
||||
|
||||
// NOTE: the boolean marks if the issue is critical or not for now.
|
||||
std::vector<std::pair<SupportPointCause, bool>> gather_issues(const SupportPoints &support_points,
|
||||
PartialObjects &partial_objects);
|
||||
|
||||
}} // namespace Slic3r::SupportSpotsGenerator
|
||||
*/
|
||||
#endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */
|
|
@ -323,31 +323,30 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
|||
|
||||
//BBS
|
||||
if (config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne &&
|
||||
config->opt_bool("enable_overhang_speed"))
|
||||
config->opt_bool("overhang_speed_classic"))
|
||||
{
|
||||
wxString msg_text = _(L("Arachne engine only works when overhang slowing down is disabled.\n"
|
||||
"This may cause decline in the quality of overhang surface when print fastly")) + "\n";
|
||||
wxString msg_text = _(L("Arachne engine doesn't work with classic overhang speed mode.\n")) + "\n";
|
||||
if (is_global_config)
|
||||
msg_text += "\n" + _(L("Disable overhang slowing down automatically? \n"
|
||||
"Yes - Enable arachne and disable overhang slowing down\n"
|
||||
msg_text += "\n" + _(L("Turn off classic mode automatically? \n"
|
||||
"Yes - Enable arachne with classic mode off\n"
|
||||
"No - Give up using arachne this time"));
|
||||
MessageDialog dialog(m_msg_dlg_parent, msg_text, "",
|
||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
auto answer = dialog.ShowModal();
|
||||
bool enable_overhang_slow_down = true;
|
||||
bool enable_overhang_slow_down_legacy = false;
|
||||
if (!is_global_config || answer == wxID_YES) {
|
||||
new_conf.set_key_value("enable_overhang_speed", new ConfigOptionBool(false));
|
||||
enable_overhang_slow_down = false;
|
||||
new_conf.set_key_value("overhang_speed_classic", new ConfigOptionBool(false));
|
||||
enable_overhang_slow_down_legacy = true;
|
||||
}
|
||||
else {
|
||||
new_conf.set_key_value("wall_generator", new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Classic));
|
||||
}
|
||||
apply(config, &new_conf);
|
||||
if (cb_value_change) {
|
||||
if (!enable_overhang_slow_down)
|
||||
cb_value_change("enable_overhang_speed", false);
|
||||
if (!enable_overhang_slow_down_legacy)
|
||||
cb_value_change("overhang_speed_classic", false);
|
||||
}
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
@ -639,7 +638,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
|||
toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters);
|
||||
|
||||
bool has_overhang_speed = config->opt_bool("enable_overhang_speed");
|
||||
for (auto el : { "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"})
|
||||
for (auto el :
|
||||
{"overhang_speed_classic", "overhang_1_4_speed",
|
||||
"overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"})
|
||||
toggle_line(el, has_overhang_speed);
|
||||
|
||||
toggle_line("flush_into_objects", !is_global_config);
|
||||
|
@ -657,7 +658,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
|||
"min_feature_size", "min_bead_width", "wall_distribution_count" })
|
||||
toggle_line(el, have_arachne);
|
||||
toggle_field("detect_thin_wall", !have_arachne);
|
||||
toggle_field("enable_overhang_speed", !have_arachne);
|
||||
toggle_field("only_one_wall_top", !have_arachne);
|
||||
|
||||
// SoftFever
|
||||
|
|
|
@ -98,7 +98,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CAT
|
|||
{"infill_combination", "",1}, {"infill_wall_overlap", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
|
||||
}},
|
||||
{ L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5},
|
||||
{"enable_overhang_speed", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},
|
||||
{"enable_overhang_speed", "",6}, {"overhang_speed_classic", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},
|
||||
{"bridge_speed", "",11}, {"gap_infill_speed", "",12}
|
||||
}}
|
||||
};
|
||||
|
|
|
@ -2515,7 +2515,7 @@ void MainFrame::init_menubar_as_editor()
|
|||
},
|
||||
"", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More"));
|
||||
m_topbar->GetCalibMenu()->AppendSubMenu(advance_menu, _L("More..."));
|
||||
|
||||
// help
|
||||
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Tutorial"), _L("Calibration help"),
|
||||
|
@ -2581,7 +2581,7 @@ void MainFrame::init_menubar_as_editor()
|
|||
m_vfa_test_dlg->ShowModal();
|
||||
}, "", nullptr,
|
||||
[this]() {return m_plater->is_view3D_shown();; }, this);
|
||||
append_submenu(calib_menu, advance_menu, wxID_ANY, _L("More"), _L("More calibrations"), "",
|
||||
append_submenu(calib_menu, advance_menu, wxID_ANY, _L("More..."), _L("More calibrations"), "",
|
||||
[this]() {return m_plater->is_view3D_shown();; });
|
||||
// help
|
||||
append_menu_item(calib_menu, wxID_ANY, _L("Tutorial"), _L("Calibration help"),
|
||||
|
|
|
@ -1919,7 +1919,12 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("sparse_infill_speed");
|
||||
optgroup->append_single_option_line("internal_solid_infill_speed");
|
||||
optgroup->append_single_option_line("top_surface_speed");
|
||||
optgroup->append_single_option_line("gap_infill_speed");
|
||||
optgroup->append_single_option_line("support_speed");
|
||||
optgroup->append_single_option_line("support_interface_speed");
|
||||
optgroup = page->new_optgroup(L("Overhang speed"), L"param_speed", 15);
|
||||
optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang");
|
||||
optgroup->append_single_option_line("overhang_speed_classic", "slow-down-for-overhang");
|
||||
Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") };
|
||||
line.label_path = "slow-down-for-overhang";
|
||||
line.append_option(optgroup->get_option("overhang_1_4_speed"));
|
||||
|
@ -1928,9 +1933,6 @@ void TabPrint::build()
|
|||
line.append_option(optgroup->get_option("overhang_4_4_speed"));
|
||||
optgroup->append_line(line);
|
||||
optgroup->append_single_option_line("bridge_speed");
|
||||
optgroup->append_single_option_line("gap_infill_speed");
|
||||
optgroup->append_single_option_line("support_speed");
|
||||
optgroup->append_single_option_line("support_interface_speed");
|
||||
|
||||
optgroup = page->new_optgroup(L("Travel speed"), L"param_travel_speed", 15);
|
||||
optgroup->append_single_option_line("travel_speed");
|
||||
|
@ -1945,7 +1947,7 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("accel_to_decel_enable");
|
||||
optgroup->append_single_option_line("accel_to_decel_factor");
|
||||
|
||||
optgroup = page->new_optgroup(L("Jerk(XY)"));
|
||||
optgroup = page->new_optgroup(L("Jerk(XY)"), L"param_speed", 15);
|
||||
optgroup->append_single_option_line("default_jerk");
|
||||
optgroup->append_single_option_line("outer_wall_jerk");
|
||||
optgroup->append_single_option_line("inner_wall_jerk");
|
||||
|
|
|
@ -238,7 +238,7 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
|||
// end temp
|
||||
auto end_temp_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto end_temp_text = new wxStaticText(this, wxID_ANY, end_temp_str, wxDefaultPosition, st_size, wxALIGN_LEFT);
|
||||
m_tiEnd = new TextInput(this, std::to_string(200), _L("\u2103"), "", wxDefaultPosition, ti_size, wxTE_CENTRE);
|
||||
m_tiEnd = new TextInput(this, std::to_string(190), _L("\u2103"), "", wxDefaultPosition, ti_size, wxTE_CENTRE);
|
||||
m_tiStart->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
end_temp_sizer->Add(end_temp_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
end_temp_sizer->Add(m_tiEnd, 0, wxALL | wxALIGN_CENTER_VERTICAL, 2);
|
||||
|
@ -338,11 +338,11 @@ void Temp_Calibration_Dlg::on_filament_type_changed(wxCommandEvent& event) {
|
|||
switch(selection)
|
||||
{
|
||||
case tABS_ASA:
|
||||
start = 260;
|
||||
start = 270;
|
||||
end = 230;
|
||||
break;
|
||||
case tPETG:
|
||||
start = 250;
|
||||
start = 260;
|
||||
end = 230;
|
||||
break;
|
||||
case tTPU:
|
||||
|
@ -360,7 +360,7 @@ void Temp_Calibration_Dlg::on_filament_type_changed(wxCommandEvent& event) {
|
|||
case tPLA:
|
||||
case tCustom:
|
||||
start = 230;
|
||||
end = 200;
|
||||
end = 190;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue