Compare commits

...

56 Commits

Author SHA1 Message Date
QIDI TECH
1f9400ddda The fan parameters are optimized 2023-10-07 13:30:00 +08:00
sunsets
d6408bf239 Update GCode.cpp 2023-10-06 10:10:46 +08:00
sunsets
d2012d4d9b gcodeviewer icons 2023-10-05 18:20:52 +08:00
sunsets
2b80442677 Update qidiparts.cpp 2023-10-05 18:00:41 +08:00
sunsets
494970573e Update GCode.cpp 2023-10-05 10:57:51 +08:00
sunsets
4ee031407d Merge branches 'main' and 'main' of https://github.com/QIDITECH/QIDISlicer 2023-10-04 15:26:05 +08:00
sunsets
f713cb579a object_range 2023-10-04 15:23:05 +08:00
QIDI TECH
7f19806dd9 Optimal PA calibration and add guide;Z seam is visible by default in preview 2023-09-28 10:04:57 +08:00
sunsets
f4f63b9a54 Merge branch 'main' of https://github.com/QIDITECH/QIDISlicer 2023-09-26 13:39:53 +08:00
sunsets
07b20e3c40 default_material 2023-09-26 13:38:41 +08:00
QIDI TECH
535ce1e4d5 Add MakerWorld 2023-09-25 08:38:20 +08:00
QIDI TECH
478a3cabc4 Update P2 P3 fan gcode 2023-09-23 13:41:54 +08:00
QIDI TECH
40e1a5534e Fix bug 2023-09-23 09:08:15 +08:00
sunsets
019c6dbb10 update first layer speed and rapid fan speed 2023-09-22 14:38:07 +08:00
sunsets
3e70923483 disable_rapid_cooling_fan_first_layers 2023-09-22 09:40:56 +08:00
QIDI TECH
d1902fa3d5 Merge branch 'main' of https://github.com/QIDITECH/QIDISlicer 2023-09-22 08:51:54 +08:00
QIDI TECH
424a37a68c Update PA Pattern 2023-09-22 08:51:49 +08:00
sunsets
1b2437fa02 Update GCode.cpp 2023-09-21 20:07:19 +08:00
sunsets
1687c5d624 exclude_object 2023-09-21 19:45:51 +08:00
sunsets
b1ff57de34 first_layer_speed 2023-09-21 16:50:10 +08:00
QIDI TECH
bccab56220 Update Plater.cpp 2023-09-21 09:54:44 +08:00
QIDI TECH
8ecf30a51d Update Plater.cpp 2023-09-18 20:32:50 +08:00
sunsets
cf13b6b6ab optimize calib 2023-09-18 16:30:28 +08:00
sunsets
1c85f23870 optimize calib 2023-09-18 16:18:31 +08:00
sunsets
35c97ae363 update win10Sdk and calib 2023-09-18 10:22:22 +08:00
QIDI TECH
963e22db99 Merge prusa 2.6.1 2023-09-16 16:26:29 +08:00
76Octane
1338e60f8b Delete github_test.txt 2023-09-16 15:42:32 +08:00
76Octane
a6c966ffd8 Update github_test.txt 2023-09-16 15:39:54 +08:00
76Octane
2c2e8aaab2 Create github_test.txt 2023-09-16 15:36:46 +08:00
sunsets
65a245af41 num_text 2023-09-15 19:25:28 +08:00
QIDI TECH
f2ea046ab5 Merge branch 'main' of https://github.com/QIDITECH/QIDISlicer 2023-09-14 15:07:29 +08:00
QIDI TECH
313d89a44f Update UserGuider 2023-09-14 15:07:07 +08:00
sunsets
5dc99e0fdd calib_flowrate 2023-09-14 10:00:20 +08:00
sunsets
f2a36c90c1 calib 2023-09-13 09:00:55 +08:00
QIDI TECH
1d73b29a00 Update Plater.cpp 2023-09-11 11:13:13 +08:00
sunsets
39bb8169a0 linux 2023-09-11 11:10:12 +08:00
QIDI TECH
205d475acb Update README.md 2023-09-06 08:29:51 +08:00
QIDI TECH
a8371bbded Merge branch 'main' of https://github.com/QIDITECH/QIDISlicer 2023-09-02 15:32:01 +08:00
QIDI TECH
261cd7d6e9 Optimized PressureAdvance 2023-09-02 15:31:11 +08:00
76Octane
c4f8a7808a Update Guide 2023-09-02 15:13:46 +08:00
QIDI TECH
a2b2cf743e Optimized PressureAdvance 2023-09-01 20:05:08 +08:00
76Octane
07e2346d8d update 2023-08-31 18:26:57 +08:00
QIDI TECH
0a6a5f8690 Optimized PressureAdvance 2023-08-31 16:25:40 +08:00
QIDI TECH
51c4567a09 Optimized FlowRate 2023-08-31 10:30:12 +08:00
76Octane
0b9b50fa22 Update calib_dlg.cpp 2023-08-31 09:32:08 +08:00
QIDI TECH
1cceb727d7 Optimized FlowRate 2023-08-30 17:11:44 +08:00
76Octane
49f599efbf Update calib_dlg 2023-08-30 16:53:46 +08:00
QIDI TECH
140ee733ca Merge branch 'main' of https://github.com/QIDITECH/QIDISlicer 2023-08-30 11:16:08 +08:00
QIDI TECH
3d89653ad6 Update QIDITechnology.ini 2023-08-30 11:15:56 +08:00
76Octane
59aa772875 Update calib_dlg
1.Add a dlg for flowrate-fine.
2.Add the notice when using the calibration feature.
3.Optimized parameter settings.
2023-08-30 11:12:12 +08:00
76Octane
a7ad626cdd Update config of flowrate 2023-08-29 14:14:27 +08:00
QIDI TECH
0886a00cae FlowRate 2023-08-26 14:52:45 +08:00
sunsets
e452f65575 Calibratioon 2023-08-25 09:36:16 +08:00
QIDI TECH
a867b747e6 Update QIDITechnology.ini 2023-08-23 13:52:28 +08:00
sunsets
8c0ec030e1 arrange distance 2023-08-23 13:05:26 +08:00
QIDI TECH
f39d64153a Version update 2023-08-23 11:37:36 +08:00
290 changed files with 68832 additions and 7606 deletions

View File

@@ -26,7 +26,6 @@ endif()
option(SLIC3R_STATIC "Compile QIDISlicer with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL}) option(SLIC3R_STATIC "Compile QIDISlicer with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL})
option(SLIC3R_GUI "Compile QIDISlicer with GUI components (OpenGL, wxWidgets)" 1) option(SLIC3R_GUI "Compile QIDISlicer with GUI components (OpenGL, wxWidgets)" 1)
option(SLIC3R_FHS "Assume QIDISlicer is to be installed in a FHS directory structure" 0) option(SLIC3R_FHS "Assume QIDISlicer is to be installed in a FHS directory structure" 0)
option(SLIC3R_WX_STABLE "Build against wxWidgets stable (3.0) as oppsed to dev (3.1) on Linux" 0)
option(SLIC3R_PCH "Use precompiled headers" 1) option(SLIC3R_PCH "Use precompiled headers" 1)
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
@@ -60,7 +59,6 @@ if (APPLE)
endif () endif ()
endif () endif ()
# Proposal for C++ unit tests and sandboxes
option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF) option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF)
option(SLIC3R_BUILD_TESTS "Build unit tests" ON) option(SLIC3R_BUILD_TESTS "Build unit tests" ON)
@@ -172,8 +170,8 @@ if(WIN32)
endif() endif()
endif() endif()
if(DEFINED WIN10SDK_PATH) if(DEFINED WIN10SDK_PATH)
if (EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") if (EXISTS "${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h")
set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}/Include") set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}")
else() else()
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")

View File

@@ -1,21 +1,21 @@
1.0.2 1.0.5
alpha=1.0.2-alpha1 alpha=1.0.5-alpha1
beta=1.0.2-beta1 beta=1.0.5-beta1
[common] [common]
release = 1.0.2 release = 1.0.5
alpha = 1.0.2-alpha1 alpha = 1.0.5-alpha1
beta = 1.0.2-beta1 beta = 1.0.5-beta1
rc = 1.0.2-rc2 rc = 1.0.5-rc2
[release:win64] [release:win64]
url = https://github.com/QIDITECH/QIDISlicer/releases/download/V1.0.2/QIDISlicer_Setup_1.0.2.exe url = https://github.com/QIDITECH/QIDISlicer/releases/download/V1.0.5/QIDISlicer_Setup_1.0.5_Win64.exe
size = 71118761 size = 71118761
[release:linux] [release:linux]
url = https://qidi3d.com url = https://github.com/QIDITECH/QIDISlicer/releases/download/V1.0.4/QIDISlicer-1.0.4.AppImage
size = 69915840 size = 69915840
[release:osx] [release:osx]
url = https://qidi3d.com/ url = https://github.com/QIDITECH/QIDISlicer/releases/download/V1.0.5/QIDISlicer_Setup_1.0.5_Win64.exe
size = 90237989 size = 90237989

View File

@@ -7,7 +7,7 @@ QIDISlicer is a professional 3D printer slicing softwarewhich is perfectly co
**Notice:QIDISlicer as a new software dedicated to QIDI's new high speed printers, please make sure your firmware version is V 4.0.0 or above.** **Notice:QIDISlicer as a new software dedicated to QIDI's new high speed printers, please make sure your firmware version is V 4.0.0 or above.**
QIDISlicer is based on [PrusaSlicer](https://github.com/prusa3d/PrusaSlicer) by Prusa Research, which is from [Slic3r](https://github.com/Slic3r/Slic3r) by Alessandro Ranellucci and the RepRap community. QIDISlicer is based on [PrusaSlicer](https://github.com/prusa3d/PrusaSlicer) by Prusa Research, which is from [Slic3r](https://github.com/Slic3r/Slic3r) by Alessandro Ranellucci and the RepRap community.
Thanks to PrusaSlicer and Bambulab for their contributions to the 3D printing community. Thanks to PrusaSlicer, Bambulab and OrcaSlicer for their contributions to the 3D printing community.
See the [QIDI's homepage](https://qidi3d.com) for more information. See the [QIDI's homepage](https://qidi3d.com) for more information.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#009688"/>
<path d="M4 9L14 9" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#CECECE"/>
<line x1="4.37881" y1="8.93516" x2="13.6213" y2="8.93515" stroke="white" stroke-width="1.2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#4db6ac"/>
<path d="M4 9L14 9" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 232 B

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="1.5" width="15" height="15" rx="0.5" stroke="#ACACAC"/>
</svg>

After

Width:  |  Height:  |  Size: 176 B

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="1.5" width="15" height="15" rx="0.5" fill="#CECECE" stroke="#ACACAC"/>
</svg>

After

Width:  |  Height:  |  Size: 191 B

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.5" y="1.5" width="15" height="15" rx="0.5" stroke="#009688"/>
</svg>

After

Width:  |  Height:  |  Size: 176 B

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#009688"/>
<path d="M4.35537 8.5374L8.30284 11.9361C8.71855 12.294 9.34501 12.2502 9.70687 11.838L14.8175 6.01521" stroke="white" stroke-width="1.2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#CECECE"/>
<path d="M4.35537 8.5374L8.30284 11.9361C8.71855 12.294 9.34501 12.2502 9.70687 11.838L14.8175 6.01521" stroke="white" stroke-width="1.2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="16" height="16" rx="1" fill="#4db6ac"/>
<path d="M4.35537 8.5374L8.30284 11.9361C8.71855 12.294 9.34501 12.2502 9.70687 11.838L14.8175 6.01521" stroke="white" stroke-width="1.2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 8.9375L6.45062 11.6772C6.67263 11.8535 6.99663 11.8101 7.16447 11.5817L12 5" stroke="white" stroke-linecap="round"/>
<path d="M3 8.9375L6.45062 11.6772C6.67263 11.8535 6.99663 11.8101 7.16447 11.5817L12 5" stroke="white" stroke-opacity="0.2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 382 B

7
resources/icons/snap.svg Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path fill="none" stroke="#808080" stroke-linecap="round" stroke-miterlimit="10" d="m1.5,11.5V3.5c0-1.104569435119629.895430564880371-2,2-2h9c1.104569435119629,0,2,.895430564880371,2,2v8" />
<line x1="1.5" y1="14.5" x2="14.5" y2="14.5" style="fill:none; stroke:#ed6b21; stroke-linecap:round; stroke-miterlimit:10;"/>
<path d="m7,15h-3V5.237499999999272c0-.683452377914364.447715250171314-1.237499999999272,1-1.237499999999272h2v11Z" style="fill:#ed6b21; stroke-width:0px;"/>
<path d="m9,15h3V5.237499999999272c0-.683452377914364-.447715250171314-1.237499999999272-1-1.237499999999272h-2v11Z" style="fill:#ed6b21; stroke-width:0px;"/>
</svg>

After

Width:  |  Height:  |  Size: 755 B

View File

@@ -1,3 +1,5 @@
min_slic3r_version = 1.0.5
1.0.5 Optimize parameters
min_slic3r_version = 1.0.4 min_slic3r_version = 1.0.4
1.0.4 Modify start code 1.0.4 Modify start code
min_slic3r_version = 1.0.3 min_slic3r_version = 1.0.3

View File

@@ -4,8 +4,8 @@
# Vendor name will be shown by the Config Wizard. # Vendor name will be shown by the Config Wizard.
name = QIDI Technology name = QIDI Technology
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the QIDISlicer configuration to be downgraded.
config_version = 1.0.4 config_version = 1.0.6
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://github.com/QIDITECH/QIDISlicer/releases/download/QIDITechnology/ config_update_url = https://github.com/QIDITECH/QIDISlicer/releases/download/QIDITechnology/
changelog_url = https://qidi3d.com/pages/software-firmware changelog_url = https://qidi3d.com/pages/software-firmware
@@ -21,7 +21,7 @@ technology = FFF
family = X family = X
bed_model = X-MAX 3_bed.stl bed_model = X-MAX 3_bed.stl
bed_texture = X-MAX 3.svg bed_texture = X-MAX 3.svg
default_materials = QIDI ABS Rapido @X-MAX 3; QIDI PLA Rapido @X-MAX 3; QIDI PLA Rapido Matte @X-MAX 3; QIDI ToughPETG @X-MAX 3; Generic ABS @X-MAX 3; Generic PETG @X-MAX 3; Generic PLA @X-MAX 3 default_materials = QIDI ABS Odorless @X-MAX 3; QIDI ABS Rapido @X-MAX 3; QIDI ASA @X-MAX 3; QIDI PLA Rapido @X-MAX 3; QIDI PLA Rapido Matte @X-MAX 3; QIDI ToughPETG @X-MAX 3; Generic ABS @X-MAX 3; Generic PETG @X-MAX 3; Generic PLA @X-MAX 3
[printer_model:X-Plus 3] [printer_model:X-Plus 3]
name = X-Plus 3 name = X-Plus 3
@@ -30,7 +30,7 @@ technology = FFF
family = X family = X
bed_model = X-Plus 3_bed.stl bed_model = X-Plus 3_bed.stl
bed_texture = X-Plus 3.svg bed_texture = X-Plus 3.svg
default_materials = QIDI ABS Rapido @X-Plus 3; QIDI PLA Rapido @X-Plus 3; QIDI PLA Rapido Matte @X-Plus 3; QIDI ToughPETG @X-Plus 3; Generic ABS @X-Plus 3; Generic PETG @X-Plus 3; Generic PLA @X-Plus 3 default_materials = QIDI ABS Odorless @X-Plus 3; QIDI ABS Rapido @X-Plus 3; QIDI ASA @X-Plus 3; QIDI PLA Rapido @X-Plus 3; QIDI PLA Rapido Matte @X-Plus 3; QIDI ToughPETG @X-Plus 3; Generic ABS @X-Plus 3; Generic PETG @X-Plus 3; Generic PLA @X-Plus 3
[printer_model:X-smart 3] [printer_model:X-smart 3]
name = X-smart 3 name = X-smart 3
@@ -39,7 +39,7 @@ technology = FFF
family = X family = X
bed_model = X-smart 3_bed.stl bed_model = X-smart 3_bed.stl
bed_texture = X-smart 3.svg bed_texture = X-smart 3.svg
default_materials = QIDI ABS Rapido @X-smart 3; QIDI PLA Rapido @X-smart 3; QIDI PLA Rapido Matte @X-smart 3; QIDI ToughPETG @X-smart 3; Generic ABS @X-smart 3; Generic PETG @X-smart 3; Generic PLA @X-smart 3 default_materials = QIDI ABS Odorless @X-smart 3;QIDI ABS Rapido @X-smart 3; QIDI ASA @X-smart 3; QIDI PLA Rapido @X-smart 3; QIDI PLA Rapido Matte @X-smart 3; QIDI ToughPETG @X-smart 3; Generic ABS @X-smart 3; Generic PETG @X-smart 3; Generic PLA @X-smart 3
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@@ -83,15 +83,17 @@ first_layer_acceleration = 500
first_layer_acceleration_over_raft = 500 first_layer_acceleration_over_raft = 500
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
first_layer_height = 0.2 first_layer_height = 0.2
first_layer_speed = 55 first_layer_infill_speed = 100
first_layer_speed = 50
first_layer_speed_over_raft = 30 first_layer_speed_over_raft = 30
first_layer_travel_speed = 300
fuzzy_skin = none fuzzy_skin = none
fuzzy_skin_point_dist = 0.8 fuzzy_skin_point_dist = 0.8
fuzzy_skin_thickness = 0.3 fuzzy_skin_thickness = 0.3
gap_fill_enabled = 0 gap_fill_enabled = 0
gap_fill_speed = 250 gap_fill_speed = 250
gcode_comments = 0 gcode_comments = 0
gcode_label_objects = 0 gcode_label_objects = 1
gcode_resolution = 0.0125 gcode_resolution = 0.0125
gcode_substitutions = gcode_substitutions =
infill_acceleration = 0 infill_acceleration = 0
@@ -192,8 +194,8 @@ support_tree_branch_diameter_double_wall = 3
support_tree_branch_distance = 1 support_tree_branch_distance = 1
support_tree_tip_diameter = 0.8 support_tree_tip_diameter = 0.8
support_tree_top_rate = 15% support_tree_top_rate = 15%
thick_bridges = 1 thick_bridges = 0
thin_walls = 1 thin_walls = 0
threads = 12 threads = 12
top_fill_pattern = monotoniclines top_fill_pattern = monotoniclines
top_infill_extrusion_width = 0 top_infill_extrusion_width = 0
@@ -263,7 +265,7 @@ solid_infill_speed = 230
# Common filament preset # Common filament preset
[filament:*common*] [filament:*common*]
advance_pressure = 0.031 advance_pressure = 0.031
bed_temperature = 60 bed_temperature = 55
bridge_fan_speed = 100 bridge_fan_speed = 100
compatible_printers = compatible_printers =
compatible_printers_condition = compatible_printers_condition =
@@ -271,6 +273,7 @@ compatible_prints =
compatible_prints_condition = compatible_prints_condition =
cooling = 1 cooling = 1
disable_fan_first_layers = 1 disable_fan_first_layers = 1
disable_rapid_cooling_fan_first_layers = 3
enable_advance_pressure = 1 enable_advance_pressure = 1
enable_auxiliary_fan = 100 enable_auxiliary_fan = 100
enable_dynamic_fan_speeds = 0 enable_dynamic_fan_speeds = 0
@@ -303,7 +306,7 @@ filament_retract_lift_above = nil
filament_retract_lift_below = nil filament_retract_lift_below = nil
filament_retract_restart_extra = nil filament_retract_restart_extra = nil
filament_retract_speed = nil filament_retract_speed = nil
filament_settings_id = PrusaSlicer_cf_pro.ini filament_settings_id =
filament_soluble = 0 filament_soluble = 0
filament_spool_weight = 0 filament_spool_weight = 0
filament_toolchange_delay = 0 filament_toolchange_delay = 0
@@ -313,7 +316,7 @@ filament_unloading_speed = 90
filament_unloading_speed_start = 100 filament_unloading_speed_start = 100
filament_vendor = QIDI filament_vendor = QIDI
filament_wipe = nil filament_wipe = nil
first_layer_bed_temperature = 60 first_layer_bed_temperature = 55
first_layer_temperature = 210 first_layer_temperature = 210
first_layer_volume_temperature = 0 first_layer_volume_temperature = 0
full_fan_speed_layer = 0 full_fan_speed_layer = 0
@@ -328,26 +331,60 @@ overhang_fan_speed_2 = 0
overhang_fan_speed_3 = 0 overhang_fan_speed_3 = 0
slowdown_below_layer_time = 5 slowdown_below_layer_time = 5
smooth_time = 0.03 smooth_time = 0.03
start_filament_gcode = "; Filament gcode\n" start_filament_gcode = "; Filament gcode\n{if (enable_advance_pressure[0] == true)}M900{else};M900{endif} K[advance_pressure] T[smooth_time]\n"
temperature = 220 temperature = 220
volume_temperature = 0 volume_temperature = 0
# QIDI filament preset # QIDI filament preset
[filament:*QIDI ABS Odorless*]
inherits = *common*
advance_pressure = 0.021
bed_temperature = 90
disable_fan_first_layers = 3
enable_auxiliary_fan = 0
enable_dynamic_fan_speeds = 1
enable_volume_fan = 40
extrusion_multiplier = 0.92
fan_below_layer_time = 30
filament_colour = #FFFACD
filament_density = 1.02
filament_max_volumetric_speed = 22
filament_type = ABS
first_layer_bed_temperature = 90
first_layer_temperature = 250
max_fan_speed = 50
min_fan_speed = 10
overhang_fan_speed_0 = 100
overhang_fan_speed_1 = 90
overhang_fan_speed_2 = 80
overhang_fan_speed_3 = 80
slowdown_below_layer_time = 4
temperature = 270
volume_temperature = 55
[filament:*QIDI ABS Rapido*] [filament:*QIDI ABS Rapido*]
inherits = *common* inherits = *common*
advance_pressure = 0.021 advance_pressure = 0.021
bed_temperature = 100 bed_temperature = 90
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
enable_dynamic_fan_speeds = 1
enable_volume_fan = 40 enable_volume_fan = 40
extrusion_multiplier = 0.95 extrusion_multiplier = 0.95
fan_below_layer_time = 30
filament_colour = #FFC800 filament_colour = #FFC800
filament_density = 1.02 filament_density = 1.02
filament_max_volumetric_speed = 22 filament_max_volumetric_speed = 22
filament_type = ABS filament_type = ABS
first_layer_bed_temperature = 100 first_layer_bed_temperature = 90
first_layer_temperature = 240 first_layer_temperature = 250
max_fan_speed = 60 max_fan_speed = 80
min_fan_speed = 60 min_fan_speed = 10
overhang_fan_speed_0 = 100
overhang_fan_speed_1 = 90
overhang_fan_speed_2 = 80
overhang_fan_speed_3 = 80
slowdown_below_layer_time = 4
temperature = 270 temperature = 270
volume_temperature = 55 volume_temperature = 55
@@ -356,6 +393,7 @@ inherits = *common*
advance_pressure = 0.01 advance_pressure = 0.01
bed_temperature = 100 bed_temperature = 100
bridge_fan_speed = 0 bridge_fan_speed = 0
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
enable_volume_fan = 0 enable_volume_fan = 0
extrusion_multiplier = 0.95 extrusion_multiplier = 0.95
@@ -370,72 +408,117 @@ min_fan_speed = 20
temperature = 270 temperature = 270
volume_temperature = 55 volume_temperature = 55
# QIDI filament preset
[filament:*QIDI ASA*]
inherits = *common*
advance_pressure = 0.021
bed_temperature = 90
disable_fan_first_layers = 3
enable_auxiliary_fan = 0
enable_dynamic_fan_speeds = 1
enable_volume_fan = 40
extrusion_multiplier = 0.92
fan_below_layer_time = 35
filament_colour = #F0E68C
filament_density = 1.02
filament_max_volumetric_speed = 16
filament_type = ABS
first_layer_bed_temperature = 90
first_layer_temperature = 250
max_fan_speed = 50
min_fan_speed = 10
overhang_fan_speed_0 = 100
overhang_fan_speed_1 = 80
overhang_fan_speed_2 = 60
overhang_fan_speed_3 = 40
slowdown_below_layer_time = 4
temperature = 270
volume_temperature = 55
[filament:*QIDI PA12-CF*] [filament:*QIDI PA12-CF*]
inherits = *common* inherits = *common*
advance_pressure = 0.01 advance_pressure = 0.01
bed_temperature = 80 bed_temperature = 80
bridge_fan_speed = 0 bridge_fan_speed = 40
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
extrusion_multiplier = 0.96 extrusion_multiplier = 0.96
fan_below_layer_time = 5
filament_colour = #696969 filament_colour = #696969
filament_density = 1.09 filament_density = 1.09
filament_max_volumetric_speed = 14 filament_max_volumetric_speed = 14
filament_type = PA12-CF filament_type = PA12-CF
first_layer_bed_temperature = 80 first_layer_bed_temperature = 80
first_layer_temperature = 290 first_layer_temperature = 290
max_fan_speed = 15 max_fan_speed = 30
min_fan_speed = 15 min_fan_speed = 10
slowdown_below_layer_time = 2
temperature = 290 temperature = 290
[filament:*QIDI PAHT-CF*] [filament:*QIDI PAHT-CF*]
inherits = *common* inherits = *common*
advance_pressure = 0.01 advance_pressure = 0.01
bed_temperature = 80 bed_temperature = 80
bridge_fan_speed = 0 bridge_fan_speed = 40
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
extrusion_multiplier = 0.96 extrusion_multiplier = 0.96
fan_below_layer_time = 5
filament_colour = #4F4F4F filament_colour = #4F4F4F
filament_density = 1.20 filament_density = 1.20
filament_max_volumetric_speed = 15 filament_max_volumetric_speed = 15
filament_type = PAHT-CF filament_type = PAHT-CF
first_layer_bed_temperature = 80 first_layer_bed_temperature = 80
first_layer_temperature = 310 first_layer_temperature = 310
max_fan_speed = 15 max_fan_speed = 30
min_fan_speed = 15 min_fan_speed = 10
slowdown_below_layer_time = 2
temperature = 310 temperature = 310
[filament:*QIDI PET-CF*] [filament:*QIDI PET-CF*]
inherits = *common* inherits = *common*
advance_pressure = 0.01 advance_pressure = 0.01
bed_temperature = 80 bed_temperature = 80
bridge_fan_speed = 5 bridge_fan_speed = 40
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
extrusion_multiplier = 1 extrusion_multiplier = 1
fan_below_layer_time = 5
filament_colour = #323232 filament_colour = #323232
filament_density = 1.30 filament_density = 1.30
filament_max_volumetric_speed = 14 filament_max_volumetric_speed = 14
filament_type = PET-CF filament_type = PET-CF
first_layer_bed_temperature = 80 first_layer_bed_temperature = 80
first_layer_temperature = 320 first_layer_temperature = 320
max_fan_speed = 10 max_fan_speed = 30
min_fan_speed = 10 min_fan_speed = 10
slowdown_below_layer_time = 2
temperature = 320 temperature = 320
[filament:*QIDI PETG-Tough*] [filament:*QIDI PETG-Tough*]
inherits = *common* inherits = *common*
advance_pressure = 0.07 advance_pressure = 0.07
bed_temperature = 80 bed_temperature = 80
bridge_fan_speed = 60 bridge_fan_speed = 90
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
enable_dynamic_fan_speeds = 1
extrusion_multiplier = 0.95 extrusion_multiplier = 0.95
fan_below_layer_time = 30
filament_colour = #00FF40 filament_colour = #00FF40
filament_density = 1.24 filament_density = 1.24
filament_max_volumetric_speed = 16 filament_max_volumetric_speed = 16
filament_type = PETG filament_type = PETG
first_layer_bed_temperature = 80 first_layer_bed_temperature = 80
first_layer_temperature = 240 first_layer_temperature = 240
max_fan_speed = 60 max_fan_speed = 40
min_fan_speed = 60 min_fan_speed = 10
min_print_speed = 10
overhang_fan_speed_0 = 90
overhang_fan_speed_1 = 70
overhang_fan_speed_2 = 50
overhang_fan_speed_3 = 40
slowdown_below_layer_time = 8
temperature = 250 temperature = 250
[filament:*QIDI PLA Rapido*] [filament:*QIDI PLA Rapido*]
@@ -451,7 +534,7 @@ filament_max_volumetric_speed = 22
inherits = *common* inherits = *common*
advance_pressure = 0.03 advance_pressure = 0.03
bed_temperature = 80 bed_temperature = 80
bridge_fan_speed = 0 bridge_fan_speed = 20
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
extrusion_multiplier = 0.96 extrusion_multiplier = 0.96
filament_colour = #FFEBCD filament_colour = #FFEBCD
@@ -468,27 +551,37 @@ temperature = 300
[filament:*Generic ABS*] [filament:*Generic ABS*]
inherits = *common* inherits = *common*
advance_pressure = 0.021 advance_pressure = 0.021
bed_temperature = 100 bed_temperature = 90
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
enable_dynamic_fan_speeds = 1
enable_volume_fan = 40 enable_volume_fan = 40
extrusion_multiplier = 0.95 extrusion_multiplier = 0.95
fan_below_layer_time = 30
filament_colour = #FFFF00 filament_colour = #FFFF00
filament_density = 1.04 filament_density = 1.04
filament_max_volumetric_speed = 17.5 filament_max_volumetric_speed = 17
filament_type = ABS filament_type = ABS
filament_vendor = Generic filament_vendor = Generic
first_layer_bed_temperature = 100 first_layer_bed_temperature = 90
first_layer_temperature = 240 first_layer_temperature = 250
max_fan_speed = 60 max_fan_speed = 80
min_fan_speed = 60 min_fan_speed = 10
overhang_fan_speed_0 = 100
overhang_fan_speed_1 = 90
overhang_fan_speed_2 = 80
overhang_fan_speed_3 = 80
slowdown_below_layer_time = 4
temperature = 260 temperature = 260
[filament:*Generic PETG*] [filament:*Generic PETG*]
inherits = *common* inherits = *common*
advance_pressure = 0.07 advance_pressure = 0.07
bed_temperature = 80 bed_temperature = 80
disable_fan_first_layers = 3
enable_auxiliary_fan = 0 enable_auxiliary_fan = 0
extrusion_multiplier = 0.95 extrusion_multiplier = 0.95
fan_below_layer_time = 30
filament_colour = #00FF00 filament_colour = #00FF00
filament_density = 1.27 filament_density = 1.27
filament_max_volumetric_speed = 9 filament_max_volumetric_speed = 9
@@ -496,20 +589,23 @@ filament_type = PETG
filament_vendor = Generic filament_vendor = Generic
first_layer_bed_temperature = 80 first_layer_bed_temperature = 80
first_layer_temperature = 240 first_layer_temperature = 240
max_fan_speed = 60 max_fan_speed = 90
min_fan_speed = 60 min_fan_speed = 40
min_print_speed = 10
slowdown_below_layer_time = 4
temperature = 250 temperature = 250
[filament:*Generic PLA*] [filament:*Generic PLA*]
inherits = *common* inherits = *common*
filament_colour = #0000FF filament_colour = #0000FF
filament_density = 1.20 filament_density = 1.20
filament_max_volumetric_speed = 17 filament_max_volumetric_speed = 14
filament_vendor = Generic filament_vendor = Generic
[filament:*Generic TPU 95A*] [filament:*Generic TPU 95A*]
inherits = *common* inherits = *common*
advance_pressure = 0.1 advance_pressure = 0.1
enable_auxiliary_fan = 0
extrusion_multiplier = 1 extrusion_multiplier = 1
filament_colour = #8000FF filament_colour = #8000FF
filament_density = 1.21 filament_density = 1.21
@@ -517,9 +613,15 @@ filament_max_volumetric_speed = 6
filament_type = TPU filament_type = TPU
filament_vendor = Generic filament_vendor = Generic
first_layer_temperature = 230 first_layer_temperature = 230
min_print_speed = 10
slowdown_below_layer_time = 8
temperature = 230 temperature = 230
# X-MAX 3 QIDI filament preset # X-MAX 3 QIDI filament preset
[filament:QIDI ABS Odorless @X-MAX 3]
inherits = *QIDI ABS Odorless*
compatible_printers_condition = printer_model=="X-MAX 3"
[filament:QIDI ABS Rapido @X-MAX 3] [filament:QIDI ABS Rapido @X-MAX 3]
inherits = *QIDI ABS Rapido* inherits = *QIDI ABS Rapido*
compatible_printers_condition = printer_model=="X-MAX 3" compatible_printers_condition = printer_model=="X-MAX 3"
@@ -528,11 +630,15 @@ compatible_printers_condition = printer_model=="X-MAX 3"
inherits = *QIDI ABS-GF* inherits = *QIDI ABS-GF*
compatible_printers_condition = printer_model=="X-MAX 3" compatible_printers_condition = printer_model=="X-MAX 3"
[filament:QIDI ASA @X-MAX 3]
inherits = *QIDI ASA*
compatible_printers_condition = printer_model=="X-MAX 3"
[filament:QIDI PA12-CF @X-MAX 3] [filament:QIDI PA12-CF @X-MAX 3]
inherits = *QIDI PA12-CF* inherits = *QIDI PA12-CF*
compatible_printers_condition = printer_model=="X-MAX 3" compatible_printers_condition = printer_model=="X-MAX 3"
[filament:QIDI PAHT-CF @X-Plus3] [filament:QIDI PAHT-CF @X-MAX 3]
inherits = *QIDI PAHT-CF* inherits = *QIDI PAHT-CF*
compatible_printers_condition = printer_model=="X-MAX 3" compatible_printers_condition = printer_model=="X-MAX 3"
@@ -574,6 +680,10 @@ inherits = *Generic TPU 95A*
compatible_printers_condition = printer_model=="X-MAX 3" compatible_printers_condition = printer_model=="X-MAX 3"
# X-Plus 3 QIDI filament preset # X-Plus 3 QIDI filament preset
[filament:QIDI ABS Odorless @X-Plus 3]
inherits = *QIDI ABS Odorless*
compatible_printers_condition = printer_model=="X-Plus 3"
[filament:QIDI ABS Rapido @X-Plus 3] [filament:QIDI ABS Rapido @X-Plus 3]
inherits = *QIDI ABS Rapido* inherits = *QIDI ABS Rapido*
compatible_printers_condition = printer_model=="X-Plus 3" compatible_printers_condition = printer_model=="X-Plus 3"
@@ -582,6 +692,10 @@ compatible_printers_condition = printer_model=="X-Plus 3"
inherits = *QIDI ABS-GF* inherits = *QIDI ABS-GF*
compatible_printers_condition = printer_model=="X-Plus 3" compatible_printers_condition = printer_model=="X-Plus 3"
[filament:QIDI ASA @X-Plus 3]
inherits = *QIDI ASA*
compatible_printers_condition = printer_model=="X-Plus 3"
[filament:QIDI PA12-CF @X-Plus 3] [filament:QIDI PA12-CF @X-Plus 3]
inherits = *QIDI PA12-CF* inherits = *QIDI PA12-CF*
compatible_printers_condition = printer_model=="X-Plus 3" compatible_printers_condition = printer_model=="X-Plus 3"
@@ -628,6 +742,13 @@ inherits = *Generic TPU 95A*
compatible_printers_condition = printer_model=="X-Plus 3" compatible_printers_condition = printer_model=="X-Plus 3"
# X-smart 3 QIDI filament preset # X-smart 3 QIDI filament preset
[filament:QIDI ABS Odorless @X-smart 3]
inherits = *QIDI ABS Odorless*
compatible_printers_condition = printer_model=="X-smart 3"
advance_pressure = 0.024
filament_max_volumetric_speed = 20
volume_temperature = 0
[filament:QIDI ABS Rapido @X-smart 3] [filament:QIDI ABS Rapido @X-smart 3]
inherits = *QIDI ABS Rapido* inherits = *QIDI ABS Rapido*
compatible_printers_condition = printer_model=="X-smart 3" compatible_printers_condition = printer_model=="X-smart 3"
@@ -635,6 +756,30 @@ advance_pressure = 0.024
filament_max_volumetric_speed = 20 filament_max_volumetric_speed = 20
volume_temperature = 0 volume_temperature = 0
[filament:QIDI ABS-GF @X-smart 3]
inherits = *QIDI ABS-GF*
compatible_printers_condition = printer_model=="X-smart 3"
volume_temperature = 0
[filament:QIDI ASA @X-smart 3]
inherits = *QIDI ASA*
compatible_printers_condition = printer_model=="X-smart 3"
advance_pressure = 0.024
filament_max_volumetric_speed = 20
volume_temperature = 0
[filament:QIDI PA12-CF @X-smart 3]
inherits = *QIDI PA12-CF*
compatible_printers_condition = printer_model=="X-smart 3"
[filament:QIDI PAHT-CF @X-smart 3]
inherits = *QIDI PAHT-CF*
compatible_printers_condition = printer_model=="X-smart 3"
[filament:QIDI PET-CF @X-smart 3]
inherits = *QIDI PET-CF*
compatible_printers_condition = printer_model=="X-smart 3"
[filament:QIDI PETG-Tough @X-smart 3] [filament:QIDI PETG-Tough @X-smart 3]
inherits = *QIDI PETG-Tough* inherits = *QIDI PETG-Tough*
compatible_printers_condition = printer_model=="X-smart 3" compatible_printers_condition = printer_model=="X-smart 3"
@@ -653,6 +798,10 @@ compatible_printers_condition = printer_model=="X-smart 3"
filament_max_volumetric_speed = 20 filament_max_volumetric_speed = 20
slowdown_below_layer_time = 8 slowdown_below_layer_time = 8
[filament:QIDI UltraPA @X-smart 3]
inherits = *QIDI UltraPA*
compatible_printers_condition = printer_model=="X-smart 3"
# X-smart 3 Generic filament preset # X-smart 3 Generic filament preset
[filament:Generic ABS @X-smart 3] [filament:Generic ABS @X-smart 3]
inherits = *Generic ABS* inherits = *Generic ABS*
@@ -669,13 +818,11 @@ filament_max_volumetric_speed = 8
[filament:Generic PLA @X-smart 3] [filament:Generic PLA @X-smart 3]
inherits = *Generic PLA* inherits = *Generic PLA*
compatible_printers_condition = printer_model=="X-smart 3" compatible_printers_condition = printer_model=="X-smart 3"
filament_max_volumetric_speed = 15
slowdown_below_layer_time = 8 slowdown_below_layer_time = 8
[filament:Generic TPU 95A @X-smart 3] [filament:Generic TPU 95A @X-smart 3]
inherits = *Generic TPU 95A* inherits = *Generic TPU 95A*
compatible_printers_condition = printer_model=="X-smart 3" compatible_printers_condition = printer_model=="X-smart 3"
slowdown_below_layer_time = 8
# Common printer preset # Common printer preset
[printer:*common*] [printer:*common*]
@@ -747,7 +894,7 @@ retract_restart_extra_toolchange = 0
retract_speed = 30 retract_speed = 30
silent_mode = 0 silent_mode = 0
single_extruder_multi_material = 0 single_extruder_multi_material = 0
start_gcode = PRINT_START\nG28\nM141 S0\nG0 Z50 F600\nM190 S[first_layer_bed_temperature]\nG28 Z\nG29 ; mesh bed leveling ,comment this code to close it\nG0 X0 Y0 Z50 F6000\nM109 S[first_layer_temperature]\nM106 P3 S255\nM83\nG0 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0)} Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0)} Z5 F6000\nG0 Z0.2 F600\nG1 E3 F1800\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 80))} E{85 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 2} E{2 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0)} E{85 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 85} E{83 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0) + 2} E{2 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 3} E{82 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0) + 12} E{-10 * 0.04} F3000\nG1 E{10 * 0.04} F3000 start_gcode = PRINT_START\nG28\nM141 S0\nG0 Z50 F600\nM190 S[first_layer_bed_temperature]\nG28 Z\nG29 ; mesh bed leveling ,comment this code to close it\nG0 X0 Y0 Z50 F6000\nM109 S[first_layer_temperature]\nM106 P3 S255\nM83\nG4 P3000\nG0 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0)} Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0)} Z5 F6000\nG0 Z0.2 F600\nG1 E3 F1800\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 80))} E{85 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 2} E{2 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0)} E{85 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 85} E{83 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0) + 2} E{2 * 0.04} F3000\nG1 Y{max((min(print_bed_max[1], first_layer_print_min[1] + 80) - 85),0) + 3} E{82 * 0.04} F3000\nG1 X{max((min(print_bed_max[0], first_layer_print_min[0] + 80) - 85),0) + 12} E{-10 * 0.04} F3000\nG1 E{10 * 0.04} F3000
template_custom_gcode = template_custom_gcode =
thumbnails = thumbnails =
thumbnails_format = QIDI thumbnails_format = QIDI

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,264 +1,363 @@
var LangText={ var LangText={
"en": { "en": {
"t0": "Welcome to QIDISlicer",
"t1": "User Guide", "t1": "User Guide",
"t2": "First Print", "t2": "First Print",
"t3": "Add Support", "t3": "Add Support",
"t4": "Connect Device", "t4": "Connect Device",
"t5": "Wifi Send", "t5": "Wifi Send",
"t6": "Issue Report", "t6": "Issue Report",
"t7": "Demonstration", "t7": "Introduce",
"t8": "Product Info", "t8": "3D Printers",
"t9": "Contact with us", "t9": "Contact with us",
"t10": "Filament", "t10": "Filaments",
"t11": "Add Text", "t11": "Add Text",
"t12": "Cut Model", "t12": "Cut Model",
"t13": "Download Model", "t13": "Download Model",
"t14": "Adaptive Meshing", "t14": "Adaptive Meshing",
"ls0": "Learn more:", "t15": "Flow Rate Calibration",
"ls1": "You can download 3D models from the following websites.", "t16": "Calibration",
"ls2": "If you have any questions about the Printer or QIDISlicer, please contact us via the appropriate E-mail address or Skype.", "t17": "Pressure Advance",
"ls3": "Note: please try to tell us your requirements in the form of video or pictures, and provide 3MF file, G-code file, machine number and other necessary information.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "Learn more:",
"ls5": "Why do you need Adaptive Meshing:", "l1": "You can download the 3D model from the following link.",
"ls6": "How to disable this feature:", "l2": "If you have any questions about the Printer or QIDISlicer, please contact us via the appropriate E-mail address or Skype.",
"ls7": "1.No wasted probe information, Adaptive Meshing will generate a mesh only in the area you actually need it: This means that leveling before each print takes significantly less time!", "l3": "Note: please try to tell us your requirements in the form of video or pictures, and provide 3MF file, G-code file, machine number and other necessary information.",
"ls8": "2.Since the mesh area will be smaller, the mesh can be much more dense. Imagine making a 3x3 mesh, but the size of a 3DBenchy! Which brings you a higher precision printing experience.", "l5": "Why do you need Adaptive Meshing:",
"ls9": "Add ';' before 'G29' in the Start G-code.", "l6": "How to close:",
"ls10": "Red Area: General mesh leveling.", "l7": "1.No wasted probe information, Adaptive Meshing will generate a mesh only in the area you actually need it: This means that leveling before each print takes significantly less time!",
"ls11": "Green Area: Adaptive mesh leveling.", "l8": "2.Since the mesh area will be smaller, the mesh can be much more dense. Imagine making a 3x3 mesh, but the size of a 3DBenchy! Which brings you a higher precision printing experience.",
"l9": "Add ';' before 'G29' in the Start G-code.",
"l10": "Red Area: General mesh leveling.",
"l11": "Green Area: Adaptive mesh leveling.",
"l12": "Why you need Flowrate Calibration",
"l13": "Different filaments have different shrinkage rates, and in order to obtain perfect surface quality, the print flow rate needs to be carefully adjusted.",
"l14": "Flowrate can be changed by modifying the 'Extrusion multiplier'.",
"l15": "1.Overextrusion: There is too much material on the model, causing bumps to form or layers to appear thicker and uneven than expected.",
"l16": "2.Underextrusion extrusion: the printing layer is too thin, the filling strength is insufficient, the surface has dents, and the model is easy to break.",
"l17": "Coarse calibration",
"l18": "Print calibration blocks in the 'Extrusion multiplier' ±0.2 range, with each block having a step size of 0.05.",
"l19": "When you are unsure of the consumable flow rate, you can use coarse calibration first to get a better range, then fine calibration to get a more accurate value.",
"l20": "After printing, determine the best value based on the model surface, enter the 'Extrusion multiplier', and save.",
"l21": "In the figure below, -5 has the best surface quality, so the optimal 'Extrusion multiplier' is 0.95 (1-5%).",
"l22": "Fine calibration",
"l23": "Print calibration blocks in the ±0.04 range based on the 'Extrusion multiplier' you entered, each with a step size of 0.01.",
"l24": "The 'Extrusion multiplier' entered in the figure below is 0.95, and 1 has the best surface quality, so the best 'Extrusion multiplier' is 0.96 (0.95+1%).",
"l25": "Pressure advance can reduce bleed during non-extrude moves and reduce blobbing during cornering.",
"l26": "When to Calibrate Pressure in Advance",
"l27": "1.Use different brands of filaments, or the filaments are damp;",
"l28": "2.The nozzle is worn or replaced with a different size nozzle;",
"l29": "3.Use different printing parameters such as temperature and line width.",
"l30": "How to calibrate",
"l31": "We provide three common calibration methods, you can choose the method you like for calibration.",
"l32": "There is a 10% error in the test results, but it is almost indistinguishable to the naked eye. Choose a suitable value and save it, then start your printing.",
"l33": "If you need to modify the printing parameters, please modify and save the parameters first, and then perform the calibration procedure.",
"l34": "After printing is completed, please enter the best value into the software and save it.",
"l35": "PA Line",
"l36": "After printing is completed, select the smoothest line, enter its corresponding value into the software and save it.",
"l37": "PA Pattern",
"l38": "After printing is completed, select the flattest set of lines, enter its corresponding values into the software and save it.",
"l39": "PA Tower",
"l40": "The pressure advance value will be changed every 5mm the PA tower rises. Please measure the height of the best area of the model, and then calculate the corresponding pressure advance value based on the set step value, then input it into the software and save it.",
}, },
"zh_CN": { "zh_CN": {
"t0": "欢迎使用QIDISlicer",
"t1": "用户指南", "t1": "用户指南",
"t2": "首次打印", "t2": "首次打印",
"t3": "添加支撑", "t3": "添加支撑",
"t4": "连接设备", "t4": "连接设备",
"t5": "无线发送", "t5": "无线发送",
"t6": "问题报告", "t6": "问题报告",
"t7": "演示", "t8": "3D打印机",
"t8": "产品信息", "t7": "介绍",
"t9": "与我们联系", "t9": "与我们联系",
"t10": "耗材", "t10": "耗材",
"t11": "添加文本", "t11": "添加文本",
"t12": "切割模型", "t12": "切割模型",
"t13": "下载模型", "t13": "下载模型",
"t14": "自适应网格划分", "t14": "自适应网格划分",
"ls0": "了解更多:", "t15": "流量校准",
"ls1": "您可以从以下网站下载 3D 模型。", "t16": "校准",
"ls2": "如果您对打印机或 QIDISlicer 有任何疑问,请通过相应的电子邮件地址或 Skype 与我们联系。", "t17": "压力提前",
"ls3": "注请尽量以视频或图片的形式告诉我们您的需求并提供3MF文件、G-Code文件、机器编号等必要信息。",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "了解更多:",
"ls5": "为什么需要自适应网格划分:", "l1": "您可以从以下链接下载3D模型。",
"ls6": "如何禁用此功能:", "l2": "如果您对打印机或 QIDISlicer 有任何疑问请通过相应的电子邮件地址或Skype与我们联系。",
"ls7": "1.不浪费探头信息,自适应网格划分将仅在您实际需要的区域生成网格:这意味着每次打印之前的调平所需的时间显着减少!", "l3": "请尽量以视频或图片的形式告诉我们您的需求并提供3MF文件、G-Code文件、机器编号等必要信息。",
"ls8": "2.由于网格面积会更小,所以网格可以更密。想象一下制作一个 3x3 网格,但大小与 3DBenchy 一样!为您带来更高精度的打印体验。", "l5": "为什么需要自适应网格划分:",
"ls9": "在起始G-code中的'G29'前添加';'。", "l6": "如何禁用此功能:",
"ls10": "红色区域:一般网格调平。", "l7": "1.不浪费探头信息,自适应网格划分将仅在您实际需要的区域生成网格:这意味着每次打印之前的调平所需的时间显着减少!",
"ls11": "绿色区域:自适应网格平整。", "l8": "2.由于网格面积会更小,所以网格可以更密。想象一下制作一个 3x3 网格但大小与3DBenchy一样为您带来更高精度的打印体验。",
"l9": "在起始G-code中的'G29'前添加';'。",
"l10": "红色区域:一般网格调平。",
"l11": "绿色区域:自适应网格调平。",
"l12": "为什么需要流量校准",
"l13": "不同的耗材拥有不同的收缩率,为了获得完美的表面质量,需要对打印流量进行细致的调整。",
"l14": "通过修改‘挤出乘数’可以更改打印流量。",
"l15": "1.挤出过度:模型上的材料过多,导致形成凸起或层显得比预期更厚且不均匀。",
"l16": "2.挤出不足:打印层太薄,填充强度不足,表面有凹痕,模型易断裂。",
"l17": "粗校准",
"l18": "打印挤出乘数±0.2范围内的校准块每个校准块步长为0.05。",
"l19": "当您不确定耗材流量时,可以先使用粗校准以获得更好的范围,然后使用精校准以获得更准确的值。",
"l20": "打印完成后,根据模型表面确定最佳值,输入‘挤出乘数’,并保存。",
"l21": "下图中-5的表面质量最佳因此最佳挤出乘数为0.951-5%)。",
"l22": "精校准",
"l23": "根据您输入的挤出乘数打印±0.04范围内的校准块每个校准块步长为0.01。",
"l24": "下图中输入的挤出乘数为0.951的表面质量最佳因此最佳挤出乘数为0.960.95+1%)。",
"l25": "压力提前可以减少非挤压移动期间的渗出,并减少转弯期间的滴落。",
"l26": "什么时候需要校准压力提前",
"l27": "1.使用不同品牌的耗材,或耗材受潮;",
"l28": "2.喷嘴磨损或更换不同尺寸的喷嘴;",
"l29": "3.使用不同的温度、线宽等打印参数。",
"l30": "如何校准",
"l31": "我们提供了三种常见的校准方式,你可以选择自己喜欢的方式进行校准。",
"l32": "测试结果存在10%的误差,但肉眼几乎无法区别,选择一个合适的值并将其保存,然后开始你的打印吧。",
"l33": "如果你需要修改打印参数,请先修改并保存参数,然后再进行校准程序。",
"l34": "打印完成后,请将最佳值输入到软件中并保存。",
"l35": "PA 划线",
"l36": "打印完成后,选择最平滑的一条线,请将其对应的值输入到软件中并保存。",
"l37": "PA 图形",
"l38": "打印完成后,选择最平整的一组线,请将其对应的值输入到软件中并保存。",
"l39": "PA 塔",
"l40": "PA 塔每升高5mm会更改一次压力提前值请测量出模型最佳区域的高度再根据设定的步进值计算出其对应的压力提前值然后输入到软件中并保存。",
}, },
"ja": { "ja": {
"t0": "QIDISlicerへようこそ",
"t1": "ユーザーガイド", "t1": "ユーザーガイド",
"t2": "最初の印刷物", "t2": "最初の印刷物",
"t3": "サポートを追加する", "t3": "サポートを追加する",
"t4": "デバイスを接続する", "t4": "デバイスを接続する",
"t5": "Wi-Fi送信", "t5": "Wi-Fi送信",
"t6": "問題レポート", "t6": "問題レポート",
"t7": "デモンストレーション", "t7": "紹介します",
"t8": "製品情報", "t8": "3Dプリンター",
"t9": "お問い合わせください", "t9": "お問い合わせください",
"t10": "フィラメント", "t10": "フィラメント",
"t11": "テキストを追加", "t11": "テキストを追加",
"t12": "カットモデル", "t12": "カットモデル",
"t13": "モデルをダウンロード", "t13": "モデルをダウンロード",
"t14": "適応性メッシュ", "t14": "適応性メッシュ",
"ls0": "もっと詳しく知る:", "t15": "流量校正",
"ls1": "3Dモデルは以下のWebサイトからダウンロードできます。", "t16": "較正",
"ls2": "如果您对打印机或 QIDISlicer 有任何疑问,请通过相应的电子邮件地址或 Skype 与我们联系。", "t17": "圧力前進",
"ls3": "注请尽量以视频或图片的形式告诉我们您的需求并提供3MF文件、G-Code文件、机器编号等必要信息。",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "もっと詳しく知る:",
"ls5": "アダプティブ メッシュが必要な理由:", "l1": "3Dモデルは以下のWebサイトからダウンロードできます。",
"ls6": "この機能を無効にする方法:", "l2": "如果您对打印机或 QIDISlicer 有任何疑问,请通过相应的电子邮件地址或 Skype 与我们联系。",
"ls7": "1. 無駄なプローブ情報がなく、アダプティブメッシュは実際に必要な領域のメッシュのみを生成します。これは、各印刷前のレベリングにかかる​​時間が大幅に短縮されることを意味します。", "l3": "请尽量以视频或图片的形式告诉我们您的需求并提供3MF文件、G-Code文件、机器编号等必要信息。",
"ls8": "2.メッシュ領域が小さくなるため、メッシュをより高密度にすることができます。 3x3 メッシュを 3DBenchy のサイズで作成することを想像してみてください。これにより、より高精度の印刷体験が得られます。", "l5": "アダプティブ メッシュが必要な理由:",
"ls9": "追加 ';' 「最初のGコード」の'G29'の前。", "l6": "この機能を無効にする方法:",
"ls10": "赤い領域: 一般的なメッシュ レベリング。", "l7": "1. 無駄なプローブ情報がなく、アダプティブメッシュは実際に必要な領域のメッシュのみを生成します。これは、各印刷前のレベリングにかかる​​時間が大幅に短縮されることを意味します。",
"ls11": "緑色の領域: 適応性メッシュ レベリング。", "l8": "2.メッシュ領域が小さくなるため、メッシュをより高密度にすることができます。 3x3 メッシュを 3DBenchy のサイズで作成することを想像してみてください。これにより、より高精度の印刷体験が得られます。",
"l9": "追加 ';' 「最初のGコード」の'G29'の前。",
"l10": "赤い領域: 一般的なメッシュ レベリング。",
"l11": "緑色の領域: 適応性メッシュ レベリング。",
"l12": "フローのキャリブレーションはなぜ必要なのでしょうか",
"l13": "消耗品によっても収縮率は異なりますし、完璧な表面品質を得るためには印刷フローの調整が必要です。",
"l14": "『押し出し乗数』を修正することでプリントフローを変更できます。",
"l15": "1.押し出しすぎ:型の上の材料が多すぎて、突起や層が予想より厚くなってムラができてしまいます。",
"l16": "2.押し出し不足:印刷層が薄すぎて、充填強度が足りなくて、表面に凹痕があって、型が切れやすいです。",
"l17": "粗いキャリブレーション",
"l18": "「押し出し乗数」±0.2の範囲のキャリブレーションブロックを印刷し、各キャリブレーションブロックのステップを0.05とします。",
"l19": "消耗品の流れがわからないときは、粗いキャリブレーションを使って範囲を広くしてから、精緻なキャリブレーションを使ってより正確な値を出すことができます。",
"l20": "印刷が終わったら、モデルの表面から最適値を決めて、「押し出し乗数」を入力し、保存します。",
"l21": "次の図では、-5が最適なので、最適な「押し出し乗数」は0.95(1-5%)となります。",
"l22": "キャリブレーションを行います",
"l23": "入力された「押し出し乗数」に応じて±0.04の範囲のキャリブレーションブロックを印刷します。キャリブレーションブロック毎のステップサイズは0.01です。",
"l24": "図では、「押し出し乗数」を0.95と入力していますが、1の表面品質が最適なので、最適な「押し出し乗数」は0.96(0.95+1%)となります。",
"l25": "圧力前進により、非絞り動作時のブリードが減少し、旋回時の液だれが減少します。",
"l26": "圧力前進を校正する場合",
"l27": "1.異なるブランドの消耗品を使用すると、消耗品が湿気を帯びます。",
"l28": "2.ノズルが磨耗しているか、異なるサイズのノズルに交換されている。",
"l29": "3.温度や線幅などのさまざまな印刷パラメータを使用します。",
"l30": "校正方法",
"l31": "一般的な3つの校正方法が用意されており、お好みの校正方法を選択できます。",
"l32": "テスト結果には10%程度の誤差がありますが、肉眼ではほとんど区別できない程度ですので、適切な値を選択して保存し、印刷を開始してください。",
"l33": "印刷パラメータを変更する必要がある場合は、まずパラメータを変更して保存してから、キャリブレーション手順を実行してください。",
"l34": "印刷が完了したら、ソフトウェアに最適な値を入力して保存します。",
"l35": "PA 線",
"l36": "印刷が完了したら、最も滑らかな線を選択し、対応する値をソフトウェアに入力して保存します。",
"l37": "PAグラフィックス",
"l38": "印刷が完了したら、最も平坦な線のセットを選択し、対応する値をソフトウェアに入力して保存します。",
"l39": "PAタワー",
"l40": "PAタワーが上昇する5mmごとに圧力アドバンス値が変更されますので、モデルの最適領域の高さを測定し、設定されたステップ値に基づいて対応する圧力アドバンス値を計算し、それをソフトウェアに入力して、それを保存。",
}, },
"fr": { "fr": {
"t0": "Bienvenue dans QIDISlicer",
"t1": "Guide de l'utilisateur", "t1": "Guide de l'utilisateur",
"t2": "Première d'impression", "t2": "Première d'impression",
"t3": "Ajouter une prise en charge", "t3": "Ajouter une prise en charge",
"t4": "Connecter l'appareil", "t4": "Connecter l'appareil",
"t5": "Envoi Wi-Fi", "t5": "Envoi Wi-Fi",
"t6": "Rapport de problème", "t6": "Rapport de problème",
"t7": "Démonstration", "t7": "Présentez",
"t8": "Informations sur le produit", "t8": "Imprimantes 3D",
"t9": "Contactez-nous", "t9": "Contactez-nous",
"t10": "Filament", "t10": "Filaments",
"t11": "Ajouter du texte", "t11": "Ajouter du texte",
"t12": "Couper le modèle", "t12": "Couper le modèle",
"t13": "Télécharger le modèle", "t13": "Télécharger le modèle",
"t14": "Maillage adaptatif", "t14": "Maillage adaptatif",
"ls0": "Apprendre encore plus:", "t15": "Étalonnage du débit",
"ls1": "Vous pouvez télécharger des modèles 3D à partir des sites Web suivants.", "t16": "Étalonnage",
"ls2": "Si vous avez des questions sur l'imprimante ou le QIDISlicer, veuillez nous contacter via l'adresse e-mail appropriée ou via Skype.", "t17": "Avance de pression",
"ls3": "Remarque : veuillez essayer de nous indiquer vos besoins sous forme de vidéo ou d'images, et fournir un fichier 3MF, un fichier G-code, un numéro de machine et d'autres informations nécessaires.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "Apprendre encore plus:",
"ls5": "Pourquoi avez-vous besoin d'un maillage adaptatif :", "l1": "Vous pouvez télécharger des modèles 3D à partir des sites Web suivants.",
"ls6": "Comment désactiver cette fonctionnalité :", "l2": "Si vous avez des questions sur l'imprimante ou le QIDISlicer, veuillez nous contacter via l'adresse e-mail appropriée ou via Skype.",
"ls7": "1. Aucune information de sonde gaspillée, le maillage adaptatif générera un maillage uniquement dans la zone où vous en avez réellement besoin : cela signifie que le nivellement avant chaque impression prend beaucoup moins de temps !", "l3": "Remarque: veuillez essayer de nous indiquer vos besoins sous forme de vidéo ou d'images, et fournir un fichier 3MF, un fichier G-code, un numéro de machine et d'autres informations nécessaires.",
"ls8": "2. Étant donné que la zone de maillage sera plus petite, le maillage peut être beaucoup plus dense. Imaginez faire un maillage 3x3, mais de la taille d'un 3DBenchy ! Ce qui vous apporte une expérience d'impression de plus grande précision.", "l5": "Pourquoi avez-vous besoin d'un maillage adaptatif:",
"ls9": "Ajouter ';' avant 'G29' dans le G-code de début", "l6": "Comment désactiver cette fonctionnalité:",
"ls10": "Zone rouge : Nivellement général du maillage.", "l7": "1.Aucune information de sonde gaspillée, le maillage adaptatif générera un maillage uniquement dans la zone où vous en avez réellement besoin: cela signifie que le nivellement avant chaque impression prend beaucoup moins de temps !",
"ls11": "Zone verte : Nivellement de maillage adaptatif.", "l8": "2.Étant donné que la zone de maillage sera plus petite, le maillage peut être beaucoup plus dense. Imaginez faire un maillage 3x3, mais de la taille d'un 3DBenchy ! Ce qui vous apporte une expérience d'impression de plus grande précision.",
"l9": "Ajouter ';' avant 'G29' dans le G-code de début",
"l10": "Zone rouge: Nivellement général du maillage.",
"l11": "Zone verte: Nivellement de maillage adaptatif.",
"l12": "Pourquoi vous avez besoin de la Calibration du débit",
"l13": "Les différents filaments ont des taux de retrait différents, et afin dobtenir une qualité de surface parfaite, le débit dimpression doit être ajusté avec soin.",
"l14": "Le débit peut être modifié en modifiant le «multiplicateur dextrusion».",
"l15": "1.Surextrusion: il y a trop de matériau sur le modèle, provoquant la formation de bosses ou lapparition de couches plus épaisses et inégales que prévu.",
"l16": "2.Extrusion dunderextrusion: la couche dimpression est trop mince, la force remplissante est insuffisante, la surface a des bosses, et le modèle est facile à casser.",
"l17": "Calibrage grossier",
"l18": "Imprimer des blocs détalonnage dans la gamme «multiplicateur dextrusion» de ± 0.2, chaque bloc ayant une taille de pas de 0.05.",
"l19": "Lorsque vous nêtes pas sûr du débit des consommables, vous pouvez utiliser létalonnage grossier dabord pour obtenir une meilleure plage, puis létalonnage fin pour obtenir une valeur plus précise.",
"l20": "Après limpression, déterminez la meilleure valeur en fonction de la surface du modèle, saisissez le «multiplicateur dextrusion» et sauvegardez.",
"l21": "Dans la figure ci-dessous, -5 a la meilleure qualité de surface, de sorte que le multiplicateur dextrusion optimal est 0.95 (1-5%).",
"l22": "Calibrage fin",
"l23": "Imprimez des blocs détalonnage de ± 0.04 en fonction du «multiplicateur dextrusion» que vous avez entré, chacun avec une taille de pas de 0.01.",
"l24": "Le «multiplicateur dextrusion» indiqué dans la figure ci-dessous est de 0.95, et 1 a la meilleure qualité de surface, donc le meilleur «multiplicateur dextrusion» est de 0.96 (0.95+1%).",
"l25": "L'avance de pression peut réduire le saignement lors des mouvements sans extrusion et réduire les gouttes dans les virages.",
"l26": "Quand calibrer la pression à lavance",
"l27": "1.Utilisez différentes marques de filaments, sinon les filaments sont humides ;",
"l28": "2.La buse est usée ou remplacée par une buse de taille différente ;",
"l29": "3.Utilisez différents paramètres d'impression tels que la température et la largeur des lignes.",
"l30": "Comment calibrer",
"l31": "Nous proposons trois méthodes d'étalonnage courantes, vous pouvez choisir la méthode que vous souhaitez pour l'étalonnage.",
"l32": "Il y a une erreur de 10 % dans les résultats du test, mais elle est presque impossible à distinguer à l'œil nu. Choisissez une valeur appropriée et enregistrez-la, puis démarrez votre impression.",
"l33": "Si vous devez modifier les paramètres d'impression, veuillez d'abord modifier et enregistrer les paramètres, puis effectuer la procédure d'étalonnage.",
"l34": "Une fois l'impression terminée, veuillez saisir la meilleure valeur dans le logiciel et l'enregistrer.",
"l35": "Ligne PA",
"l36": "Une fois l'impression terminée, sélectionnez la ligne la plus lisse, entrez la valeur correspondante dans le logiciel et enregistrez-la.",
"l37": "Modèle PA",
"l38": "Une fois l'impression terminée, sélectionnez l'ensemble de lignes le plus plat, entrez ses valeurs correspondantes dans le logiciel et enregistrez-le.",
"l39": "La tour PA",
"l40": "La valeur d'avance de pression sera modifiée tous les 5 mm d'élévation de la tour PA. Veuillez mesurer la hauteur de la meilleure zone du modèle, puis calculer la valeur d'avance de pression correspondante en fonction de la valeur de pas définie, puis la saisir dans le logiciel et l'enregistrer.",
}, },
"de": { "de": {
"t0": "Willkommen bei QIDISlicer",
"t1": "Benutzerhandbuch", "t1": "Benutzerhandbuch",
"t2": "Erster Druck", "t2": "Erster Druck",
"t3": "Unterstützung hinzufügen", "t3": "Unterstützung hinzufügen",
"t4": "Gerät verbinden", "t4": "Gerät verbinden",
"t5": "Wi-Fi senden", "t5": "Wi-Fi senden",
"t6": "Problembericht", "t6": "Problembericht",
"t7": "Demonstration", "t7": "Vorstellung",
"t8": "Produktinformationen", "t8": "3D-Drucker",
"t9": "Kontaktieren Sie uns", "t9": "Kontaktieren Sie uns",
"t10": "Filament", "t10": "Filaments",
"t11": "Text hinzufügen", "t11": "Text hinzufügen",
"t12": "Modell schneiden", "t12": "Modell schneiden",
"t13": "Modell herunterladen", "t13": "Modell herunterladen",
"t14": "Adaptive Gitternetz", "t14": "Adaptive Gitternetz",
"ls0": "Erfahren Sie mehr:", "t15": "Kalibrierung der Durchflussrate",
"ls1": "Sie können 3D-Modelle von den folgenden Websites herunterladen.", "t16": "Kalibrierung",
"ls2": "Wenn Sie Fragen zum Drucker oder QIDISlicer haben, kontaktieren Sie uns bitte über die entsprechende E-Mail-Adresse oder Skype.", "t17": "Druckvorschub",
"ls3": "Hinweis: Bitte versuchen Sie, uns Ihre Anforderungen in Form von Videos oder Bildern mitzuteilen und geben Sie die 3MF-Datei, die G-Code-Datei, die Maschinennummer und andere notwendige Informationen an.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "Erfahren Sie mehr:",
"ls5": "Warum brauchen Sie Adaptive Meshing:", "l1": "Sie können 3D-Modelle von den folgenden Websites herunterladen.",
"ls6": "So deaktivieren Sie diese Funktion:", "l2": "Wenn Sie Fragen zum Drucker oder QIDISlicer haben, kontaktieren Sie uns bitte über die entsprechende E-Mail-Adresse oder Skype.",
"ls7": "1. Keine Verschwendung von Sondeninformationen, Adaptive Meshing erstellt ein Netz nur in dem Bereich, in dem Sie es tatsächlich benötigen: Das bedeutet, dass die Nivellierung vor jedem Druck deutlich weniger Zeit in Anspruch nimmt!", "l3": "Hinweis: Bitte versuchen Sie, uns Ihre Anforderungen in Form von Videos oder Bildern mitzuteilen und geben Sie die 3MF-Datei, die G-Code-Datei, die Maschinennummer und andere notwendige Informationen an.",
"ls8": "2.Da die Maschenfläche kleiner wird, kann das Netz viel dichter sein. Stellen Sie sich vor, Sie erstellen ein 3x3-Netz, aber in der Größe eines 3DBenchy! Das bringt Ihnen ein präziseres Druckerlebnis.", "l5": "Warum brauchen Sie Adaptive Meshing:",
"ls9": "Füge hinzu ein „;“ vor „G29“ im Start-G-Code.", "l6": "So deaktivieren Sie diese Funktion:",
"ls10": "Roter Bereich: Allgemeine Netznivellierung.", "l7": "1.Keine Verschwendung von Sondeninformationen, Adaptive Meshing erstellt ein Netz nur in dem Bereich, in dem Sie es tatsächlich benötigen: Das bedeutet, dass die Nivellierung vor jedem Druck deutlich weniger Zeit in Anspruch nimmt!",
"ls11": "Grüner Bereich: Adaptive Netznivellierung.", "l8": "2.Da die Maschenfläche kleiner wird, kann das Netz viel dichter sein. Stellen Sie sich vor, Sie erstellen ein 3x3-Netz, aber in der Größe eines 3DBenchy! Das bringt Ihnen ein präziseres Druckerlebnis.",
"l9": "Füge hinzu ein „;“ vor „G29“ im Start-G-Code.",
"l10": "Roter Bereich: Allgemeine Netznivellierung.",
"l11": "Grüner Bereich: Adaptive Netznivellierung.",
"l12": "Was ist eine icd",
"l13": "Es gibt unterschiedliche skalenquoten und man muss den fluss der blüten genau einstellen, um eine optimale oberflächenqualität zu erreichen.",
"l14": "Die durchflussrate kann durch änderung des expeit geändert werden.",
"l15": "1.Überdruck: das modell enthält zu viel material, was dazu führt, dass die form dicker und ungleichmäßiger aussieht als erwartet.",
"l16": "2.Auspressen: der druck ist zu dünn, lässt das material zu wenig laden, hat löcher an der oberfläche, die die modelle beschädigen.",
"l17": "Grobkalibrierung.",
"l18": "Das überprüfen der kalibrierungsblock im rahmen des cop-fensters mit einer wand Von 0.05 stufen.",
"l19": "Bei problemen mit problemen können sie zunächst die raummanadung verwenden, um die richtige übereinstimmung zu erreichen, dann die feinkalibrierung einsetzen.",
"l20": "Wenn der ausdruck fertig ist, beschriftet man die besten werte anhand der oberfläche des modells, geht in den specker-bereich und speichern.",
"l21": "In der grafik, -5 mit der besten oberflächenqualität, kriegst du damit eine optimale ersparnis Von 0.95 (1-5%).",
"l22": "Härtstrich.",
"l23": "Das druckmodul für die wand im bereich Von 0.04 überprüfen, und jeder schritt ist eine 0.01",
"l24": "In dem unteren bild wurde ein -queprol Von 0.95 angezeigt, wobei 1 die beste oberflächenqualität hat. Damit liegt die beste -weshalb bei 0.96 (0.95+1).",
"l25": "Der Druckvorschub kann das Ausbluten bei nicht extrudierten Bewegungen und das Klopfen bei Kurvenfahrten reduzieren.",
"l26": "Wann sollte der Druck im Voraus kalibriert werden",
"l27": "1.Verwenden Sie Filamente verschiedener Marken, oder die Filamente sind feucht;",
"l28": "2.Die Düse ist abgenutzt oder durch eine Düse anderer Größe ersetzt;",
"l29": "3.Verwenden Sie verschiedene Druckparameter wie Temperatur und Linienbreite.",
"l30": "So kalibrieren Sie",
"l31": "Wir bieten drei gängige Kalibrierungsmethoden an. Sie können die Methode auswählen, die Ihnen für die Kalibrierung am besten gefällt.",
"l32": "Die Testergebnisse weisen einen Fehler von 10 % auf, der jedoch mit bloßem Auge kaum zu erkennen ist. Wählen Sie einen geeigneten Wert, speichern Sie ihn und starten Sie dann den Druckvorgang.",
"l33": "Wenn Sie die Druckparameter ändern müssen, ändern und speichern Sie bitte zuerst die Parameter und führen Sie dann den Kalibrierungsvorgang durch.",
"l34": "Nachdem der Druckvorgang abgeschlossen ist, geben Sie bitte den besten Wert in die Software ein und speichern Sie ihn.",
"l35": "PA-Leitung",
"l36": "Wählen Sie nach Abschluss des Druckvorgangs die glatteste Linie aus, geben Sie den entsprechenden Wert in die Software ein und speichern Sie ihn.",
"l37": "PA-Muster",
"l38": "Wählen Sie nach Abschluss des Druckvorgangs den flachsten Liniensatz aus, geben Sie die entsprechenden Werte in die Software ein und speichern Sie sie.",
"l39": "PA-Turm",
"l40": "Der Druckvorschubwert wird alle 5 mm geändert, wenn der PA-Turm ansteigt. Bitte messen Sie die Höhe des besten Bereichs des Modells und berechnen Sie dann den entsprechenden Druckvorschubwert basierend auf dem eingestellten Schrittwert, geben Sie ihn dann in die Software ein und speichern Sie ihn.",
}, },
"be": { "be": {
"t0": "Вітаем у QIDISlicer",
"t1": "Кіраўніцтва карыстальніка", "t1": "Кіраўніцтва карыстальніка",
"t2": "Першы адбітак", "t2": "Першы адбітак",
"t3": "Дадаць падтрымку", "t3": "Дадаць падтрымку",
"t4": "Падключыць прыладу", "t4": "Падключыць прыладу",
"t5": "Адправіць па Wi-Fi", "t5": "Адправіць па Wi-Fi",
"t6": "Паведамленне аб праблеме", "t6": "Паведамленне аб праблеме",
"t7": "Дэманстрацыя", "t7": "Познакомьтесь с нами",
"t8": "Інфармацыя аб прадукце", "t8": "3D принтеры",
"t9": "Звязацца з намі", "t9": "Звязацца з намі",
"t10": "Філамент", "t10": "Філамент",
"t11": "Дадаць тэкст", "t11": "Дадаць тэкст",
"t12": "Разрэзаная мадэль", "t12": "Разрэзаная мадэль",
"t13": "Спампаваць мадэль", "t13": "Спампаваць мадэль",
"t14": "Адаптыўная сетка", "t14": "Адаптыўная сетка",
"ls0": "даведацца больш:", "t15": "Каліброўка хуткасці патоку",
"ls1": "Вы можаце загрузіць 3D-мадэлі з наступных сайтаў.", "t16": "Каліброўка",
"ls2": "Калі ў вас ёсць якія-небудзь пытанні аб прынтары або QIDISlicer, калі ласка, звяжыцеся з намі праз адпаведны адрас электроннай пошты або Skype.", "t17": "Увеличение давления",
"ls3": "Заўвага: калі ласка, паспрабуйце паведаміць нам свае патрабаванні ў выглядзе відэа ці малюнкаў, а таксама прадаставіць файл 3MF, файл G-кода, нумар машыны і іншую неабходную інфармацыю.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l0": "даведацца больш:",
"ls5": "Навошта вам патрэбна адаптыўная сетка:", "l1": "Вы можаце загрузіць 3D-мадэлі з наступных сайтаў.",
"ls6": "Як адключыць гэтую функцыю:", "l2": "Калі ў вас ёсць якія-небудзь пытанні аб прынтары або QIDISlicer, калі ласка, звяжыцеся з намі праз адпаведны адрас электроннай пошты або Skype.",
"ls7": "1.Няма марнай інфармацыі пра зонд, Adaptive Meshing будзе ствараць сетку толькі ў той вобласці, якая вам сапраўды патрэбна: гэта азначае, што выраўноўванне перад кожнай друкам займае значна менш часу!", "l3": "Заўвага: калі ласка, паспрабуйце паведаміць нам свае патрабаванні ў выглядзе відэа ці малюнкаў, а таксама прадаставіць файл 3MF, файл G-кода, нумар машыны і іншую неабходную інфармацыю.",
"ls8": "2.Паколькі плошча сеткі будзе меншай, сетка можа быць значна больш шчыльнай. Уявіце, што вы робіце сетку 3x3, але памерам з 3DBenchy! Што дае вам вопыт больш высокай дакладнасці друку.", "l5": "Навошта вам патрэбна адаптыўная сетка:",
"ls9": "Дадайце ';' перад 'G29' у Пачатковым G-кодзе.", "l6": "Як адключыць гэтую функцыю:",
"ls10": "Чырвоная зона: агульнае выраўноўванне сеткі.", "l7": "1.Няма марнай інфармацыі пра зонд, Adaptive Meshing будзе ствараць сетку толькі ў той вобласці, якая вам сапраўды патрэбна: гэта азначае, што выраўноўванне перад кожнай друкам займае значна менш часу!",
"ls11": "Зялёная зона: адаптыўнае выраўноўванне сеткі.", "l8": "2.Паколькі плошча сеткі будзе меншай, сетка можа быць значна больш шчыльнай. Уявіце, што вы робіце сетку 3x3, але памерам з 3DBenchy! Што дае вам вопыт больш высокай дакладнасці друку.",
}, "l9": "Дадайце ';' перад 'G29' у Пачатковым G-кодзе.",
"ca": { "l10": "Чырвоная зона: агульнае выраўноўванне сеткі.",
"t0": "Benvingut a QIDISlicer", "l11": "Зялёная зона: адаптыўнае выраўноўванне сеткі.",
"t1": "Guia de l'usuari", "l12": "Зачем вам калибровка расхода",
"t2": "Primera d'impressió", "l13": "Различные нити накала имеют разную скорость усадки, и для того, чтобы получить идеальное качество поверхности, скорость печати должна быть тщательно скорректирована.",
"t3": "Afegeix suport", "l14": "Расход может быть изменен путем изменения 'экструзионный множитель'.",
"t4": "Connecta el dispositiu", "l15": "1.Чрезмерная трузия: на модели слишком много материала, что приводит к образованию шишек или слоев, которые кажутся толще и неравномерно, чем ожидалось.",
"t5": "Enviament sense fil", "l16": "2.Экструзия: слой печати слишком тонкий, прочность наполнения недостаточна, поверхность имеет вмятины, а модель легко сломать.",
"t6": "Informe de problemes", "l17": "Калибровка по грубой поверхности",
"t7": "Demostració", "l18": "Печать калибровочных блоков в диапазоне 'экструзионный множитель' ±0.2, причем каждый блок имеет размер шага 0.05.",
"t8": "Informació del producte", "l19": "Если вы не уверены в скорости потока расходуемых материалов, вы можете сначала использовать крупногабаритную калибровку, чтобы получить лучший диапазон, а затем тонкую калибровку, чтобы получить более точное значение.",
"t9": "Contacteu amb nosaltres", "l20": "После печати определите наилучшее значение на основе поверхности модели, введите 'экструзионный множитель' и сохраните.",
"t10": "Filament", "l21": "На рисунке ниже -5 имеет наилучшее качество поверхности, поэтому оптимальный 'экструзионный множитель' составляет 0,95 (1-5%).",
"t11": "Afegeix text", "l22": "- точная калибровка",
"t12": "Tallar el model", "l23": "Печать калибровочных блоков в диапазоне ±0.04 на основе 'экструзионный множитель'вы ввели, каждый размер шага 0.01.",
"t13": "Baixa el model", "l24": "Приведенный ниже 'экструзионный множитель' равен 0,95, а 1 имеет наилучшее качество поверхности, поэтому лучший 'экструзионный множитель' равен 0.96 (0.95+1%).",
"t14": "Malla adaptativa", "l25": "Увеличение давления может уменьшить кровотечение при движениях без выдавливания и уменьшить образование пятен при прохождении поворотов.",
"ls0": "Aprèn més:", "l26": "Когда следует калибровать давление заранее",
"ls1": "Podeu descarregar models 3D dels següents llocs web.", "l27": "1.Используйте нити разных марок, иначе нити влажные;",
"ls2": "Si teniu cap pregunta sobre la impressora o QIDISlicer, poseu-vos en contacte amb nosaltres mitjançant l'adreça de correu electrònic corresponent o Skype.", "l28": "2.Насадка изношена или заменена насадкой другого размера;",
"ls3": "Nota: proveu d'indicar-nos els vostres requisits en forma de vídeo o imatges i proporcioneu un fitxer 3MF, un fitxer de codi G, un número de màquina i altra informació necessària.", "l29": "3.Используйте различные параметры печати, такие как температура и ширина линии.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!", "l30": "Как калибровать",
"ls5": "Per què necessiteu el mallat adaptatiu:", "l31": "Мы предоставляем три распространенных метода калибровки. Вы можете выбрать тот метод калибровки, который вам нравится.",
"ls6": "Com desactivar aquesta funció:", "l32": "В результатах теста есть погрешность в 10%, но она практически неразличима невооруженным глазом. Выберите подходящее значение и сохраните его, а затем начните печать.",
"ls7": "1. Sense informació de la sonda malgastada, l'Adaptive Meshing generarà una malla només a l'àrea que realment la necessiteu: això significa que l'anivellament abans de cada impressió triga molt menys temps!", "l33": "Если вам необходимо изменить параметры печати, сначала измените и сохраните параметры, а затем выполните процедуру калибровки.",
"ls8": "2.Com que l'àrea de la malla serà més petita, la malla pot ser molt més densa. Imagineu-vos fent una malla de 3x3, però de la mida d'un 3DBenchy! El que us ofereix una experiència d'impressió de major precisió.", "l34": "После завершения печати введите наилучшее значение в программу и сохраните его.",
"ls9": "Afegeix ';' abans de 'G29' al codi G inicial.", "l35": "Линия PA",
"ls10": "Zona vermella: Anivellament general de malla.", "l36": "После завершения печати выберите самую плавную линию, введите соответствующее ей значение в программу и сохраните.",
"ls11": "Zona verda: Anivellació adaptativa de malla.", "l37": "Шаблон PA",
}, "l38": "После завершения печати выберите самый плоский набор линий, введите соответствующие значения в программу и сохраните.",
"cs": { "l39": "ПА Тауэр",
"t0": "Vítejte v QIDISlicer", "l40": "Значение опережения давления будет меняться каждые 5 мм подъема башни PA. Измерьте высоту лучшей области модели, а затем рассчитайте соответствующее значение опережения давления на основе установленного значения шага, затем введите его в программное обеспечение и сохраните.",
"t1": "Uživatelská příručka",
"t2": "První výtisk",
"t3": "Přidat podporu",
"t4": "Připojit zařízení",
"t5": "Bezdrátové odesílání",
"t6": "Hlášení o problému",
"t7": "Demonstrace",
"t8": "Informace o produktu",
"t9": "Kontaktujte nás",
"t10": "Filament",
"t11": "Přidat text",
"t12": "Vyříznutý model",
"t13": "Stáhnout model",
"t14": "Adaptivní mřížka",
"ls0": "Další informace:",
"ls1": "3D modely si můžete stáhnout z následujících webových stránek.",
"ls2": "Máte-li jakékoli dotazy týkající se tiskárny nebo QIDISlicer, kontaktujte nás prosím prostřednictvím příslušné e-mailové adresy nebo Skype.",
"ls3": "Poznámka: Zkuste nám prosím sdělit své požadavky ve formě videa nebo obrázků a poskytněte soubor 3MF, soubor G-kódu, číslo stroje a další potřebné informace.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!",
"ls5": "Proč potřebujete adaptivní síťování:",
"ls6": "Jak tuto funkci zakázat:",
"ls7": "1. Žádné plýtvání informacemi ze sondy, adaptivní síťování vygeneruje síť pouze v oblasti, kde ji skutečně potřebujete: To znamená, že vyrovnání před každým tiskem zabere podstatně méně času!",
"ls8": "2. Vzhledem k tomu, že plocha sítě bude menší, může být síť mnohem hustší. Představte si, že vytvoříte síť 3x3, ale velikost 3DBenchy! Což vám přináší vyšší přesnost tisku.",
"ls9": "Přidat ';' před 'G29' v začátek G-code.",
"ls10": "Červená oblast: Obecné vyrovnání sítě.",
"ls11": "Zelená plocha: Adaptivní vyrovnání sítě.",
},
"es": {
"t0": "Bienvenido a QIDISlicer",
"t1": "Guía del usuario",
"t2": "Primera de impresión",
"t3": "Agregar soporte",
"t4": "Conectar dispositivo",
"t5": "Envío inalámbrico",
"t6": "Informe de problema",
"t7": "Demostración",
"t8": "Información del producto",
"t9": "Contacta con nosotros",
"t10": "Filamento",
"t11": "Agregar texto",
"t12": "Modelo cortado",
"t13": "Descargar Modelo",
"t14": "Malla adaptativa",
"ls0": "Aprende más:",
"ls1": "Puede descargar modelos 3D de los siguientes sitios web.",
"ls2": "Si tiene alguna pregunta sobre la impresora o QIDISlicer, comuníquese con nosotros a través de la dirección de correo electrónico correspondiente o Skype.",
"ls3": "Nota: intente decirnos sus requisitos en forma de video o imágenes, y proporcione el archivo 3MF, el archivo de código G, el número de máquina y otra información necesaria.",
"ls4": "A unique leveling solution for Klipper-enabled 3D printers!",
"ls5": "Por qué necesita Mallado adaptativo:",
"ls6": "Cómo deshabilitar esta función:",
"ls7": "1. Sin desperdiciar información de la sonda, Adaptive Meshing generará una malla solo en el área que realmente la necesita: ¡esto significa que nivelar antes de cada impresión lleva mucho menos tiempo!",
"ls8": "2. Dado que el área de la malla será más pequeña, la malla puede ser mucho más densa. ¡Imagina hacer una malla de 3x3, pero del tamaño de un 3DBenchy! Lo que le brinda una experiencia de impresión de mayor precisión.",
"ls9": "Agregar ';' antes de 'G29' en el Código G inicial.",
"ls10": "Zona roja: Nivelación general de mallas.",
"ls11": "Zona verde: Nivelación de malla adaptativa.",
}, },
}; };

View File

@@ -16,91 +16,80 @@ html, body {
background-color: #F4F7FE; background-color: #F4F7FE;
} }
.LinkBtn {
font-size: 12px;
color: #1200FF;
cursor: pointer;
}
.LinkBtn:hover {
text-decoration: underline;
}
/*------------------*/
* {
/* 页面初始化 清除元素原有的内外边距 */
padding: 0;
margin: 0;
/* 盒子模型 */
box-sizing: border-box;
}
body { body {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
/* 让页面占浏览器可视区域的高度 */
height: 100vh; height: 100vh;
} }
#LeftBoard { .LeftBoard {
/*border-right-width: 1px;
border-right-style: solid;*/
background: #414345; background: #414345;
width:360px; width:360px;
min-width: 280px; min-width: 280px;
height: 100%; height: 100%;
} }
.GuideBtn {
height: 100px;
display: flex;
flex-direction: line;
align-items: center;
justify-content: center;
cursor: pointer;
color: #4479FB;
font-size: 24px;
}
.GuideBtn:hover {
font-size: 26px;
}
.Logo {
height: 40px;
display: flex;
margin-right: 10px;
}
/*导航栏属性*/
li { li {
/* 清除li元素前面的项目符号 */
list-style: none; list-style: none;
} }
.accordion { .accordion {
/* 溢出隐藏 */
overflow: hidden; overflow: hidden;
/* 圆角属性 */
border-radius: 0 8px 8px 0; border-radius: 0 8px 8px 0;
/* 盒子阴影 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
} }
.accordion > li { .accordion > li {
width: 100%; width: 100%;
} }
.accordion > li input { .accordion > li input {
/* 将单选按钮隐藏起来 */
display: none; display: none;
} }
.accordion > li label { .accordion > li label {
/* 相对定位 */
position: relative; position: relative;
/* label元素是行内元素 需要将其转为块级元素 才能设置宽度和高度 */
display: block; display: block;
width: 100%; width: 100%;
padding: 20px 50px; padding: 20px 50px;
color: #fff; color: #FFFFFF;
background-color: #4479FB; background-color: #4479FB;
/* 鼠标移入变小手 */
cursor: pointer; cursor: pointer;
} }
.accordion > li label i { .accordion > li label i {
/* 绝对定位 */
position: absolute; position: absolute;
/* calc方法自动计算数值 让字体图标垂直居中 */
top: calc(50% - 12px); top: calc(50% - 12px);
left: 20px; left: 20px;
font-size: 24px; font-size: 24px;
/* 加过渡 */
transition: all 0.5s; transition: all 0.5s;
} }
.accordion > li label:hover { .accordion > li label:hover {
border-left-color: #F1F1FF; border-left-color: #FFFFFF;
border-left-width: 4px; border-left-width: 4px;
border-style: solid; border-style: solid;
} }
/* :not()选择器选取的是除括号里的元素以外的其它元素 :first-child选择器是第一个元素 */
.accordion > li:not(:first-child) label { .accordion > li:not(:first-child) label {
border-top: 1px solid #38814d; border-top: 1px solid #38814D;
} }
.accordion > li ol { .accordion > li ol {
width: 100%; width: 100%;
@@ -135,307 +124,168 @@ li {
.accordion > li input:checked ~ ol li { .accordion > li input:checked ~ ol li {
height: 50px; height: 50px;
} }
/*导航栏属性*/
#GuideArea { .MenuBtn {
/*border-bottom-width:1px; height: 50px;
border-bottom-style:inset;*/
height: 100px;
display: flex; display: flex;
flex-direction: column; flex-direction: line;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
position: relative;
width: 100%;
}
#Guide1 {
height: 36px;
line-height: 36px;
display: flex;
justify-content: center;
user-select: none;
}
#GuideBtn {
cursor: pointer; cursor: pointer;
color: #FFFFFF;
transition: all 0.5s;
}
.MenuBtn:hover {
color: #4479FB; color: #4479FB;
} }
.TbItem { .MenuBtnSelected {
font-size: 24px;
display: flex;
justify-content: flex-start;
cursor: pointer;
color: #4479FB
}
.TbItem:hover {
font-size: 25px;
}
.TbItemSelected {
}
#Icon1 {
margin-right: 10px;
}
#QDIcon {
height: 36px;
}
/*------------------*/
#BtnArea {
padding: 0;
margin: 0;
}
.BtnItem {
padding-left: 30px;
height: 50px;
line-height: 50px;
text-indent: 10px;
display: flex;
justify-content: flex-start;
cursor: pointer;
color: white;
transition: all 0.5s;
}
.BtnItem:hover {
color: #4479FB
}
.BtnItemSelected {
background-color: #CED1D9; background-color: #CED1D9;
color: #4479FB color: #4479FB;
} }
.BtnIcon { .MenuBtnIcon {
margin-left: 15px; margin: auto 15px;
display: flex;
flex-direction: column;
justify-content: center;
}
.BookIcon {
display: flex;
flex-direction: column;
justify-content: center;
}
.MainIcon {
width: 20px; width: 20px;
height: 20px; height: 20px;
display: flex;
} }
.LeftIcon { .RightBoard {
width: 30px;
height: 30px;
padding-left: 10px;
}
/*--------------------*/
#RightBoard {
width: 100%; width: 100%;
height: 100%; height: 100%;
min-width: 835px; min-width: 700px;
padding: 0px 50px;
overflow-y: auto; overflow-y: auto;
} }
#ThumbBoard .GuideBoard {
{ margin-top: 20px;
overflow-y:auto;
padding: 0px 40px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-content: flex-start;
margin: 30px 40px;
position: relative;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
} }
@media screen and (max-width: 1100px) { .Thumbnail {
.GuideBlock { min-width: 300px;
width: calc(50% - 20px); max-width: 600px;
margin: 0px 10px 40px 10px; width: 23%;
cursor: pointer; margin: 10px auto;
}
}
@media screen and ( min-width:1101px) and ( max-width:1680px ) {
.GuideBlock {
width: calc(33% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and ( min-width:1681px) and (max-width:2120px ) {
.GuideBlock {
width: calc(25% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and ( min-width:2121px) and (max-width:2620px ) {
.GuideBlock {
width: calc(20% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and (min-width:2621px ) and (max-width:3120px ) {
.GuideBlock {
width: calc(16.6% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and (min-width:3121px ) {
.GuideBlock {
width: calc(14.2% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
#IssueBoard {
overflow-y: auto;
padding: 0px 40px;
display: flex; display: flex;
flex-wrap: wrap; flex-direction: column;
justify-content: center; cursor: pointer;
align-items: center;
margin: 30px 40px;
position: relative;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
} }
@media screen{ .ThumbnailTitle {
.halfBlock { text-align: center;
text-align: center; font-size: 18px;
justify-content: center; font-weight: 700;
width: calc(50% - 20px); width: 100%;
margin: 0px 10px; color: #4479FB;
}
} }
@media screen and (max-width: 1100px) { .ImageBoard {
.PrintBlock { margin: 50px auto;
text-align:center;
width: calc(50% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and ( min-width:1101px) and ( max-width:1680px ) {
.PrintBlock {
width: calc(33% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and ( min-width:1681px) and (max-width:2120px ) {
.PrintBlock {
width: calc(25% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and ( min-width:2121px) and (max-width:2620px ) {
.PrintBlock {
width: calc(20% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and (min-width:2621px ) and (max-width:3120px ) {
.PrintBlock {
width: calc(16.6% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
@media screen and (min-width:3121px ) {
.PrintBlock {
width: calc(14.2% - 20px);
margin: 0px 10px 40px 10px;
cursor: pointer;
}
}
#FilaBigBoard {
display: none; display: none;
} }
#FilaBoard { .AutozoomImage img {
width: 100%;
max-width: 1200px;
border-radius: 5px;
border: 1px solid #4479FB;
margin: 20px auto;
display: flex; display: flex;
flex-wrap: wrap;
text-align: center;
justify-content: center; justify-content: center;
height: 100vh;
overflow-y: auto;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
box-sizing: border-box;
} }
.main { .IntroduceBoard {
margin: 50px 50px; margin: 50px auto;
display: none;
}
.IntroduceTitle {
text-align: center;
font-size: 36px;
font-weight: 700;
margin: 30px 0;
color: #4479FB;
line-height: 50px;
}
.IntroduceText {
font-size: 16px;
line-height: 20px;
}
.IntroduceTextBold {
font-size: 16px;
font-weight: 700;
line-height: 25px;
}
.CenterImage img{
margin: 20px auto;
max-width: 1200px;
border-radius: 5px;
border: 1px solid #4479FB;
display: flex;
justify-content: center;
}
.IntroduceTextRed {
font-size: 16px;
color: #ED1C24;
line-height: 20px;
text-align: center;
}
.IntroduceTextGreen {
font-size: 16px;
color: #24AB4D;
line-height: 20px;
text-align: center;
}
.FilamentsTable {
display: flex;
justify-content: center;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
} }
/*表格属性*/
td, th { td, th {
/* 设置td,th宽度高度 */
border: 1px solid gray; border: 1px solid gray;
font-size: 12px; font-size: 12px;
width: 100px; width: 100px;
height: 50px; height: 50px;
text-align: center;
} }
th { th {
background-color: #4F6EBC; background-color: #4F6EBC;
color: #fff; color: #FFFFFF;
} }
table { table {
table-layout: fixed; table-layout: fixed;
width: 200px; /* 固定宽度 */ width: 200px;
} }
td:first-child, th:first-child { td:first-child, th:first-child {
position: sticky; position: sticky;
left: 0; /* 首行永远固定在左侧 */ left: 0;
z-index: 1; z-index: 1;
background: #CDDBFB; background: #CDDBFB;
} }
th:nth-child(odd) { /*奇表头*/ th:nth-child(odd) {
background-color: #6993FB; background-color: #6993FB;
} }
thead tr th { thead tr th {
position: sticky; position: sticky;
top: 0; /* 列首永远固定在头部 */ top: 0;
} }
tbody tr:nth-child(odd) { tbody tr:nth-child(odd) {
@@ -445,121 +295,22 @@ tbody tr:nth-child(odd) {
th:first-child { th:first-child {
z-index: 2; z-index: 2;
} }
/*表格属性*/
/*------Gif------*/ .IssueBoard {
#GifBigBoard { margin-top: 20px;
display: none; display: none;
} }
#GifBoard { .EmailBlock {
margin: 10px auto;
display: flex; display: flex;
flex-wrap: wrap;
align-content: flex-start;
align-items: center;
justify-content: center; justify-content: center;
overflow-y: auto; flex-wrap: wrap;
margin: 30px 40px;
position: relative;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
} }
@media screen { .PrinterBlock {
.GifBlock { margin: 0 10px;
margin: 0px 0px 0px 0px; display: flex;
cursor: pointer; flex-direction: column;
}
} }
.UG_PRINTER {
text-align: center;
width: 100%;
}
.UG_PRINTER img {
width: 15%;
}
.UG_IMG {
justify-content:center;
text-align:center;
width: 100%;
}
.UG_IMG img {
width: 100%;
border-radius: 5px 5px 5px 5px;
}
.UG_TITLE {
text-align: center;
font-size: 18px;
font-weight: 700;
line-height: 25px;
width: 100%;
margin-top: 20px;
margin-bottom: 4px;
color: #4479FB;
}
.UG_DESC {
font-size: 14px;
width: 100%;
line-height: 20px;
}
.UG_DESC_RED {
font-size: 14px;
width: 100%;
line-height: 20px;
color: #ED1C24;
}
.UG_DESC_GREEN {
font-size: 14px;
width: 100%;
line-height: 20px;
color: #24AB4D;
}
.UG_CDESC {
margin-top:35px;
margin-bottom:5px;
text-align: center;
font-size: 24px;
width: 100%;
line-height: 20px;
}
.UG_B_TITLE {
text-align: center;
font-size: 36px;
font-weight: 700;
line-height: 25px;
width: 100%;
margin-top: 20px;
margin-bottom: 25px;
color: #4479FB;
}
.UG_B_DESC {
font-size: 16px;
width: 100%;
line-height: 20px;
margin-bottom: 50px;
}
.UG_AMB_DESC {
font-size: 16px;
font-weight: 700;
width: 100%;
line-height: 20px;
}
.UG_AM_DESC {
font-size: 16px;
width: 100%;
line-height: 20px;
}

View File

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View File

Before

Width:  |  Height:  |  Size: 721 KiB

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

View File

Before

Width:  |  Height:  |  Size: 460 KiB

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 502 KiB

After

Width:  |  Height:  |  Size: 502 KiB

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1687913925905" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8764" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M264.153704 952.655092 64.311213 952.655092 64.311213 224.110197l199.843514 0L264.154727 952.655092 264.153704 952.655092zM229.954811 261.241886 98.815051 261.241886l0 67.14219 131.139759 0L229.954811 261.241886 229.954811 261.241886zM326.469981 64.541969l198.266599 0 0 888.044562L326.469981 952.58653 326.469981 64.541969 326.469981 64.541969zM495.458767 98.756211 360.142895 98.756211l0 69.234851 135.314849 0L495.457744 98.756211 495.458767 98.756211zM954.857758 892.768143l-190.012611 61.911048-225.718835-692.706665 190.010565-61.911048L954.857758 892.768143 954.857758 892.768143zM708.125331 245.963924 583.437517 286.590204l20.798698 63.833841 124.685767-40.627303L708.125331 245.963924 708.125331 245.963924zM708.125331 245.963924" fill="#4479FB" p-id="8765"></path></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -2,496 +2,492 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="Cache-Control" content="max-age=7200" /> <meta http-equiv="Cache-Control" content="max-age=7200"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" /> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
<title>homepage</title> <title>homepage</title>
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css" /> <link rel="stylesheet" type="text/css" href="css/font-awesome.min.css"/>
<link rel="stylesheet" type="text/css" href="css/home.css" /> <link rel="stylesheet" type="text/css" href="css/home.css"/>
<link rel="stylesheet" type="text/css" href="css/dark.css" /> <link rel="stylesheet" type="text/css" href="css/dark.css"/>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script> <script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="../data/text.js"></script> <script type="text/javascript" src="../data/text.js"></script>
<script type="text/javascript" src="js/json2.js"></script> <script type="text/javascript" src="js/json2.js"></script>
<script type="text/javascript" src="js/globalapi.js"></script> <script type="text/javascript" src="js/globalapi.js"></script>
<script type="text/javascript" src="js/home.js"></script> <script type="text/javascript" src="js/home.js"></script>
</head> </head>
<body class="ZScrol" onLoad="OnInit()">
<div id="LeftBoard"> <body onLoad="OnInit()">
<div id="GuideArea"> <div class="LeftBoard">
<div id="Guide1"> <div menu="UserGuide" class="GuideBtn" onClick="GotoMenu('UserGuide')">
<div id="Icon1"><img id="QDIcon" src="img/logo.png" /></div> <div class="Logo"><img src="img/logo.png"/></div>
<div menu="userguide" class="TbItem" onClick="GotoMain('userguide');"> <div class="trans" tid="t1"></div>
<div class="trans" tid="t1">UserGuide</div>
</div>
</div>
</div> </div>
<ul class="accordion"> <ul class="accordion">
<li> <li>
<input type="radio" name="item" id="item1" /> <input type="radio" name="GuideMenu" id="GuideMenu1"/>
<label for="item1"> <label for="GuideMenu1">
<a class="trans" tid="t7"></a> <a class="trans" tid="t7"></a>
<i class="fa fa-caret-right"></i> <i class="fa fa-caret-right"></i>
</label> </label>
<ol> <ol>
<li menu="firstprint" class="BtnItem" onClick="GotoMenu('firstprint')"> <li menu="FirstPrint" class="MenuBtn" onClick="GotoMenu('FirstPrint')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t2">UserGuide</div> <div class="trans" tid="t2"></div>
</li> </li>
<li menu="addsupport" class="BtnItem" onClick="GotoMenu('addsupport')"> <li menu="AddSupport" class="MenuBtn" onClick="GotoMenu('AddSupport')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t3">UserGuide</div> <div class="trans" tid="t3"></div>
</li> </li>
<li menu="wifisend" class="BtnItem" onClick="GotoMenu('wifisend')"> <li menu="WifiSend" class="MenuBtn" onClick="GotoMenu('WifiSend')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t5">UserGuide</div> <div class="trans" tid="t5"></div>
</li> </li>
<li menu="connectdevice" class="BtnItem" onClick="GotoMenu('connectdevice')"> <li menu="ConnectDevice" class="MenuBtn" onClick="GotoMenu('ConnectDevice')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t4">UserGuide</div> <div class="trans" tid="t4"></div>
</li> </li>
<li menu="addtext" class="BtnItem" onClick="GotoMenu('addtext')"> <li menu="AddText" class="MenuBtn" onClick="GotoMenu('AddText')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t11">UserGuide</div> <div class="trans" tid="t11"></div>
</li> </li>
<li menu="cutmodel" class="BtnItem" onClick="GotoMenu('cutmodel')"> <li menu="CutModel" class="MenuBtn" onClick="GotoMenu('CutModel')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t12">UserGuide</div> <div class="trans" tid="t12"></div>
</li> </li>
<li menu="admeshing" class="BtnItem" onClick="GotoMenu('admeshing')"> <li menu="AdaptiveMeshing" class="MenuBtn" onClick="GotoMenu('AdaptiveMeshing')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t14">UserGuide</div> <div class="trans" tid="t14"></div>
</li> </li>
<li menu="downloadm" class="BtnItem" onClick="GotoMenu('downloadm')"> <li menu="ModelDownload" class="MenuBtn" onClick="GotoMenu('ModelDownload')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t13">UserGuide</div> <div class="trans" tid="t13"></div>
</li> </li>
</ol> </ol>
</li> </li>
<li> <li>
<input type="radio" name="item" id="item2" /> <input type="radio" name="GuideMenu" id="GuideMenu2"/>
<label for="item2"> <label for="GuideMenu2">
<a class="trans" tid="t16"></a>
<i class="fa fa-caret-right"></i>
</label>
<ol>
<li menu="Flowrate" class="MenuBtn" onClick="GotoMenu('Flowrate')">
<div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t15"></div>
</li>
<li menu="PressureAdvance" class="MenuBtn" onClick="GotoMenu('PressureAdvance')">
<div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t17"></div>
</li>
</ol>
</li>
<li>
<input type="radio" name="GuideMenu" id="GuideMenu3"/>
<label for="GuideMenu3">
<a class="trans" tid="t8"></a> <a class="trans" tid="t8"></a>
<i class="fa fa-caret-right"></i> <i class="fa fa-caret-right"></i>
</label> </label>
<ol> <ol>
<li menu="infomax3" class="BtnItem" onClick="GotoMenu('infomax3')"> <li menu="X-MAX 3" class="MenuBtn" onClick="GotoMenu('X-MAX 3')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="TextS1">X-MAX 3</div> <div>X-MAX 3</div>
</li> </li>
<li menu="infoplus3" class="BtnItem" onClick="GotoMenu('infoplus3')"> <li menu="X-Plus 3" class="MenuBtn" onClick="GotoMenu('X-Plus 3')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="TextS1">X-Plus 3</div> <div>X-Plus 3</div>
</li> </li>
<li menu="infosmart3" class="BtnItem" onClick="GotoMenu('infosmart3')"> <li menu="X-smart 3" class="MenuBtn" onClick="GotoMenu('X-smart 3')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="TextS1">X-Smart 3</div> <div>X-Smart 3</div>
</li> </li>
<li menu="infof1" class="BtnItem" onClick="GotoMenu('infof1')"> <li menu="Filaments" class="MenuBtn" onClick="GotoMenu('Filaments')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t10">FInfo</div> <div class="trans" tid="t10"></div>
</li> </li>
</ol> </ol>
</li> </li>
<li> <li>
<input type="radio" name="item" id="item3" /> <input type="radio" name="GuideMenu" id="GuideMenu4"/>
<label for="item3"> <label for="GuideMenu4">
<a class="trans" tid="t9"></a> <a class="trans" tid="t9"></a>
<i class="fa fa-caret-right"></i> <i class="fa fa-caret-right"></i>
</label> </label>
<ol> <ol>
<li menu="issuereport" class="BtnItem" onClick="GotoMenu('issuereport')"> <li menu="IssueReport" class="MenuBtn" onClick="GotoMenu('IssueReport')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i1.svg" /></div> <div class="MenuBtnIcon"><img src="img/MenuBtnIcon.svg"/></div>
<div class="trans" tid="t6">UserGuide</div> <div class="trans" tid="t6"></div>
</li> </li>
</ol> </ol>
</li> </li>
</ul> </ul>
</div> </div>
<div id="RightBoard">
<div id="ThumbBoard" class="ZScrol" board="userguide"> <div class="RightBoard">
<div class="GuideBlock" onClick="GotoMenu('firstprint');"> <div class="GuideBoard" board="UserGuide">
<div class="UG_IMG"><img src="img/FirstPrint.gif" /></div> <div class="Thumbnail" onClick="GotoMenu('FirstPrint')">
<div class="UG_TITLE trans TextS1" tid="t2">FirstPrint</div> <div class="AutozoomImage"><img src="img/FirstPrint.gif"/></div>
<div class="ThumbnailTitle trans" tid="t2"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('addsupport');"> <div class="Thumbnail" onClick="GotoMenu('AddSupport')">
<div class="UG_IMG"><img src="img/AddSupport.gif" /></div> <div class="AutozoomImage"><img src="img/AddSupport.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t3">AddSupport</div> <div class="ThumbnailTitle trans" tid="t3"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('wifisend');"> <div class="Thumbnail" onClick="GotoMenu('WifiSend')">
<div class="UG_IMG"><img src="img/WifiSend.gif" /></div> <div class="AutozoomImage"><img src="img/WifiSend.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t5">WifiSend</div> <div class="ThumbnailTitle trans" tid="t5"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('connectdevice');"> <div class="Thumbnail" onClick="GotoMenu('ConnectDevice')">
<div class="UG_IMG"><img src="img/ConnectDevice.gif" /></div> <div class="AutozoomImage"><img src="img/ConnectDevice.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t4">ConnectDevice</div> <div class="ThumbnailTitle trans" tid="t4"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('addtext');"> <div class="Thumbnail" onClick="GotoMenu('AddText')">
<div class="UG_IMG"><img src="img/AddText.gif" /></div> <div class="AutozoomImage"><img src="img/AddText.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t11">AddText</div> <div class="ThumbnailTitle trans" tid="t11"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('cutmodel');"> <div class="Thumbnail" onClick="GotoMenu('CutModel')">
<div class="UG_IMG"><img src="img/CutModel.gif" /></div> <div class="AutozoomImage"><img src="img/CutModel.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t12">CutModel</div> <div class="ThumbnailTitle trans" tid="t12"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('admeshing');"> <div class="Thumbnail" onClick="GotoMenu('AdaptiveMeshing')">
<div class="UG_IMG"><img src="img/AMPNG.png" /></div> <div class="AutozoomImage"><img src="img/AdaptiveMeshingThumbnail.png"/></div>
<div class="UG_TITLE trans TextS1" tid="t14">AddText</div> <div class="ThumbnailTitle trans" tid="t14"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('downloadm');"> <div class="Thumbnail" onClick="GotoMenu('ModelDownload')">
<div class="UG_IMG"><img src="img/DownloadModel.png" /></div> <div class="AutozoomImage"><img src="img/DownloadModel.png"/></div>
<div class="UG_TITLE trans TextS1" tid="t13">AddText</div> <div class="ThumbnailTitle trans" tid="t13"></div>
</div> </div>
<div class="GuideBlock" onClick="GotoMenu('issuereport');"> <div class="Thumbnail" onClick="GotoMenu('Flowrate')">
<div class="UG_IMG"><img src="img/IssueReport.png" /></div> <div class="AutozoomImage"><img src="img/FlowrateCoarse.gif"/></div>
<div class="UG_TITLE trans TextS1" tid="t6">IssueReport</div> <div class="ThumbnailTitle trans" tid="t15"></div>
</div>
<div class="Thumbnail" onClick="GotoMenu('PressureAdvance')">
<div class="AutozoomImage"><img src="img/PressureAdvance.gif"/></div>
<div class="ThumbnailTitle trans" tid="t17"></div>
</div>
<div class="Thumbnail" onClick="GotoMenu('IssueReport')">
<div class="AutozoomImage"><img src="img/IssueReport.png"/></div>
<div class="ThumbnailTitle trans" tid="t6"></div>
</div> </div>
</div> </div>
<div id="GifBigBoard" board="firstprint"> <div class="ImageBoard" board="FirstPrint">
<div id="GifBoard"> <div class="IntroduceTitle trans" tid="t2"></div>
<div class="GifBlock"> <div class="AutozoomImage"><img src="img/FirstPrint.gif"/></div>
<div class="UG_IMG"><img src="img/FirstPrint.gif" /></div> </div>
</div>
<div class="ImageBoard" board="AddSupport">
<div class="IntroduceTitle trans" tid="t3"></div>
<div class="AutozoomImage"><img src="img/AddSupport.gif"/></div>
</div>
<div class="ImageBoard" board="ConnectDevice">
<div class="IntroduceTitle trans" tid="t5"></div>
<div class="AutozoomImage"><img src="img/ConnectDevice.gif"/></div>
</div>
<div class="ImageBoard" board="WifiSend">
<div class="IntroduceTitle trans" tid="t4"></div>
<div class="AutozoomImage"><img src="img/WifiSend.gif"/></div>
</div>
<div class="ImageBoard" board="AddText">
<div class="IntroduceTitle trans" tid="t11"></div>
<div class="AutozoomImage"><img src="img/AddText.gif"/></div>
</div>
<div class="ImageBoard" board="CutModel">
<div class="IntroduceTitle trans" tid="t12"></div>
<div class="AutozoomImage"><img src="img/CutModel.gif"/></div>
</div>
<div class="IntroduceBoard" board="AdaptiveMeshing">
<div class="IntroduceTitle trans" tid="t14"></div>
<div class="IntroduceTextBold trans" tid="l5"></div>
<div class="IntroduceText trans" tid="l7"></div>
<div class="IntroduceText trans" tid="l8"></div>
<div class="CenterImage"><img src="img/AdaptiveMeshing.png"/></div>
<div class="IntroduceTextRed trans" tid="l10"></div>
<div class="IntroduceTextGreen trans" tid="l11"></div>
<div class="IntroduceTextBold trans" tid="l6"></div>
<div class="IntroduceText trans" tid="l9"></div>
<div class="CenterImage"><img src="img/DisableAM.png"/></div>
<div class="IntroduceTextBold trans" tid="l0"></div>
<div class="IntroduceText">https://github.com/kyleisah/Klipper-Adaptive-Meshing-Purging#key-features</div>
</div>
<div class="IntroduceBoard" board="ModelDownload">
<div class="IntroduceTitle trans" tid="t13"></div>
<div class="IntroduceTextBold trans" tid="l1"></div>
<div class="IntroduceText">https://makerworld.com</div>
<div class="AutozoomImage"><img src="img/MakerWorld.png"/></div>
<div class="IntroduceText">https://www.thingiverse.com</div>
<div class="AutozoomImage"><img src="img/thingiverse.png"/></div>
<div class="IntroduceText">https://www.printables.com/model</div>
<div class="AutozoomImage"><img src="img/printables.png"/></div>
</div>
<div class="IntroduceBoard" board="Flowrate">
<div class="IntroduceTitle trans" tid="t15"></div>
<div class="IntroduceTextBold trans" tid="l12"></div>
<div class="IntroduceText trans" tid="l13"></div>
<div class="IntroduceText trans" tid="l14"></div>
<div class="CenterImage"><img src="img/FlowrateValue.png"/></div>
<div class="IntroduceText trans" tid="l15"></div>
<div class="IntroduceText trans" tid="l16"></div>
<div class="CenterImage"><img src="img/FlowrateCompare.png"/></div>
<div class="IntroduceTextBold trans" tid="l17"></div>
<div class="IntroduceText trans" tid="l18"></div>
<div class="IntroduceText trans" tid="l19"></div>
<div class="IntroduceText trans" tid="l20"></div>
<div class="AutozoomImage"><img src="img/FlowrateCoarse.gif"/></div>
<div class="IntroduceText trans" tid="l21"></div>
<div class="CenterImage"><img src="img/FlowrateCoarse.png"/></div>
<div class="IntroduceTextBold trans" tid="l22"></div>
<div class="IntroduceText trans" tid="l23"></div>
<div class="IntroduceText trans" tid="l20"></div>
<div class="AutozoomImage"><img src="img/FlowrateFine.gif"/></div>
<div class="IntroduceText trans" tid="l24"></div>
<div class="CenterImage"><img src="img/FlowrateFine.png"/></div>
</div>
<div class="IntroduceBoard" board="PressureAdvance">
<div class="IntroduceTitle trans" tid="t17"></div>
<div class="IntroduceText trans" tid="l25"></div>
<div class="CenterImage"><img src="img/PressureAdvanceCompare.png"/></div>
<div class="IntroduceTextBold trans" tid="l26"></div>
<div class="IntroduceText trans" tid="l27"></div>
<div class="IntroduceText trans" tid="l28"></div>
<div class="IntroduceText trans" tid="l29"></div>
<div class="IntroduceTextBold trans" tid="l30"></div>
<div class="IntroduceText trans" tid="l31"></div>
<div class="IntroduceText trans" tid="l32"></div>
<div class="AutozoomImage"><img src="img/PressureAdvance.gif"/></div>
<div class="IntroduceText trans" tid="l33"></div>
<div class="IntroduceText trans" tid="l34"></div>
<div class="CenterImage"><img src="img/PressureAdvanceValue.png"/></div>
<div class="IntroduceTextBold trans" tid="l35"></div>
<div class="IntroduceText trans" tid="l36"></div>
<div class="CenterImage"><img src="img/PressureAdvanceLine.png"/></div>
<div class="IntroduceTextBold trans" tid="l37"></div>
<div class="IntroduceText trans" tid="l38"></div>
<div class="CenterImage"><img src="img/PressureAdvancePattern.png"/></div>
<div class="IntroduceTextBold trans" tid="l39"></div>
<div class="IntroduceText trans" tid="l40"></div>
<div class="CenterImage"><img src="img/PressureAdvanceTower.png"/></div>
</div>
<div class="IntroduceBoard" board="X-MAX 3">
<div class="AutozoomImage"><img src="img/X-MAX3Poster.png"/></div>
</div>
<div class="IntroduceBoard" board="X-Plus 3">
<div class="AutozoomImage"><img src="img/X-Plus3Poster.png"/></div>
</div>
<div class="IntroduceBoard" board="X-smart 3">
<div class="AutozoomImage"><img src="img/X-smart3Poster.png"/></div>
</div>
<div class="IntroduceBoard" board="Filaments">
<div class="FilamentsTable">
<table cellspacing="0">
<thead>
<tr>
<th>Filament</th>
<th>Drying box</th>
<th>Anneal</th>
<th>Water resistance</th>
<th>Corrosion resistance</th>
<th>Creep resistance</th>
<th>HDT 0.45</th>
<th>HDT 1.80</th>
<th>Tensile strength(MPa)</th>
<th>Tensile modulus(MPa)</th>
<th>Elongation at break(%)</th>
<th>Flexural strength(MPa)</th>
<th>Flexural modulus(MPa)</th>
<th>Notch impact strength(KJ/㎡)</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in 1" :key="index">
<td>ToughPETG</td>
<td>≤20</td>
<td>/</td>
<td>+++</td>
<td>++</td>
<td>+++</td>
<td>78℃</td>
<td>73℃</td>
<td>40.3±0.6</td>
<td>1780±80</td>
<td>4.0±0.2</td>
<td>62.8±0.4</td>
<td>1919±54</td>
<td>13.9±2.3</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>UltraABS-GF25</td>
<td>≤30</td>
<td>/</td>
<td>+++</td>
<td>+</td>
<td>+++</td>
<td>97℃</td>
<td>93℃</td>
<td>48.48 ± 0.61</td>
<td>3752.13 ± 68.39</td>
<td>2.10 ± 0.10</td>
<td>78.80 ± 1.26</td>
<td>3531.71 ± 75.79</td>
<td>8.91 ± 0.63</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>ABS Rapido</td>
<td>≤30</td>
<td>/</td>
<td>++</td>
<td>+</td>
<td>+++</td>
<td>85℃</td>
<td>/</td>
<td>41 ± 1</td>
<td>3850 ± 150</td>
<td>11 ± 1</td>
<td>77.5 ± 2.5</td>
<td>77.5 ± 2.5</td>
<td>20 ± 2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PA12-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>+</td>
<td>++</td>
<td>+</td>
<td>149.6℃</td>
<td>112.4℃</td>
<td>87.49 ± 2.81</td>
<td>5438.40 ± 282.82</td>
<td>2.59 ± 0.19</td>
<td>133.17 ± 4.66</td>
<td>4667.43 ± 339.80</td>
<td>6.11 ± 1.45</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PATH-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>++</td>
<td>+++</td>
<td>+++</td>
<td>192.3℃</td>
<td>121.7℃</td>
<td>104.90 ± 1.99</td>
<td>8383.26 ± 419.53</td>
<td>1.60 ± 0.07</td>
<td>147.70 ± 4.09</td>
<td>5969.35 ± 145.28</td>
<td>6.17 ± 0.2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PET-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>+++</td>
<td>+++</td>
<td>+++</td>
<td>148.8℃</td>
<td>112.1℃</td>
<td>87.41 ± 3.57</td>
<td>6025.53 ± 355.46</td>
<td>1.99 ±0.18</td>
<td>122.69 ± 5.19</td>
<td>5313.21 ± 197.89</td>
<td>5.57 ± 0.58</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PLA Rapido</td>
<td>≤30</td>
<td>/</td>
<td>+</td>
<td>+</td>
<td>+</td>
<td>57℃</td>
<td>/</td>
<td>39 ± 3</td>
<td>4650 ± 150</td>
<td>12.5 ± 2.5</td>
<td>72.5 ± 2.5</td>
<td>2850 ± 150</td>
<td>6 ± 2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PLA Rapido Matte</td>
<td>≤30</td>
<td>/</td>
<td>+</td>
<td>+</td>
<td>+</td>
<td>58℃</td>
<td>/</td>
<td>39 ± 3</td>
<td>2400 ± 200</td>
<td>3 ± 1</td>
<td>77.5 ± 2.5</td>
<td>2200 ± 200</td>
<td>7.5 ± 1.5</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>UltraPA</td>
<td>≤15</td>
<td>/</td>
<td>++</td>
<td>++</td>
<td>++</td>
<td>77.8℃</td>
<td>73.1℃</td>
<td>86.15 ± 0.56</td>
<td>3609.22 ± 153.31</td>
<td>11.68 ± 3.36</td>
<td>121.47 ± 3.14</td>
<td>3314.03 ±181.88</td>
<td>5.78 ± 0.30</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
<div id="GifBigBoard" board="addsupport"> <div class="IssueBoard" board="IssueReport">
<div id="GifBoard"> <div class="IntroduceTitle trans" tid="t6"></div>
<div class="GifBlock"> <div class="IntroduceText trans" tid="l2"></div>
<div class="UG_IMG"><img src="img/AddSupport.gif" /></div> <div class="IntroduceText trans" tid="l3"></div>
</div> <div class="EmailBlock">
</div> <div class="PrinterBlock">
</div> <div class="CenterImage"><img src="img/X-MAX 3.png"/></div>
<div class="ThumbnailTitle">X-MAX 3</div>
<div id="GifBigBoard" board="connectdevice"> <div class="IntroduceText">
<div id="GifBoard"> <b>E-mail:</b><br/>MAX3support@qd3dprinter.com<br>MAX3AMS@qd3dprinter.com<br/><br>
<div class="GifBlock"> <b>Skype:</b><br/>MAX3support@qd3dprinter.com
<div class="UG_IMG"><img src="img/ConnectDevice.gif" /></div>
</div>
</div>
</div>
<div id="GifBigBoard" board="wifisend">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_IMG"><img src="img/WifiSend.gif" /></div>
</div>
</div>
</div>
<div id="GifBigBoard" board="addtext">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_IMG"><img src="img/AddText.gif" /></div>
</div>
</div>
</div>
<div id="GifBigBoard" board="cutmodel">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_IMG"><img src="img/CutModel.gif" /></div>
</div>
</div>
</div>
<div id="GifBigBoard" board="admeshing">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_B_TITLE trans TextS1" tid="t14">AdaptiveMeshing</div>
<div class="UG_CDESC"> </div>
</div>
<div class="UG_AMB_DESC trans" tid="ls5"></div>
<div class="UG_AM_DESC trans" tid="ls7"></div>
<div class="UG_AM_DESC trans" tid="ls8"></div>
<div class="UG_CDESC"> </div>
<div class="GifBlock">
<div class="UG_IMG"><img src="img/AdaptiveMeshing.png" /></div>
<a class="UG_DESC_RED trans" tid="ls10"></a>
<a class="UG_DESC_GREEN trans" tid="ls11"></a>
<div class="UG_CDESC"> </div>
</div>
<div class="UG_AMB_DESC trans" tid="ls6"></div>
<div class="UG_AM_DESC trans" tid="ls9"></div>
<div class="UG_CDESC"> </div>
<div class="GifBlock">
<div class="UG_IMG"><img src="img/DisableAM.png" /></div>
<div class="UG_CDESC"> </div>
</div>
<div class="UG_AMB_DESC trans" tid="ls0"></div>
<div class="UG_AM_DESC">URL:https://github.com/kyleisah/Klipper-Adaptive-Meshing-Purging#key-features</div>
<div class="UG_CDESC"> </div>
<!--<div class="GifBlock">
<div class="UG_IMG"><img src="img/AdaptiveMeshing.png" /></div>
<div class="UG_CDESC"> </div>
</div>
<div class="GifBlock">
<div class="UG_AMB_DESC trans" tid="ls7"></div>
<div class="UG_AM_DESC trans" tid="ls10"></div>
<div class="UG_CDESC"> </div>
</div>
<div class="GifBlock">
<div class="UG_IMG"><img src="img/DisableAM.png" /></div>
<div class="UG_CDESC"> </div>
</div>-->
</div>
</div>
<div id="GifBigBoard" board="downloadm">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_B_TITLE trans TextS1" tid="t13">download</div>
<div class="UG_CDESC trans" tid="ls1"></div>
</div>
</div>
<div id="IssueBoard">
<div class="halfBlock">
<div class="UG_DESC">URL:https://www.thingiverse.com/</div>
<div class="UG_IMG"><img src="img/thingiverse.png" /></div>
</div>
<div class="halfBlock">
<div class="UG_DESC">URL:https://www.printables.com/model</div>
<div class="UG_IMG"><img src="img/printables.png" /></div>
</div>
</div>
</div>
<!--<div id="GifBigBoard" board="downloadm">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_B_TITLE trans" tid="t13">downloadm</div>
</div>
<div id="IssueBoard">
<div class="halfBlock">
<div class="UG_CDESC">URL:https://www.thingiverse.com/</div>
<div class="UG_IMG"><img src="img/thingiverse.png" /></div>
<div class="UG_CDESC">URL:https://www.printables.com/model</div>
<div class="UG_IMG"><img src="img/printables.png" /></div>
</div>
</div>
</div>
</div>-->
<div id="GifBigBoard" board="infomax3">
<div id="GifBoard">
<div class="UG_IMG"><img src="img/info1.png" /></div>
</div>
</div>
<div id="GifBigBoard" board="infoplus3">
<div id="GifBoard">
<div class="UG_IMG"><img src="img/info2.png" /></div>
</div>
</div>
<div id="GifBigBoard" board="infosmart3">
<div id="GifBoard">
<div class="UG_IMG"><img src="img/info3.png" /></div>
</div>
</div>
<div id="FilaBigBoard" board="infof1">
<div id="FilaBoard">
<div class="main">
<table cellspacing="0">
<thead>
<tr>
<th>Filament</th>
<th>Drying box</th>
<th>Anneal</th>
<th>Water resistance</th>
<th>Corrosion resistance</th>
<th>Creep resistance</th>
<th>HDT 0.45</th>
<th>HDT 1.80</th>
<th>Tensile strength(MPa)</th>
<th>Tensile modulus(MPa)</th>
<th>Elongation at break(%)</th>
<th>Flexural strength(MPa)</th>
<th>Flexural modulus(MPa)</th>
<th>Notch impact strength(KJ/㎡)</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in 1" :key="index">
<td>ToughPETG</td>
<td>≤20</td>
<td>/</td>
<td>+++</td>
<td>++</td>
<td>+++</td>
<td>78℃</td>
<td>73℃</td>
<td>40.3±0.6</td>
<td>1780±80</td>
<td>4.0±0.2</td>
<td>62.8±0.4</td>
<td>1919±54</td>
<td>13.9±2.3</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>UltraABS-GF25</td>
<td>≤30</td>
<td>/</td>
<td>+++</td>
<td>+</td>
<td>+++</td>
<td>97℃</td>
<td>93℃</td>
<td>48.48 ± 0.61</td>
<td>3752.13 ± 68.39</td>
<td>2.10 ± 0.10</td>
<td>78.80 ± 1.26</td>
<td>3531.71 ± 75.79</td>
<td>8.91 ± 0.63</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>ABS Rapido</td>
<td>≤30</td>
<td>/</td>
<td>++</td>
<td>+</td>
<td>+++</td>
<td>85℃</td>
<td>/</td>
<td>41 ± 1</td>
<td>3850 ± 150</td>
<td>11 ± 1</td>
<td>77.5 ± 2.5</td>
<td>77.5 ± 2.5</td>
<td>20 ± 2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PA12-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>+</td>
<td>++</td>
<td>+</td>
<td>149.6℃</td>
<td>112.4℃</td>
<td>87.49 ± 2.81</td>
<td>5438.40 ± 282.82</td>
<td>2.59 ± 0.19</td>
<td>133.17 ± 4.66</td>
<td>4667.43 ± 339.80</td>
<td>6.11 ± 1.45</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PATH-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>++</td>
<td>+++</td>
<td>+++</td>
<td>192.3℃</td>
<td>121.7℃</td>
<td>104.90 ± 1.99</td>
<td>8383.26 ± 419.53</td>
<td>1.60 ± 0.07</td>
<td>147.70 ± 4.09</td>
<td>5969.35 ± 145.28</td>
<td>6.17 ± 0.2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PET-CF</td>
<td>≤15</td>
<td>80-100℃</td>
<td>+++</td>
<td>+++</td>
<td>+++</td>
<td>148.8℃</td>
<td>112.1℃</td>
<td>87.41 ± 3.57</td>
<td>6025.53 ± 355.46</td>
<td>1.99 ±0.18</td>
<td>122.69 ± 5.19</td>
<td>5313.21 ± 197.89</td>
<td>5.57 ± 0.58</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PLA Rapido</td>
<td>≤30</td>
<td>/</td>
<td>+</td>
<td>+</td>
<td>+</td>
<td>57℃</td>
<td>/</td>
<td>39 ± 3</td>
<td>4650 ± 150</td>
<td>12.5 ± 2.5</td>
<td>72.5 ± 2.5</td>
<td>2850 ± 150</td>
<td>6 ± 2</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>PLA Rapido Matte</td>
<td>≤30</td>
<td>/</td>
<td>+</td>
<td>+</td>
<td>+</td>
<td>58℃</td>
<td>/</td>
<td>39 ± 3</td>
<td>2400 ± 200</td>
<td>3 ± 1</td>
<td>77.5 ± 2.5</td>
<td>2200 ± 200</td>
<td>7.5 ± 1.5</td>
</tr>
<tr v-for="(item, index) in 1" :key="index">
<td>UltraPA</td>
<td>≤15</td>
<td>/</td>
<td>++</td>
<td>++</td>
<td>++</td>
<td>77.8℃</td>
<td>73.1℃</td>
<td>86.15 ± 0.56</td>
<td>3609.22 ± 153.31</td>
<td>11.68 ± 3.36</td>
<td>121.47 ± 3.14</td>
<td>3314.03 ±181.88</td>
<td>5.78 ± 0.30</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="GifBigBoard" board="issuereport">
<div id="GifBoard">
<div class="GifBlock">
<div class="UG_B_TITLE trans TextS1" tid="t6"></div>
<div class="trans" tid="ls2"></div>
<div class="UG_B_DESC trans" tid="ls3"></div>
</div>
</div>
<div id="IssueBoard">
<div class="PrintBlock">
<div class="UG_IMG"><img src="img/X-MAX3.png" /></div>
<div class="UG_TITLE">X-MAX 3</div>
<div class="UG_DESC">
E-mail:<br />MAX3support@qd3dprinter.com<br>MAX3AMS@qd3dprinter.com<br /><br>
Skype:<br />MAX3support@qd3dprinter.com
</div> </div>
</div> </div>
<div class="PrintBlock"> <div class="PrinterBlock">
<div class="UG_IMG"><img src="img/X-Plus3.png" /></div> <div class="CenterImage"><img src="img/X-Plus 3.png"/></div>
<div class="UG_TITLE">X-Plus 3</div> <div class="ThumbnailTitle">X-Plus 3</div>
<div class="UG_DESC"> <div class="IntroduceText">
E-mail:<br />Plus3support01@qd3dprinter.com<br>Plus3support02@qd3dprinter.com<br /><br> <b>E-mail:</b><br/>Plus3support01@qd3dprinter.com<br>Plus3support02@qd3dprinter.com<br/><br>
Skype:<br />Plus3support@qd3dprinter.com <b>Skype:</b><br/>Plus3support@qd3dprinter.com
</div> </div>
</div> </div>
<div class="PrintBlock"> <div class="PrinterBlock">
<div class="UG_IMG"><img src="img/X-Smart3.png" /></div> <div class="CenterImage"><img src="img/X-smart 3.png"/></div>
<div class="UG_TITLE">X-Smart 3</div> <div class="ThumbnailTitle">X-smart 3</div>
<div class="UG_DESC"> <div class="IntroduceText">
E-mail:<br />Smart3support@qd3dprinter.com<br>Smart3AMS@qd3dprinter.com<br /><br> <b>E-mail:</b><br/>Smart3support@qd3dprinter.com<br>Smart3AMS@qd3dprinter.com<br/><br>
Skype:<br />Smart3support@qd3dprinter.com <b>Skype:</b><br/>Smart3support@qd3dprinter.com
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,11 +1,6 @@
function OnInit() function OnInit()
{ {
//-----Test----- TranslatePage();
//Set_RecentFile_MouseRightBtn_Event();
//-----Official-----
TranslatePage();
SendMsg_GetLoginInfo(); SendMsg_GetLoginInfo();
SendMsg_GetRecentFile(); SendMsg_GetRecentFile();
@@ -13,40 +8,27 @@ function OnInit()
function GotoMenu( strMenu ) function GotoMenu( strMenu )
{ {
let MenuList = $(".BtnItem"); if (strMenu == "UserGuide")
let nAll=MenuList.length;
for(let n=0;n<nAll;n++)
{ {
let OneBtn=MenuList[n]; $(".MenuBtnSelected").removeClass("MenuBtnSelected");
$("div[board]").hide();
if( $(OneBtn).attr("menu")==strMenu ) $("div[board=\'" + strMenu + "\']").show();
{
$(".BtnItem").removeClass("BtnItemSelected");
$(OneBtn).addClass("BtnItemSelected");
$("div[board]").hide();
$("div[board=\'"+strMenu+"\']").show();
}
} }
} else
{
let MenuList = $(".MenuBtn");
let nAll=MenuList.length;
function GotoMain(strMenu) { for(let n=0;n<nAll;n++)
let MenuList = $(".TbItem") {
let nAll = MenuList.length; let OneBtn=MenuList[n];
if ($(OneBtn).attr("menu")==strMenu)
for (let n = 0; n < nAll; n++) { {
let OneBtn = MenuList[n]; $(".MenuBtnSelected").removeClass("MenuBtnSelected");
$(OneBtn).addClass("MenuBtnSelected");
if ($(OneBtn).attr("menu") == strMenu) { $("div[board]").hide();
$(".TbItem").removeClass("TbItemSelected"); $("div[board=\'"+strMenu+"\']").show();
}
$(OneBtn).addClass("TbItemSelected");
$(".BtnItem").removeClass("BtnItemSelected");
$("div[board]").hide();
$("div[board=\'" + strMenu + "\']").show();
} }
} }
} }

View File

@@ -1,7 +1,8 @@
#add_subdirectory(slasupporttree) #add_subdirectory(slasupporttree)
#add_subdirectory(openvdb) #add_subdirectory(openvdb)
# add_subdirectory(meshboolean) # add_subdirectory(meshboolean)
add_subdirectory(its_neighbor_index) #add_subdirectory(its_neighbor_index)
# add_subdirectory(opencsg) # add_subdirectory(opencsg)
#add_subdirectory(aabb-evaluation) #add_subdirectory(aabb-evaluation)
add_subdirectory(wx_gl_test) #add_subdirectory(wx_gl_test)
add_subdirectory(print_arrange_polys)

View File

@@ -0,0 +1,7 @@
add_executable(print_arrange_polys main.cpp)
target_link_libraries(print_arrange_polys libslic3r admesh)
if (WIN32)
qidislicer_copy_dlls(print_arrange_polys)
endif()

View File

@@ -0,0 +1,103 @@
#include <iostream>
#include <ostream>
#include <libslic3r/TriangleMesh.hpp>
#include <boost/filesystem.hpp>
void print_arrange_polygons(const std::string &dirpath, std::ostream &out)
{
using namespace Slic3r;
boost::filesystem::path p = dirpath;
if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p))
return;
for (const auto& entry : boost::filesystem::directory_iterator(p)) {
if (!boost::filesystem::is_regular_file(entry)) {
continue;
}
TriangleMesh mesh;
mesh.ReadSTLFile(entry.path().c_str());
ExPolygons outline = mesh.horizontal_projection();
out << "// " << entry.path().filename() << ": " << std::endl;
for (const ExPolygon &expoly : outline) {
out << "MyPoly{\n"; // Start of polygon
out << "\t{\n"; // Start of contour
for (const auto& point : expoly.contour.points) {
out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates
}
out << " },\n"; // End of contour
out << " {\n"; // start of holes
for (const auto& hole : expoly.holes) {
out << " {\n"; // Start of hole
for (const auto& point : hole.points) {
out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates
}
out << " },\n"; // End of hole Polygon
}
out << " }\n"; // end of holes Polygons
out << "},\n"; // End of ExPolygon
}
}
}
void print_arrange_items(const std::string &dirpath, std::ostream &out)
{
using namespace Slic3r;
boost::filesystem::path p = dirpath;
if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p))
return;
for (const auto& entry : boost::filesystem::directory_iterator(p)) {
if (!boost::filesystem::is_regular_file(entry)) {
continue;
}
TriangleMesh mesh;
mesh.ReadSTLFile(entry.path().c_str());
ExPolygons outline = mesh.horizontal_projection();
out << "ExPolygons{ " << "// " << entry.path().filename() << ":\n";
for (const ExPolygon &expoly : outline) {
out << " MyPoly{\n"; // Start of polygon
out << " {\n"; // Start of contour
for (const auto& point : expoly.contour.points) {
out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates
}
out << " },\n"; // End of contour
out << " {\n"; // start of holes
for (const auto& hole : expoly.holes) {
out << " {\n"; // Start of hole
for (const auto& point : hole.points) {
out << " {" << point.x() << ", " << point.y() << "},\n"; // Print point coordinates
}
out << " },\n"; // End of hole Polygon
}
out << " }\n"; // end of holes Polygons
out << " },\n"; // End of ExPolygon
}
out << "},\n";
}
}
int main(int argc, const char *argv[])
{
if (argc <= 1)
return -1;
std::string dirpath = argv[1];
print_arrange_items(dirpath, std::cout);
return 0;
}

View File

@@ -49,21 +49,12 @@ if (SLIC3R_GUI)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux") if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}") set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
if (SLIC3R_WX_STABLE)
find_package(wxWidgets 3.0 REQUIRED COMPONENTS base core adv html gl)
else ()
find_package(wxWidgets 3.1 QUIET COMPONENTS base core adv html gl)
if (NOT wxWidgets_FOUND)
message(FATAL_ERROR "\nCould not find wxWidgets 3.1.\n"
"Hint: On Linux you can set -DSLIC3R_WX_STABLE=1 to use wxWidgets 3.0\n")
endif ()
endif ()
include(${wxWidgets_USE_FILE})
else ()
find_package(wxWidgets 3.2 REQUIRED COMPONENTS html adv gl core base webview aui net media)
endif () endif ()
find_package(wxWidgets 3.2 MODULE REQUIRED COMPONENTS base core adv html gl aui net webview)
include(${wxWidgets_USE_FILE})
slic3r_remap_configs(wx::wxhtml wx::wxadv wx::wxgl wx::wxcore wx::wxbase RelWithDebInfo Release)
if(UNIX) if(UNIX)
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}") message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")

View File

@@ -40,6 +40,7 @@
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PostProcessor.hpp"
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/CutUtils.hpp"
#include "libslic3r/ModelArrange.hpp" #include "libslic3r/ModelArrange.hpp"
#include "libslic3r/Platform.hpp" #include "libslic3r/Platform.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
@@ -313,10 +314,10 @@ int CLI::run(int argc, char **argv)
// Loop through transform options. // Loop through transform options.
bool user_center_specified = false; bool user_center_specified = false;
Points bed = get_bed_shape(m_print_config); arr2::ArrangeBed bed = arr2::to_arrange_bed(get_bed_shape(m_print_config));
ArrangeParams arrange_cfg; arr2::ArrangeSettings arrange_cfg;
arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); arrange_cfg.set_distance_from_objects(min_object_distance(m_print_config));
for (auto const &opt_key : m_transforms) { for (auto const &opt_key : m_transforms) {
if (opt_key == "merge") { if (opt_key == "merge") {
Model m; Model m;
@@ -329,7 +330,7 @@ int CLI::run(int argc, char **argv)
if (this->has_print_action()) if (this->has_print_action())
arrange_objects(m, bed, arrange_cfg); arrange_objects(m, bed, arrange_cfg);
else else
arrange_objects(m, InfiniteBed{}, arrange_cfg); arrange_objects(m, arr2::InfiniteBed{}, arrange_cfg);
} }
m_models.clear(); m_models.clear();
m_models.emplace_back(std::move(m)); m_models.emplace_back(std::move(m));
@@ -437,8 +438,11 @@ int CLI::run(int argc, char **argv)
} }
#else #else
// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower); // model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower);
model.objects.front()->cut(0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()), Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper); ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
auto cut_objects = cut.perform_with_plane();
for (ModelObject* obj : cut_objects)
model.add_object(*obj);
#endif #endif
model.delete_object(size_t(0)); model.delete_object(size_t(0));
} }
@@ -572,7 +576,7 @@ int CLI::run(int argc, char **argv)
if (! m_config.opt_bool("dont_arrange")) { if (! m_config.opt_bool("dont_arrange")) {
if (user_center_specified) { if (user_center_specified) {
Vec2d c = m_config.option<ConfigOptionPoint>("center")->value; Vec2d c = m_config.option<ConfigOptionPoint>("center")->value;
arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg); arrange_objects(model, arr2::InfiniteBed{scaled(c)}, arrange_cfg);
} else } else
arrange_objects(model, bed, arrange_cfg); arrange_objects(model, bed, arrange_cfg);
} }

View File

@@ -165,6 +165,7 @@ struct indexed_triangle_set
std::vector<stl_vertex> vertices; std::vector<stl_vertex> vertices;
bool empty() const { return indices.empty() || vertices.empty(); } bool empty() const { return indices.empty() || vertices.empty(); }
bool operator==(const indexed_triangle_set& other) const { return this->indices == other.indices && this->vertices == other.vertices; }
}; };
extern bool stl_open(stl_file *stl, const char *file); extern bool stl_open(stl_file *stl, const char *file);

View File

@@ -86,7 +86,13 @@ inline IntPoint IntPoint2d(cInt x, cInt y)
inline cInt Round(double val) inline cInt Round(double val)
{ {
return static_cast<cInt>((val < 0) ? (val - 0.5) : (val + 0.5)); double v = val < 0 ? val - 0.5 : val + 0.5;
#if defined(CLIPPERLIB_INT32) && ! defined(NDEBUG)
static constexpr const double hi = 65536 * 16383;
if (v > hi || -v > hi)
throw clipperException("Coordinate outside allowed range");
#endif
return static_cast<cInt>(v);
} }
// Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional. // Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional.

View File

@@ -152,6 +152,7 @@ namespace ImGui
// const wchar_t MmuSegmentationMarker = 0x1F; // const wchar_t MmuSegmentationMarker = 0x1F;
const wchar_t PlugMarker = 0x1C; const wchar_t PlugMarker = 0x1C;
const wchar_t DowelMarker = 0x1D; const wchar_t DowelMarker = 0x1D;
const wchar_t SnapMarker = 0x1E;
// Do not forget use following letters only in wstring // Do not forget use following letters only in wstring
const wchar_t DocumentationButton = 0x2600; const wchar_t DocumentationButton = 0x2600;
const wchar_t DocumentationHoverButton = 0x2601; const wchar_t DocumentationHoverButton = 0x2601;

View File

@@ -18,11 +18,10 @@ set(LIBNEST2D_SRCFILES
include/libnest2d/optimizers/nlopt/simplex.hpp include/libnest2d/optimizers/nlopt/simplex.hpp
include/libnest2d/optimizers/nlopt/subplex.hpp include/libnest2d/optimizers/nlopt/subplex.hpp
include/libnest2d/optimizers/nlopt/genetic.hpp include/libnest2d/optimizers/nlopt/genetic.hpp
src/libnest2d.cpp
) )
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) add_library(libnest2d INTERFACE)
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r) target_link_libraries(libnest2d INTERFACE NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r)
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) target_compile_definitions(libnest2d INTERFACE LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)

View File

@@ -243,6 +243,12 @@ inline void translate(Slic3r::ExPolygon& sh, const Slic3r::Point& offs)
sh.translate(offs); sh.translate(offs);
} }
template<>
inline void translate(Slic3r::Polygon& sh, const Slic3r::Point& offs)
{
sh.translate(offs);
}
#define DISABLE_BOOST_ROTATE #define DISABLE_BOOST_ROTATE
template<> template<>
inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads) inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads)
@@ -250,6 +256,12 @@ inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads)
sh.rotate(rads); sh.rotate(rads);
} }
template<>
inline void rotate(Slic3r::Polygon& sh, const Radians& rads)
{
sh.rotate(rads);
}
} // namespace shapelike } // namespace shapelike
namespace nfp { namespace nfp {

View File

@@ -15,12 +15,19 @@ namespace Slic3r {
// The stored pointer is not checked for being null when dereferenced. // The stored pointer is not checked for being null when dereferenced.
// //
// This is a movable only object due to the fact that it can possibly hold // This is a movable only object due to the fact that it can possibly hold
// a unique_ptr which a non-copy. // a unique_ptr which can only be moved.
//
// Drawbacks:
// No custom deleters are supported when storing a unique_ptr, but overloading
// std::default_delete for a particular type could be a workaround
//
// raw array types are problematic, since std::default_delete also does not
// support them well.
template<class T> template<class T>
class AnyPtr { class AnyPtr {
enum { RawPtr, UPtr, ShPtr, WkPtr }; enum { RawPtr, UPtr, ShPtr };
boost::variant<T*, std::unique_ptr<T>, std::shared_ptr<T>, std::weak_ptr<T>> ptr; boost::variant<T*, std::unique_ptr<T>, std::shared_ptr<T>> ptr;
template<class Self> static T *get_ptr(Self &&s) template<class Self> static T *get_ptr(Self &&s)
{ {
@@ -28,91 +35,119 @@ class AnyPtr {
case RawPtr: return boost::get<T *>(s.ptr); case RawPtr: return boost::get<T *>(s.ptr);
case UPtr: return boost::get<std::unique_ptr<T>>(s.ptr).get(); case UPtr: return boost::get<std::unique_ptr<T>>(s.ptr).get();
case ShPtr: return boost::get<std::shared_ptr<T>>(s.ptr).get(); case ShPtr: return boost::get<std::shared_ptr<T>>(s.ptr).get();
case WkPtr: {
auto shptr = boost::get<std::weak_ptr<T>>(s.ptr).lock();
return shptr.get();
}
} }
return nullptr; return nullptr;
} }
public: template<class TT> friend class AnyPtr;
template<class TT = T, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
AnyPtr(TT *p = nullptr) : ptr{p}
{}
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
AnyPtr(std::unique_ptr<TT> p) : ptr{std::unique_ptr<T>(std::move(p))}
{}
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
AnyPtr(std::shared_ptr<TT> p) : ptr{std::shared_ptr<T>(std::move(p))}
{}
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
AnyPtr(std::weak_ptr<TT> p) : ptr{std::weak_ptr<T>(std::move(p))}
{}
~AnyPtr() = default; template<class TT>
using SimilarPtrOnly = std::enable_if_t<std::is_convertible_v<TT*, T*>>;
public:
AnyPtr() noexcept = default;
AnyPtr(T *p) noexcept: ptr{p} {}
AnyPtr(std::nullptr_t) noexcept {};
template<class TT, class = SimilarPtrOnly<TT>>
AnyPtr(TT *p) noexcept : ptr{p}
{}
template<class TT = T, class = SimilarPtrOnly<TT>>
AnyPtr(std::unique_ptr<TT> p) noexcept : ptr{std::unique_ptr<T>(std::move(p))}
{}
template<class TT = T, class = SimilarPtrOnly<TT>>
AnyPtr(std::shared_ptr<TT> p) noexcept : ptr{std::shared_ptr<T>(std::move(p))}
{}
AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {} AnyPtr(AnyPtr &&other) noexcept : ptr{std::move(other.ptr)} {}
template<class TT, class = SimilarPtrOnly<TT>>
AnyPtr(AnyPtr<TT> &&other) noexcept
{
this->operator=(std::move(other));
}
AnyPtr(const AnyPtr &other) = delete; AnyPtr(const AnyPtr &other) = delete;
AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; } AnyPtr &operator=(AnyPtr &&other) noexcept
{
ptr = std::move(other.ptr);
return *this;
}
AnyPtr &operator=(const AnyPtr &other) = delete; AnyPtr &operator=(const AnyPtr &other) = delete;
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>> template<class TT, class = SimilarPtrOnly<TT>>
AnyPtr &operator=(TT *p) { ptr = p; return *this; } AnyPtr& operator=(AnyPtr<TT> &&other) noexcept
{
switch (other.ptr.which()) {
case RawPtr: *this = boost::get<TT *>(other.ptr); break;
case UPtr: *this = std::move(boost::get<std::unique_ptr<TT>>(other.ptr)); break;
case ShPtr: *this = std::move(boost::get<std::shared_ptr<TT>>(other.ptr)); break;
}
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>> return *this;
AnyPtr &operator=(std::unique_ptr<TT> p) { ptr = std::move(p); return *this; } }
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>> template<class TT, class = SimilarPtrOnly<TT>>
AnyPtr &operator=(std::shared_ptr<TT> p) { ptr = p; return *this; } AnyPtr &operator=(TT *p) noexcept
{
ptr = static_cast<T *>(p);
return *this;
}
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>> template<class TT, class = SimilarPtrOnly<TT>>
AnyPtr &operator=(std::weak_ptr<TT> p) { ptr = std::move(p); return *this; } AnyPtr &operator=(std::unique_ptr<TT> p) noexcept
{
ptr = std::unique_ptr<T>(std::move(p));
return *this;
}
const T &operator*() const { return *get_ptr(*this); } template<class TT, class = SimilarPtrOnly<TT>>
T &operator*() { return *get_ptr(*this); } AnyPtr &operator=(std::shared_ptr<TT> p) noexcept
{
ptr = std::shared_ptr<T>(std::move(p));
return *this;
}
T *operator->() { return get_ptr(*this); } const T &operator*() const noexcept { return *get_ptr(*this); }
const T *operator->() const { return get_ptr(*this); } T &operator*() noexcept { return *get_ptr(*this); }
T *get() { return get_ptr(*this); } T *operator->() noexcept { return get_ptr(*this); }
const T *get() const { return get_ptr(*this); } const T *operator->() const noexcept { return get_ptr(*this); }
operator bool() const T *get() noexcept { return get_ptr(*this); }
const T *get() const noexcept { return get_ptr(*this); }
operator bool() const noexcept
{ {
switch (ptr.which()) { switch (ptr.which()) {
case RawPtr: return bool(boost::get<T *>(ptr)); case RawPtr: return bool(boost::get<T *>(ptr));
case UPtr: return bool(boost::get<std::unique_ptr<T>>(ptr)); case UPtr: return bool(boost::get<std::unique_ptr<T>>(ptr));
case ShPtr: return bool(boost::get<std::shared_ptr<T>>(ptr)); case ShPtr: return bool(boost::get<std::shared_ptr<T>>(ptr));
case WkPtr: {
auto shptr = boost::get<std::weak_ptr<T>>(ptr).lock();
return bool(shptr);
}
} }
return false; return false;
} }
// If the stored pointer is a shared or weak pointer, returns a reference // If the stored pointer is a shared pointer, returns a reference
// counted copy. Empty shared pointer is returned otherwise. // counted copy. Empty shared pointer is returned otherwise.
std::shared_ptr<T> get_shared_cpy() const std::shared_ptr<T> get_shared_cpy() const noexcept
{ {
std::shared_ptr<T> ret; std::shared_ptr<T> ret;
switch (ptr.which()) { if (ptr.which() == ShPtr)
case ShPtr: ret = boost::get<std::shared_ptr<T>>(ptr); break; ret = boost::get<std::shared_ptr<T>>(ptr);
case WkPtr: ret = boost::get<std::weak_ptr<T>>(ptr).lock(); break;
default:
;
}
return ret; return ret;
} }
// If the underlying pointer is unique, convert to shared pointer // If the underlying pointer is unique, convert to shared pointer
void convert_unique_to_shared() void convert_unique_to_shared() noexcept
{ {
if (ptr.which() == UPtr) if (ptr.which() == UPtr)
ptr = std::shared_ptr<T>{std::move(boost::get<std::unique_ptr<T>>(ptr))}; ptr = std::shared_ptr<T>{std::move(boost::get<std::unique_ptr<T>>(ptr))};
@@ -125,6 +160,7 @@ public:
} }
}; };
} // namespace Slic3r } // namespace Slic3r
#endif // ANYPTR_HPP #endif // ANYPTR_HPP

View File

@@ -12,6 +12,7 @@
#include <ClipperUtils.hpp> #include <ClipperUtils.hpp>
#include <boost/geometry/index/rtree.hpp> #include <boost/geometry/index/rtree.hpp>
#include <boost/container/small_vector.hpp>
#if defined(_MSC_VER) && defined(__clang__) #if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW #define BOOST_NO_CXX17_HDR_STRING_VIEW
@@ -258,7 +259,7 @@ protected:
auto& index = isBig(item.area()) ? spatindex : smalls_spatindex; auto& index = isBig(item.area()) ? spatindex : smalls_spatindex;
// Query the spatial index for the neighbors // Query the spatial index for the neighbors
std::vector<SpatElement> result; boost::container::small_vector<SpatElement, 100> result;
result.reserve(index.size()); result.reserve(index.size());
index.query(query, std::back_inserter(result)); index.query(query, std::back_inserter(result));

View File

@@ -0,0 +1,269 @@
#ifndef ARRANGE2_HPP
#define ARRANGE2_HPP
#include "Scene.hpp"
#include "Items/MutableItemTraits.hpp"
#include "Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/MinAreaBoundingBox.hpp"
namespace Slic3r { namespace arr2 {
template<class ArrItem> class Arranger
{
public:
class Ctl : public ArrangeTaskCtl {
public:
virtual void on_packed(ArrItem &item) {};
};
virtual ~Arranger() = default;
virtual void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
Ctl &ctl) = 0;
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &ctl);
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
Ctl &&ctl)
{
arrange(items, fixed, bed, ctl);
}
void arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &&ctl)
{
arrange(items, fixed, bed, ctl);
}
static std::unique_ptr<Arranger> create(const ArrangeSettingsView &settings);
};
template<class ArrItem> using ArrangerCtl = typename Arranger<ArrItem>::Ctl;
template<class ArrItem>
class DefaultArrangerCtl : public Arranger<ArrItem>::Ctl {
ArrangeTaskCtl *taskctl = nullptr;
public:
DefaultArrangerCtl() = default;
explicit DefaultArrangerCtl(ArrangeTaskBase::Ctl &ctl) : taskctl{&ctl} {}
void update_status(int st) override
{
if (taskctl)
taskctl->update_status(st);
}
bool was_canceled() const override
{
if (taskctl)
return taskctl->was_canceled();
return false;
}
};
template<class ArrItem>
void Arranger<ArrItem>::arrange(std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangeTaskCtl &ctl)
{
arrange(items, fixed, bed, DefaultArrangerCtl<ArrItem>{ctl});
}
class EmptyItemOutlineError: public std::exception {
static constexpr const char *Msg = "No outline can be derived for object";
public:
const char* what() const noexcept override { return Msg; }
};
template<class ArrItem> class ArrangeableToItemConverter
{
public:
virtual ~ArrangeableToItemConverter() = default;
// May throw EmptyItemOutlineError
virtual ArrItem convert(const Arrangeable &arrbl, coord_t offs = 0) const = 0;
// Returns the extent of simplification that the converter utilizes when
// creating arrange items. Zero shall mean no simplification at all.
virtual coord_t simplification_tolerance() const { return 0; }
static std::unique_ptr<ArrangeableToItemConverter> create(
ArrangeSettingsView::GeometryHandling geometry_handling,
coord_t safety_d);
static std::unique_ptr<ArrangeableToItemConverter> create(
const Scene &sc)
{
return create(sc.settings().get_geometry_handling(),
scaled(sc.settings().get_distance_from_objects()));
}
};
template<class DStore, class = WritableDataStoreOnly<DStore>>
class AnyWritableDataStore: public AnyWritable
{
DStore &dstore;
public:
AnyWritableDataStore(DStore &store): dstore{store} {}
void write(std::string_view key, std::any d) override
{
set_data(dstore, std::string{key}, std::move(d));
}
};
template<class ArrItem>
class BasicItemConverter : public ArrangeableToItemConverter<ArrItem>
{
coord_t m_safety_d;
coord_t m_simplify_tol;
public:
BasicItemConverter(coord_t safety_d = 0, coord_t simpl_tol = 0)
: m_safety_d{safety_d}, m_simplify_tol{simpl_tol}
{}
coord_t safety_dist() const noexcept { return m_safety_d; }
coord_t simplification_tolerance() const override
{
return m_simplify_tol;
}
};
template<class ArrItem>
class ConvexItemConverter : public BasicItemConverter<ArrItem>
{
public:
using BasicItemConverter<ArrItem>::BasicItemConverter;
ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
};
template<class ArrItem>
class AdvancedItemConverter : public BasicItemConverter<ArrItem>
{
protected:
virtual ArrItem get_arritem(const Arrangeable &arrbl, coord_t eps) const;
public:
using BasicItemConverter<ArrItem>::BasicItemConverter;
ArrItem convert(const Arrangeable &arrbl, coord_t offs) const override;
};
template<class ArrItem>
class BalancedItemConverter : public AdvancedItemConverter<ArrItem>
{
protected:
ArrItem get_arritem(const Arrangeable &arrbl, coord_t offs) const override;
public:
using AdvancedItemConverter<ArrItem>::AdvancedItemConverter;
};
template<class ArrItem, class En = void> struct ImbueableItemTraits_
{
static constexpr const char *Key = "object_id";
static void imbue_id(ArrItem &itm, const ObjectID &id)
{
set_arbitrary_data(itm, Key, id);
}
static std::optional<ObjectID> retrieve_id(const ArrItem &itm)
{
std::optional<ObjectID> ret;
auto idptr = get_data<const ObjectID>(itm, Key);
if (idptr)
ret = *idptr;
return ret;
}
};
template<class ArrItem>
using ImbueableItemTraits = ImbueableItemTraits_<StripCVRef<ArrItem>>;
template<class ArrItem>
void imbue_id(ArrItem &itm, const ObjectID &id)
{
ImbueableItemTraits<ArrItem>::imbue_id(itm, id);
}
template<class ArrItem>
std::optional<ObjectID> retrieve_id(const ArrItem &itm)
{
return ImbueableItemTraits<ArrItem>::retrieve_id(itm);
}
template<class ArrItem>
bool apply_arrangeitem(const ArrItem &itm, ArrangeableModel &mdl)
{
bool ret = false;
if (auto id = retrieve_id(itm)) {
mdl.visit_arrangeable(*id, [&itm, &ret](Arrangeable &arrbl) {
if ((ret = arrbl.assign_bed(get_bed_index(itm))))
arrbl.transform(unscaled(get_translation(itm)), get_rotation(itm));
});
}
return ret;
}
template<class ArrItem>
double get_min_area_bounding_box_rotation(const ArrItem &itm)
{
return MinAreaBoundigBox{envelope_convex_hull(itm),
MinAreaBoundigBox::pcConvex}
.angle_to_X();
}
template<class ArrItem>
double get_fit_into_bed_rotation(const ArrItem &itm, const RectangleBed &bed)
{
double ret = 0.;
auto bbsz = envelope_bounding_box(itm).size();
auto binbb = bounding_box(bed);
auto binbbsz = binbb.size();
if (bbsz.x() >= binbbsz.x() || bbsz.y() >= binbbsz.y())
ret = fit_into_box_rotation(envelope_convex_hull(itm), binbb);
return ret;
}
template<class ArrItem>
auto get_corrected_bed(const ExtendedBed &bed,
const ArrangeableToItemConverter<ArrItem> &converter)
{
auto bedcpy = bed;
visit_bed([tol = -converter.simplification_tolerance()](auto &rawbed) {
rawbed = offset(rawbed, tol);
}, bedcpy);
return bedcpy;
}
}} // namespace Slic3r::arr2
#endif // ARRANGE2_HPP

View File

@@ -0,0 +1,498 @@
#ifndef ARRANGEIMPL_HPP
#define ARRANGEIMPL_HPP
#include <random>
#include <map>
#include "Arrange.hpp"
#include "Core/ArrangeBase.hpp"
#include "Core/ArrangeFirstFit.hpp"
#include "Core/NFP/PackStrategyNFP.hpp"
#include "Core/NFP/Kernels/TMArrangeKernel.hpp"
#include "Core/NFP/Kernels/GravityKernel.hpp"
#include "Core/NFP/RectangleOverfitPackingStrategy.hpp"
#include "Core/Beds.hpp"
#include "Items/MutableItemTraits.hpp"
#include "SegmentedRectangleBed.hpp"
#include "libslic3r/Execution/ExecutionTBB.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#ifndef NDEBUG
#include "Core/NFP/Kernels/SVGDebugOutputKernelWrapper.hpp"
#endif
namespace Slic3r { namespace arr2 {
// arrange overload for SegmentedRectangleBed which is exactly what is used
// by XL printers.
template<class It,
class ConstIt,
class SelectionStrategy,
class PackStrategy, class...SBedArgs>
void arrange(SelectionStrategy &&selstrategy,
PackStrategy &&packingstrategy,
const Range<It> &items,
const Range<ConstIt> &fixed,
const SegmentedRectangleBed<SBedArgs...> &bed)
{
// Dispatch:
arrange(std::forward<SelectionStrategy>(selstrategy),
std::forward<PackStrategy>(packingstrategy), items, fixed,
RectangleBed{bed.bb}, SelStrategyTag<SelectionStrategy>{});
std::vector<int> bed_indices = get_bed_indices(items, fixed);
std::map<int, BoundingBox> pilebb;
std::map<int, bool> bed_occupied;
for (auto &itm : items) {
auto bedidx = get_bed_index(itm);
if (bedidx >= 0) {
pilebb[bedidx].merge(fixed_bounding_box(itm));
if (is_wipe_tower(itm))
bed_occupied[bedidx] = true;
}
}
for (auto &fxitm : fixed) {
auto bedidx = get_bed_index(fxitm);
if (bedidx >= 0)
bed_occupied[bedidx] = true;
}
auto bedbb = bounding_box(bed);
auto piecesz = unscaled(bedbb).size();
piecesz.x() /= bed.segments_x();
piecesz.y() /= bed.segments_y();
using Pivots = RectPivots;
Pivots pivot = bed.alignment();
for (int bedidx : bed_indices) {
if (auto occup_it = bed_occupied.find(bedidx);
occup_it != bed_occupied.end() && occup_it->second)
continue;
BoundingBox bb;
auto pilesz = unscaled(pilebb[bedidx]).size();
bb.max.x() = scaled(std::ceil(pilesz.x() / piecesz.x()) * piecesz.x());
bb.max.y() = scaled(std::ceil(pilesz.y() / piecesz.y()) * piecesz.y());
switch (pivot) {
case Pivots::BottomLeft:
bb.translate(bedbb.min - bb.min);
break;
case Pivots::TopRight:
bb.translate(bedbb.max - bb.max);
break;
case Pivots::BottomRight: {
Point bedref{bedbb.max.x(), bedbb.min.y()};
Point bbref {bb.max.x(), bb.min.y()};
bb.translate(bedref - bbref);
break;
}
case Pivots::TopLeft: {
Point bedref{bedbb.min.x(), bedbb.max.y()};
Point bbref {bb.min.x(), bb.max.y()};
bb.translate(bedref - bbref);
break;
}
case Pivots::Center: {
bb.translate(bedbb.center() - bb.center());
break;
}
default:
;
}
Vec2crd d = bb.center() - pilebb[bedidx].center();
auto pilebbx = pilebb[bedidx];
pilebbx.translate(d);
Point corr{0, 0};
corr.x() = -std::min(0, pilebbx.min.x() - bedbb.min.x())
-std::max(0, pilebbx.max.x() - bedbb.max.x());
corr.y() = -std::min(0, pilebbx.min.y() - bedbb.min.y())
-std::max(0, pilebbx.max.y() - bedbb.max.y());
d += corr;
for (auto &itm : items)
if (get_bed_index(itm) == static_cast<int>(bedidx) && !is_wipe_tower(itm))
translate(itm, d);
}
}
using VariantKernel =
boost::variant<TMArrangeKernel, GravityKernel>;
template<> struct KernelTraits_<VariantKernel> {
template<class ArrItem>
static double placement_fitness(const VariantKernel &kernel,
const ArrItem &itm,
const Vec2crd &transl)
{
double ret = NaNd;
boost::apply_visitor(
[&](auto &k) { ret = k.placement_fitness(itm, transl); }, kernel);
return ret;
}
template<class ArrItem, class Bed, class Ctx, class RemIt>
static bool on_start_packing(VariantKernel &kernel,
ArrItem &itm,
const Bed &bed,
const Ctx &packing_context,
const Range<RemIt> &remaining_items)
{
bool ret = false;
boost::apply_visitor([&](auto &k) {
ret = k.on_start_packing(itm, bed, packing_context, remaining_items);
}, kernel);
return ret;
}
template<class ArrItem>
static bool on_item_packed(VariantKernel &kernel, ArrItem &itm)
{
bool ret = false;
boost::apply_visitor([&](auto &k) { ret = k.on_item_packed(itm); },
kernel);
return ret;
}
};
template<class ArrItem>
struct firstfit::ItemArrangedVisitor<ArrItem, DataStoreOnly<ArrItem>> {
template<class Bed, class PIt, class RIt>
static void on_arranged(ArrItem &itm,
const Bed &bed,
const Range<PIt> &packed,
const Range<RIt> &remaining)
{
using OnArrangeCb = std::function<void(StripCVRef<ArrItem> &)>;
auto cb = get_data<OnArrangeCb>(itm, "on_arranged");
if (cb) {
(*cb)(itm);
}
}
};
inline RectPivots xlpivots_to_rect_pivots(ArrangeSettingsView::XLPivots xlpivot)
{
if (xlpivot == arr2::ArrangeSettingsView::xlpRandom) {
// means it should be random
std::random_device rd{};
std::mt19937 rng(rd());
std::uniform_int_distribution<std::mt19937::result_type>
dist(0, arr2::ArrangeSettingsView::xlpRandom - 1);
xlpivot = static_cast<ArrangeSettingsView::XLPivots>(dist(rng));
}
RectPivots rectpivot = RectPivots::Center;
switch(xlpivot) {
case arr2::ArrangeSettingsView::xlpCenter: rectpivot = RectPivots::Center; break;
case arr2::ArrangeSettingsView::xlpFrontLeft: rectpivot = RectPivots::BottomLeft; break;
case arr2::ArrangeSettingsView::xlpFrontRight: rectpivot = RectPivots::BottomRight; break;
case arr2::ArrangeSettingsView::xlpRearLeft: rectpivot = RectPivots::TopLeft; break;
case arr2::ArrangeSettingsView::xlpRearRight: rectpivot = RectPivots::TopRight; break;
default:
;
}
return rectpivot;
}
template<class It, class Bed>
void fill_rotations(const Range<It> &items,
const Bed &bed,
const ArrangeSettingsView &settings)
{
if (!settings.is_rotation_enabled())
return;
for (auto &itm : items) {
if (is_wipe_tower(itm)) // Rotating the wipe tower is currently problematic
continue;
// Use the minimum bounding box rotation as a starting point.
auto minbbr = get_min_area_bounding_box_rotation(itm);
std::vector<double> rotations =
{minbbr,
minbbr + PI / 4., minbbr + PI / 2.,
minbbr + PI, minbbr + 3 * PI / 4.};
// Add the original rotation of the item if minbbr
// is not already the original rotation (zero)
if (std::abs(minbbr) > 0.)
rotations.emplace_back(0.);
// Also try to find the rotation that fits the item
// into a rectangular bed, given that it cannot fit,
// and there exists a rotation which can fit.
if constexpr (std::is_convertible_v<Bed, RectangleBed>) {
double fitbrot = get_fit_into_bed_rotation(itm, bed);
if (std::abs(fitbrot) > 0.)
rotations.emplace_back(fitbrot);
}
set_allowed_rotations(itm, rotations);
}
}
// An arranger put together to fulfill all the requirements based
// on the supplied ArrangeSettings
template<class ArrItem>
class DefaultArranger: public Arranger<ArrItem> {
ArrangeSettings m_settings;
static constexpr auto Accuracy = 1.;
template<class It, class FixIt, class Bed>
void arrange_(
const Range<It> &items,
const Range<FixIt> &fixed,
const Bed &bed,
ArrangerCtl<ArrItem> &ctl)
{
auto cmpfn = [](const auto &itm1, const auto &itm2) {
int pa = get_priority(itm1);
int pb = get_priority(itm2);
return pa == pb ? area(envelope_convex_hull(itm1)) > area(envelope_convex_hull(itm2)) :
pa > pb;
};
auto on_arranged = [&ctl](auto &itm, auto &bed, auto &ctx, auto &rem) {
ctl.update_status(rem.size());
ctl.on_packed(itm);
firstfit::DefaultOnArrangedFn{}(itm, bed, ctx, rem);
};
auto stop_cond = [&ctl] { return ctl.was_canceled(); };
firstfit::SelectionStrategy sel{cmpfn, on_arranged, stop_cond};
constexpr auto ep = ex_tbb;
VariantKernel basekernel;
switch (m_settings.get_arrange_strategy()) {
default:
[[fallthrough]];
case ArrangeSettingsView::asAuto:
if constexpr (std::is_convertible_v<Bed, CircleBed>){
basekernel = GravityKernel{};
} else {
basekernel = TMArrangeKernel{items.size(), area(bed)};
}
break;
case ArrangeSettingsView::asPullToCenter:
basekernel = GravityKernel{};
break;
}
#ifndef NDEBUG
SVGDebugOutputKernelWrapper<VariantKernel> kernel{bounding_box(bed), basekernel};
#else
auto & kernel = basekernel;
#endif
fill_rotations(items, bed, m_settings);
bool with_wipe_tower = std::any_of(items.begin(), items.end(),
[](auto &itm) {
return is_wipe_tower(itm);
});
// With rectange bed, and no fixed items, let's use an infinite bed
// with RectangleOverfitKernelWrapper. It produces better results than
// a pure RectangleBed with inner-fit polygon calculation.
if (!with_wipe_tower &&
m_settings.get_arrange_strategy() == ArrangeSettingsView::asAuto &&
std::is_convertible_v<Bed, RectangleBed>) {
PackStrategyNFP base_strategy{std::move(kernel), ep, Accuracy, stop_cond};
RectangleOverfitPackingStrategy final_strategy{std::move(base_strategy)};
arr2::arrange(sel, final_strategy, items, fixed, bed);
} else {
PackStrategyNFP ps{std::move(kernel), ep, Accuracy, stop_cond};
arr2::arrange(sel, ps, items, fixed, bed);
}
}
public:
explicit DefaultArranger(const ArrangeSettingsView &settings)
{
m_settings.set_from(settings);
}
void arrange(
std::vector<ArrItem> &items,
const std::vector<ArrItem> &fixed,
const ExtendedBed &bed,
ArrangerCtl<ArrItem> &ctl) override
{
visit_bed([this, &items, &fixed, &ctl](auto rawbed) {
if constexpr (IsSegmentedBed<decltype(rawbed)>)
rawbed.pivot = xlpivots_to_rect_pivots(
m_settings.get_xl_alignment());
arrange_(range(items), crange(fixed), rawbed, ctl);
}, bed);
}
};
template<class ArrItem>
std::unique_ptr<Arranger<ArrItem>> Arranger<ArrItem>::create(
const ArrangeSettingsView &settings)
{
// Currently all that is needed is handled by DefaultArranger
return std::make_unique<DefaultArranger<ArrItem>>(settings);
}
template<class ArrItem>
ArrItem ConvexItemConverter<ArrItem>::convert(const Arrangeable &arrbl,
coord_t offs) const
{
auto bed_index = arrbl.get_bed_index();
Polygon outline = arrbl.convex_outline();
if (outline.empty())
throw EmptyItemOutlineError{};
Polygon envelope = arrbl.convex_envelope();
coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
if (infl != 0) {
outline = Geometry::convex_hull(offset(outline, infl));
if (! envelope.empty())
envelope = Geometry::convex_hull(offset(envelope, infl));
}
ArrItem ret;
set_convex_shape(ret, outline);
if (! envelope.empty())
set_convex_envelope(ret, envelope);
set_bed_index(ret, bed_index);
set_priority(ret, arrbl.priority());
imbue_id(ret, arrbl.id());
if constexpr (IsWritableDataStore<ArrItem>)
arrbl.imbue_data(AnyWritableDataStore{ret});
return ret;
}
template<class ArrItem>
ArrItem AdvancedItemConverter<ArrItem>::convert(const Arrangeable &arrbl,
coord_t offs) const
{
auto bed_index = arrbl.get_bed_index();
ArrItem ret = get_arritem(arrbl, offs);
set_bed_index(ret, bed_index);
set_priority(ret, arrbl.priority());
imbue_id(ret, arrbl.id());
if constexpr (IsWritableDataStore<ArrItem>)
arrbl.imbue_data(AnyWritableDataStore{ret});
return ret;
}
template<class ArrItem>
ArrItem AdvancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
coord_t offs) const
{
coord_t infl = offs + coord_t(std::ceil(this->safety_dist() / 2.));
auto outline = arrbl.full_outline();
if (outline.empty())
throw EmptyItemOutlineError{};
auto envelope = arrbl.full_envelope();
if (infl != 0) {
outline = offset_ex(outline, infl);
if (! envelope.empty())
envelope = offset_ex(envelope, infl);
}
auto simpl_tol = static_cast<double>(this->simplification_tolerance());
if (simpl_tol > 0)
{
outline = expolygons_simplify(outline, simpl_tol);
if (!envelope.empty())
envelope = expolygons_simplify(envelope, simpl_tol);
}
ArrItem ret;
set_shape(ret, outline);
if (! envelope.empty())
set_envelope(ret, envelope);
return ret;
}
template<class ArrItem>
ArrItem BalancedItemConverter<ArrItem>::get_arritem(const Arrangeable &arrbl,
coord_t offs) const
{
ArrItem ret = AdvancedItemConverter<ArrItem>::get_arritem(arrbl, offs);
set_convex_envelope(ret, envelope_convex_hull(ret));
return ret;
}
template<class ArrItem>
std::unique_ptr<ArrangeableToItemConverter<ArrItem>>
ArrangeableToItemConverter<ArrItem>::create(
ArrangeSettingsView::GeometryHandling gh,
coord_t safety_d)
{
std::unique_ptr<ArrangeableToItemConverter<ArrItem>> ret;
constexpr coord_t SimplifyTol = scaled(.2);
switch(gh) {
case arr2::ArrangeSettingsView::ghConvex:
ret = std::make_unique<ConvexItemConverter<ArrItem>>(safety_d);
break;
case arr2::ArrangeSettingsView::ghBalanced:
ret = std::make_unique<BalancedItemConverter<ArrItem>>(safety_d, SimplifyTol);
break;
case arr2::ArrangeSettingsView::ghAdvanced:
ret = std::make_unique<AdvancedItemConverter<ArrItem>>(safety_d, SimplifyTol);
break;
default:
;
}
return ret;
}
}} // namespace Slic3r::arr2
#endif // ARRANGEIMPL_HPP

View File

@@ -0,0 +1,198 @@
#include "ArrangeSettingsDb_AppCfg.hpp"
namespace Slic3r {
ArrangeSettingsDb_AppCfg::ArrangeSettingsDb_AppCfg(AppConfig *appcfg) : m_appcfg{appcfg}
{
m_settings_fff.postfix = "_fff";
m_settings_fff_seq.postfix = "_fff_seq_print";
m_settings_sla.postfix = "_sla";
std::string dist_fff_str =
m_appcfg->get("arrange", "min_object_distance_fff");
std::string dist_bed_fff_str =
m_appcfg->get("arrange", "min_bed_distance_fff");
std::string dist_fff_seq_print_str =
m_appcfg->get("arrange", "min_object_distance_fff_seq_print");
std::string dist_bed_fff_seq_print_str =
m_appcfg->get("arrange", "min_bed_distance_fff_seq_print");
std::string dist_sla_str =
m_appcfg->get("arrange", "min_object_distance_sla");
std::string dist_bed_sla_str =
m_appcfg->get("arrange", "min_bed_distance_sla");
std::string en_rot_fff_str =
m_appcfg->get("arrange", "enable_rotation_fff");
std::string en_rot_fff_seqp_str =
m_appcfg->get("arrange", "enable_rotation_fff_seq_print");
std::string en_rot_sla_str =
m_appcfg->get("arrange", "enable_rotation_sla");
// std::string alignment_fff_str =
// m_appcfg->get("arrange", "alignment_fff");
// std::string alignment_fff_seqp_str =
// m_appcfg->get("arrange", "alignment_fff_seq_pring");
// std::string alignment_sla_str =
// m_appcfg->get("arrange", "alignment_sla");
// Override default alignment and save save/load it to a temporary slot "alignment_xl"
std::string alignment_xl_str =
m_appcfg->get("arrange", "alignment_xl");
std::string geom_handling_str =
m_appcfg->get("arrange", "geometry_handling");
std::string strategy_str =
m_appcfg->get("arrange", "arrange_strategy");
if (!dist_fff_str.empty())
m_settings_fff.vals.d_obj = string_to_float_decimal_point(dist_fff_str);
if (!dist_bed_fff_str.empty())
m_settings_fff.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_str);
if (!dist_fff_seq_print_str.empty())
m_settings_fff_seq.vals.d_obj = string_to_float_decimal_point(dist_fff_seq_print_str);
if (!dist_bed_fff_seq_print_str.empty())
m_settings_fff_seq.vals.d_bed = string_to_float_decimal_point(dist_bed_fff_seq_print_str);
if (!dist_sla_str.empty())
m_settings_sla.vals.d_obj = string_to_float_decimal_point(dist_sla_str);
if (!dist_bed_sla_str.empty())
m_settings_sla.vals.d_bed = string_to_float_decimal_point(dist_bed_sla_str);
if (!en_rot_fff_str.empty())
m_settings_fff.vals.rotations = (en_rot_fff_str == "1" || en_rot_fff_str == "yes");
if (!en_rot_fff_seqp_str.empty())
m_settings_fff_seq.vals.rotations = (en_rot_fff_seqp_str == "1" || en_rot_fff_seqp_str == "yes");
if (!en_rot_sla_str.empty())
m_settings_sla.vals.rotations = (en_rot_sla_str == "1" || en_rot_sla_str == "yes");
// if (!alignment_sla_str.empty())
// m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str);
// if (!alignment_fff_str.empty())
// m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str);
// if (!alignment_fff_seqp_str.empty())
// m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str);
// Override default alignment and save save/load it to a temporary slot "alignment_xl"
ArrangeSettingsView::XLPivots arr_alignment = ArrangeSettingsView::xlpFrontLeft;
if (!alignment_xl_str.empty()) {
int align_val = std::stoi(alignment_xl_str);
if (align_val >= 0 && align_val < ArrangeSettingsView::xlpCount)
arr_alignment =
static_cast<ArrangeSettingsView::XLPivots>(align_val);
}
m_settings_sla.vals.xl_align = arr_alignment ;
m_settings_fff.vals.xl_align = arr_alignment ;
m_settings_fff_seq.vals.xl_align = arr_alignment ;
ArrangeSettingsView::GeometryHandling geom_handl = arr2::ArrangeSettingsView::ghConvex;
if (!geom_handling_str.empty()) {
int gh = std::stoi(geom_handling_str);
if(gh >= 0 && gh < ArrangeSettingsView::GeometryHandling::ghCount)
geom_handl = static_cast<ArrangeSettingsView::GeometryHandling>(gh);
}
m_settings_sla.vals.geom_handling = geom_handl;
m_settings_fff.vals.geom_handling = geom_handl;
m_settings_fff_seq.vals.geom_handling = geom_handl;
ArrangeSettingsView::ArrangeStrategy arr_strategy = arr2::ArrangeSettingsView::asAuto;
if (!strategy_str.empty()) {
int strateg = std::stoi(strategy_str);
if(strateg >= 0 && strateg < ArrangeSettingsView::ArrangeStrategy::asCount)
arr_strategy = static_cast<ArrangeSettingsView::ArrangeStrategy>(strateg);
}
m_settings_sla.vals.arr_strategy = arr_strategy;
m_settings_fff.vals.arr_strategy = arr_strategy;
m_settings_fff_seq.vals.arr_strategy = arr_strategy;
}
void ArrangeSettingsDb_AppCfg::distance_from_obj_range(float &min,
float &max) const
{
min = get_slot(this).dobj_range.minval;
max = get_slot(this).dobj_range.maxval;
}
void ArrangeSettingsDb_AppCfg::distance_from_bed_range(float &min,
float &max) const
{
min = get_slot(this).dbed_range.minval;
max = get_slot(this).dbed_range.maxval;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_distance_from_objects(float v)
{
Slot &slot = get_slot(this);
slot.vals.d_obj = v;
m_appcfg->set("arrange", "min_object_distance" + slot.postfix,
float_to_string_decimal_point(v));
return *this;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_distance_from_bed(float v)
{
Slot &slot = get_slot(this);
slot.vals.d_bed = v;
m_appcfg->set("arrange", "min_bed_distance" + slot.postfix,
float_to_string_decimal_point(v));
return *this;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_rotation_enabled(bool v)
{
Slot &slot = get_slot(this);
slot.vals.rotations = v;
m_appcfg->set("arrange", "enable_rotation" + slot.postfix, v ? "1" : "0");
return *this;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_xl_alignment(XLPivots v)
{
m_settings_fff.vals.xl_align = v;
m_appcfg->set("arrange", "alignment_xl", std::to_string(v));
return *this;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_geometry_handling(GeometryHandling v)
{
m_settings_fff.vals.geom_handling = v;
m_appcfg->set("arrange", "geometry_handling", std::to_string(v));
return *this;
}
arr2::ArrangeSettingsDb& ArrangeSettingsDb_AppCfg::set_arrange_strategy(ArrangeStrategy v)
{
m_settings_fff.vals.arr_strategy = v;
m_appcfg->set("arrange", "arrange_strategy", std::to_string(v));
return *this;
}
} // namespace Slic3r

View File

@@ -0,0 +1,92 @@
#ifndef ARRANGESETTINGSDB_APPCFG_HPP
#define ARRANGESETTINGSDB_APPCFG_HPP
#include "ArrangeSettingsView.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class ArrangeSettingsDb_AppCfg: public arr2::ArrangeSettingsDb
{
public:
enum Slots { slotFFF, slotFFFSeqPrint, slotSLA };
private:
AppConfig *m_appcfg;
Slots m_current_slot = slotFFF;
struct FloatRange { float minval = 0.f, maxval = 100.f; };
struct Slot
{
Values vals;
Values defaults;
FloatRange dobj_range, dbed_range;
std::string postfix;
};
// Settings and their defaults are stored separately for fff,
// sla and fff sequential mode
Slot m_settings_fff, m_settings_fff_seq, m_settings_sla;
template<class Self>
static auto & get_slot(Self *self, Slots slot) {
switch(slot) {
case slotFFF: return self->m_settings_fff;
case slotFFFSeqPrint: return self->m_settings_fff_seq;
case slotSLA: return self->m_settings_sla;
}
return self->m_settings_fff;
}
template<class Self> static auto &get_slot(Self *self)
{
return get_slot(self, self->m_current_slot);
}
template<class Self>
static auto& get_ref(Self *self) { return get_slot(self).vals; }
public:
explicit ArrangeSettingsDb_AppCfg(AppConfig *appcfg);
float get_distance_from_objects() const override { return get_ref(this).d_obj; }
float get_distance_from_bed() const override { return get_ref(this).d_bed; }
bool is_rotation_enabled() const override { return get_ref(this).rotations; }
XLPivots get_xl_alignment() const override { return m_settings_fff.vals.xl_align; }
GeometryHandling get_geometry_handling() const override { return m_settings_fff.vals.geom_handling; }
ArrangeStrategy get_arrange_strategy() const override { return m_settings_fff.vals.arr_strategy; }
void distance_from_obj_range(float &min, float &max) const override;
void distance_from_bed_range(float &min, float &max) const override;
ArrangeSettingsDb& set_distance_from_objects(float v) override;
ArrangeSettingsDb& set_distance_from_bed(float v) override;
ArrangeSettingsDb& set_rotation_enabled(bool v) override;
ArrangeSettingsDb& set_xl_alignment(XLPivots v) override;
ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) override;
ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) override;
Values get_defaults() const override { return get_slot(this).defaults; }
void set_active_slot(Slots slot) noexcept { m_current_slot = slot; }
void set_distance_from_obj_range(Slots slot, float min, float max)
{
get_slot(this, slot).dobj_range = FloatRange{min, max};
}
void set_distance_from_bed_range(Slots slot, float min, float max)
{
get_slot(this, slot).dbed_range = FloatRange{min, max};
}
Values &get_defaults(Slots slot) { return get_slot(this, slot).defaults; }
};
} // namespace Slic3r
#endif // ARRANGESETTINGSDB_APPCFG_HPP

View File

@@ -0,0 +1,119 @@
#ifndef ARRANGESETTINGSVIEW_HPP
#define ARRANGESETTINGSVIEW_HPP
namespace Slic3r { namespace arr2 {
class ArrangeSettingsView
{
public:
enum GeometryHandling { ghConvex, ghBalanced, ghAdvanced, ghCount };
enum ArrangeStrategy { asAuto, asPullToCenter, asCount };
enum XLPivots {
xlpCenter,
xlpRearLeft,
xlpFrontLeft,
xlpFrontRight,
xlpRearRight,
xlpRandom,
xlpCount
};
virtual ~ArrangeSettingsView() = default;
virtual float get_distance_from_objects() const = 0;
virtual float get_distance_from_bed() const = 0;
virtual bool is_rotation_enabled() const = 0;
virtual XLPivots get_xl_alignment() const = 0;
virtual GeometryHandling get_geometry_handling() const = 0;
virtual ArrangeStrategy get_arrange_strategy() const = 0;
};
class ArrangeSettingsDb: public ArrangeSettingsView
{
public:
virtual void distance_from_obj_range(float &min, float &max) const = 0;
virtual void distance_from_bed_range(float &min, float &max) const = 0;
virtual ArrangeSettingsDb& set_distance_from_objects(float v) = 0;
virtual ArrangeSettingsDb& set_distance_from_bed(float v) = 0;
virtual ArrangeSettingsDb& set_rotation_enabled(bool v) = 0;
virtual ArrangeSettingsDb& set_xl_alignment(XLPivots v) = 0;
virtual ArrangeSettingsDb& set_geometry_handling(GeometryHandling v) = 0;
virtual ArrangeSettingsDb& set_arrange_strategy(ArrangeStrategy v) = 0;
struct Values {
float d_obj = 6.f, d_bed = 0.f;
bool rotations = false;
XLPivots xl_align = XLPivots::xlpFrontLeft;
GeometryHandling geom_handling = GeometryHandling::ghConvex;
ArrangeStrategy arr_strategy = ArrangeStrategy::asAuto;
Values() = default;
Values(const ArrangeSettingsView &sv)
{
d_bed = sv.get_distance_from_bed();
d_obj = sv.get_distance_from_objects();
arr_strategy = sv.get_arrange_strategy();
geom_handling = sv.get_geometry_handling();
rotations = sv.is_rotation_enabled();
xl_align = sv.get_xl_alignment();
}
};
virtual Values get_defaults() const { return {}; }
ArrangeSettingsDb& set_from(const ArrangeSettingsView &sv)
{
set_distance_from_bed(sv.get_distance_from_bed());
set_distance_from_objects(sv.get_distance_from_objects());
set_arrange_strategy(sv.get_arrange_strategy());
set_geometry_handling(sv.get_geometry_handling());
set_rotation_enabled(sv.is_rotation_enabled());
set_xl_alignment(sv.get_xl_alignment());
return *this;
}
};
class ArrangeSettings: public Slic3r::arr2::ArrangeSettingsDb
{
ArrangeSettingsDb::Values m_v = {};
public:
explicit ArrangeSettings(
const ArrangeSettingsDb::Values &v = {})
: m_v{v}
{}
explicit ArrangeSettings(const ArrangeSettingsView &v)
: m_v{v}
{}
float get_distance_from_objects() const override { return m_v.d_obj; }
float get_distance_from_bed() const override { return m_v.d_bed; }
bool is_rotation_enabled() const override { return m_v.rotations; }
XLPivots get_xl_alignment() const override { return m_v.xl_align; }
GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; }
ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; }
void distance_from_obj_range(float &min, float &max) const override { min = 0.f; max = 100.f; }
void distance_from_bed_range(float &min, float &max) const override { min = 0.f; max = 100.f; }
ArrangeSettings& set_distance_from_objects(float v) override { m_v.d_obj = v; return *this; }
ArrangeSettings& set_distance_from_bed(float v) override { m_v.d_bed = v; return *this; }
ArrangeSettings& set_rotation_enabled(bool v) override { m_v.rotations = v; return *this; }
ArrangeSettings& set_xl_alignment(XLPivots v) override { m_v.xl_align = v; return *this; }
ArrangeSettings& set_geometry_handling(GeometryHandling v) override { m_v.geom_handling = v; return *this; }
ArrangeSettings& set_arrange_strategy(ArrangeStrategy v) override { m_v.arr_strategy = v; return *this; }
auto & values() const { return m_v; }
auto & values() { return m_v; }
};
}} // namespace Slic3r::arr2
#endif // ARRANGESETTINGSVIEW_HPP

View File

@@ -0,0 +1,295 @@
#ifndef ARRANGEBASE_HPP
#define ARRANGEBASE_HPP
#include <iterator>
#include <type_traits>
#include "ArrangeItemTraits.hpp"
#include "PackingContext.hpp"
#include "libslic3r/Point.hpp"
namespace Slic3r { namespace arr2 {
namespace detail_is_const_it {
template<class It, class En = void>
struct IsConstIt_ { static constexpr bool value = false; };
template<class It>
using iterator_category_t = typename std::iterator_traits<It>::iterator_category;
template<class It>
using iterator_reference_t = typename std::iterator_traits<It>::reference;
template<class It>
struct IsConstIt_ <It, std::enable_if_t<std::is_class_v<iterator_category_t<It>>> >
{
static constexpr bool value =
std::is_const_v<std::remove_reference_t<iterator_reference_t<It>>>;
};
} // namespace detail_is_const_it
template<class It>
static constexpr bool IsConstIterator = detail_is_const_it::IsConstIt_<It>::value;
template<class It>
constexpr bool is_const_iterator(const It &it) noexcept { return IsConstIterator<It>; }
// The pack() function will use tag dispatching, based on the given strategy
// object that is used as its first argument.
// This tag is derived for a packing strategy as default, and will be used
// to cast a compile error.
struct UnimplementedPacking {};
// PackStrategyTag_ needs to be specialized for any valid packing strategy class
template<class PackStrategy> struct PackStrategyTag_ {
using Tag = UnimplementedPacking;
};
// Helper metafunc to derive packing strategy tag from a strategy object.
template<class Strategy>
using PackStrategyTag =
typename PackStrategyTag_<remove_cvref_t<Strategy>>::Tag;
template<class PackStrategy, class En = void> struct PackStrategyTraits_ {
template<class ArrItem> using Context = DefaultPackingContext<ArrItem>;
template<class ArrItem, class Bed>
static Context<ArrItem> create_context(PackStrategy &ps,
const Bed &bed,
int bed_index)
{
return {};
}
};
template<class PS> using PackStrategyTraits = PackStrategyTraits_<StripCVRef<PS>>;
template<class PS, class ArrItem>
using PackStrategyContext =
typename PackStrategyTraits<PS>::template Context<StripCVRef<ArrItem>>;
template<class ArrItem, class PackStrategy, class Bed>
PackStrategyContext<PackStrategy, ArrItem> create_context(PackStrategy &&ps,
const Bed &bed,
int bed_index)
{
return PackStrategyTraits<PackStrategy>::template create_context<
StripCVRef<ArrItem>>(ps, bed, bed_index);
}
// Function to pack one item into a bed.
// strategy parameter holds clue to what packing strategy to use. This function
// needs to be overloaded for the strategy tag belonging to the given
// strategy.
// 'bed' parameter is the type of bed into which the new item should be packed.
// See beds.hpp for valid bed classes.
// 'item' parameter is the item to be packed. After succesful arrangement
// (see return value) the item will have it's translation and rotation
// set correctly. If the function returns false, the translation and
// rotation of the input item might be changed to arbitrary values.
// 'fixed_items' paramter holds a range of ArrItem type objects that are already
// on the bed and need to be avoided by the newly packed item.
// 'remaining_items' is a range of ArrItem type objects that are intended to be
// packed in the future. This information can be leveradged by
// the packing strategy to make more intelligent placement
// decisions for the input item.
template<class Strategy, class Bed, class ArrItem, class RemIt>
bool pack(Strategy &&strategy,
const Bed &bed,
ArrItem &item,
const PackStrategyContext<Strategy, ArrItem> &context,
const Range<RemIt> &remaining_items)
{
static_assert(IsConstIterator<RemIt>, "Remaining item iterator is not const!");
// Dispatch:
return pack(std::forward<Strategy>(strategy), bed, item, context,
remaining_items, PackStrategyTag<Strategy>{});
}
// Overload without fixed items:
template<class Strategy, class Bed, class ArrItem>
bool pack(Strategy &&strategy, const Bed &bed, ArrItem &item)
{
std::vector<ArrItem> dummy;
auto context = create_context<ArrItem>(strategy, bed, PhysicalBedId);
return pack(std::forward<Strategy>(strategy), bed, item, context,
crange(dummy));
}
// Overload when strategy is unkown, yields compile error:
template<class Strategy, class Bed, class ArrItem, class RemIt>
bool pack(Strategy &&strategy,
const Bed &bed,
ArrItem &item,
const PackStrategyContext<Strategy, ArrItem> &context,
const Range<RemIt> &remaining_items,
const UnimplementedPacking &)
{
static_assert(always_false<Strategy>::value,
"Packing unimplemented for this placement strategy");
return false;
}
// Helper function to remove unpackable items from the input container.
template<class PackStrategy, class Container, class Bed, class StopCond>
void remove_unpackable_items(PackStrategy &&ps,
Container &c,
const Bed &bed,
const StopCond &stopcond)
{
// Safety test: try to pack each item into an empty bed. If it fails
// then it should be removed from the list
auto it = c.begin();
while (it != c.end() && !stopcond()) {
StripCVRef<decltype(*it)> &itm = *it;
auto cpy{itm};
if (!pack(ps, bed, cpy)) {
set_bed_index(itm, Unarranged);
it = c.erase(it);
} else
it++;
}
}
// arrange() function will use tag dispatching based on the selection strategy
// given as its first argument.
// This tag is derived for a selection strategy as default, and will be used
// to cast a compile error.
struct UnimplementedSelection {};
// SelStrategyTag_ needs to be specialized for any valid selection strategy class
template<class SelStrategy> struct SelStrategyTag_ {
using Tag = UnimplementedSelection;
};
// Helper metafunc to derive the selection strategy tag from a strategy object.
template<class Strategy>
using SelStrategyTag = typename SelStrategyTag_<remove_cvref_t<Strategy>>::Tag;
// Main function to start the arrangement. Takes a selection and a packing
// strategy object as the first two parameters. An implementation
// (function overload) must exist for this function that takes the coresponding
// selection strategy tag belonging to the given selstrategy argument.
//
// items parameter is a range of arrange items to arrange.
// fixed parameter is a range of arrange items that have fixed position and will
// not move during the arrangement but need to be avoided by the
// moving items.
// bed parameter is the type of bed into which the items need to fit.
template<class It,
class ConstIt,
class TBed,
class SelectionStrategy,
class PackStrategy>
void arrange(SelectionStrategy &&selstrategy,
PackStrategy &&packingstrategy,
const Range<It> &items,
const Range<ConstIt> &fixed,
const TBed &bed)
{
static_assert(IsConstIterator<ConstIt>, "Fixed item iterator is not const!");
// Dispatch:
arrange(std::forward<SelectionStrategy>(selstrategy),
std::forward<PackStrategy>(packingstrategy), items, fixed, bed,
SelStrategyTag<SelectionStrategy>{});
}
template<class It, class TBed, class SelectionStrategy, class PackStrategy>
void arrange(SelectionStrategy &&selstrategy,
PackStrategy &&packingstrategy,
const Range<It> &items,
const TBed &bed)
{
std::vector<typename std::iterator_traits<It>::value_type> dummy;
arrange(std::forward<SelectionStrategy>(selstrategy),
std::forward<PackStrategy>(packingstrategy), items, crange(dummy),
bed);
}
// Overload for unimplemented selection strategy, yields compile error:
template<class It,
class ConstIt,
class TBed,
class SelectionStrategy,
class PackStrategy>
void arrange(SelectionStrategy &&selstrategy,
PackStrategy &&packingstrategy,
const Range<It> &items,
const Range<ConstIt> &fixed,
const TBed &bed,
const UnimplementedSelection &)
{
static_assert(always_false<SelectionStrategy>::value,
"Arrange unimplemented for this selection strategy");
}
template<class It>
std::vector<int> get_bed_indices(const Range<It> &items)
{
auto bed_indices = reserve_vector<int>(items.size());
for (auto &itm : items)
bed_indices.emplace_back(get_bed_index(itm));
std::sort(bed_indices.begin(), bed_indices.end());
auto endit = std::unique(bed_indices.begin(), bed_indices.end());
bed_indices.erase(endit, bed_indices.end());
return bed_indices;
}
template<class It, class CIt>
std::vector<int> get_bed_indices(const Range<It> &items, const Range<CIt> &fixed)
{
std::vector<int> ret;
auto iitems = get_bed_indices(items);
auto ifixed = get_bed_indices(fixed);
ret.reserve(std::max(iitems.size(), ifixed.size()));
std::set_union(iitems.begin(), iitems.end(),
ifixed.begin(), ifixed.end(),
std::back_inserter(ret));
return ret;
}
template<class It>
size_t get_bed_count(const Range<It> &items)
{
return get_bed_indices(items).size();
}
template<class It> int get_max_bed_index(const Range<It> &items)
{
auto it = std::max_element(items.begin(),
items.end(),
[](auto &i1, auto &i2) {
return get_bed_index(i1) < get_bed_index(i2);
});
int ret = Unarranged;
if (it != items.end())
ret = get_bed_index(*it);
return ret;
}
struct DefaultStopCondition {
constexpr bool operator()() const noexcept { return false; }
};
}} // namespace Slic3r::arr2
#endif // ARRANGEBASE_HPP

View File

@@ -0,0 +1,166 @@
#ifndef ARRANGEFIRSTFIT_HPP
#define ARRANGEFIRSTFIT_HPP
#include <iterator>
#include <map>
#include <libslic3r/Arrange/Core/ArrangeBase.hpp>
namespace Slic3r { namespace arr2 { namespace firstfit {
struct SelectionTag {};
// Can be specialized by Items
template<class ArrItem, class En = void>
struct ItemArrangedVisitor {
template<class Bed, class PIt, class RIt>
static void on_arranged(ArrItem &itm,
const Bed &bed,
const Range<PIt> &packed_items,
const Range<RIt> &remaining_items)
{}
};
// Use the the visitor baked into the ArrItem type by default
struct DefaultOnArrangedFn {
template<class ArrItem, class Bed, class PIt, class RIt>
void operator()(ArrItem &itm,
const Bed &bed,
const Range<PIt> &packed,
const Range<RIt> &remaining)
{
ItemArrangedVisitor<StripCVRef<ArrItem>>::on_arranged(itm, bed, packed,
remaining);
}
};
struct DefaultItemCompareFn {
template<class ArrItem>
bool operator() (const ArrItem &ia, const ArrItem &ib)
{
return get_priority(ia) > get_priority(ib);
}
};
template<class CompareFn = DefaultItemCompareFn,
class OnArrangedFn = DefaultOnArrangedFn,
class StopCondition = DefaultStopCondition>
struct SelectionStrategy
{
CompareFn cmpfn;
OnArrangedFn on_arranged_fn;
StopCondition cancel_fn;
SelectionStrategy(CompareFn cmp = {},
OnArrangedFn on_arranged = {},
StopCondition stopcond = {})
: cmpfn{cmp},
on_arranged_fn{std::move(on_arranged)},
cancel_fn{std::move(stopcond)}
{}
};
} // namespace firstfit
template<class... Args> struct SelStrategyTag_<firstfit::SelectionStrategy<Args...>> {
using Tag = firstfit::SelectionTag;
};
template<class It,
class ConstIt,
class TBed,
class SelStrategy,
class PackStrategy>
void arrange(
SelStrategy &&sel,
PackStrategy &&ps,
const Range<It> &items,
const Range<ConstIt> &fixed,
const TBed &bed,
const firstfit::SelectionTag &)
{
using ArrItem = typename std::iterator_traits<It>::value_type;
using ArrItemRef = std::reference_wrapper<ArrItem>;
auto sorted_items = reserve_vector<ArrItemRef>(items.size());
for (auto &itm : items) {
set_bed_index(itm, Unarranged);
sorted_items.emplace_back(itm);
}
using Context = PackStrategyContext<PackStrategy, ArrItem>;
std::map<int, Context> bed_contexts;
auto get_or_init_context = [&ps, &bed, &bed_contexts](int bedidx) -> Context& {
auto ctx_it = bed_contexts.find(bedidx);
if (ctx_it == bed_contexts.end()) {
auto res = bed_contexts.emplace(
bedidx, create_context<ArrItem>(ps, bed, bedidx));
assert(res.second);
ctx_it = res.first;
}
return ctx_it->second;
};
for (auto &itm : fixed) {
auto bedidx = get_bed_index(itm);
if (bedidx >= 0) {
Context &ctx = get_or_init_context(bedidx);
add_fixed_item(ctx, itm);
}
}
if constexpr (!std::is_null_pointer_v<decltype(sel.cmpfn)>) {
std::stable_sort(sorted_items.begin(), sorted_items.end(), sel.cmpfn);
}
auto is_cancelled = [&sel]() {
return sel.cancel_fn();
};
remove_unpackable_items(ps, sorted_items, bed, [&is_cancelled]() {
return is_cancelled();
});
auto it = sorted_items.begin();
using SConstIt = typename std::vector<ArrItemRef>::const_iterator;
while (it != sorted_items.end() && !is_cancelled()) {
bool was_packed = false;
int bedidx = 0;
while (!was_packed && !is_cancelled()) {
for (; !was_packed && !is_cancelled(); bedidx++) {
set_bed_index(*it, bedidx);
auto remaining = Range{std::next(static_cast<SConstIt>(it)),
sorted_items.cend()};
Context &ctx = get_or_init_context(bedidx);
was_packed = pack(ps, bed, *it, ctx, remaining);
if(was_packed) {
add_packed_item(ctx, *it);
auto packed_range = Range{sorted_items.cbegin(),
static_cast<SConstIt>(it)};
sel.on_arranged_fn(*it, bed, packed_range, remaining);
} else {
set_bed_index(*it, Unarranged);
}
}
}
++it;
}
}
}} // namespace Slic3r::arr2
#endif // ARRANGEFIRSTFIT_HPP

View File

@@ -0,0 +1,114 @@
#ifndef ARRANGE_ITEM_TRAITS_HPP
#define ARRANGE_ITEM_TRAITS_HPP
#include <libslic3r/Point.hpp>
namespace Slic3r { namespace arr2 {
// A logical bed representing an object not being arranged. Either the arrange
// has not yet successfully run on this ArrangePolygon or it could not fit the
// object due to overly large size or invalid geometry.
const constexpr int Unarranged = -1;
const constexpr int PhysicalBedId = 0;
// Basic interface of an arrange item. This struct can be specialized for any
// type that is arrangeable.
template<class ArrItem, class En = void> struct ArrangeItemTraits_ {
static Vec2crd get_translation(const ArrItem &ap)
{
return ap.get_translation();
}
static double get_rotation(const ArrItem &ap)
{
return ap.get_rotation();
}
static int get_bed_index(const ArrItem &ap) { return ap.get_bed_index(); }
static int get_priority(const ArrItem &ap) { return ap.get_priority(); }
// Setters:
static void set_translation(ArrItem &ap, const Vec2crd &v)
{
ap.set_translation(v);
}
static void set_rotation(ArrItem &ap, double v) { ap.set_rotation(v); }
static void set_bed_index(ArrItem &ap, int v) { ap.set_bed_index(v); }
};
template<class T> using ArrangeItemTraits = ArrangeItemTraits_<StripCVRef<T>>;
// Getters:
template<class T> Vec2crd get_translation(const T &itm)
{
return ArrangeItemTraits<T>::get_translation(itm);
}
template<class T> double get_rotation(const T &itm)
{
return ArrangeItemTraits<T>::get_rotation(itm);
}
template<class T> int get_bed_index(const T &itm)
{
return ArrangeItemTraits<T>::get_bed_index(itm);
}
template<class T> int get_priority(const T &itm)
{
return ArrangeItemTraits<T>::get_priority(itm);
}
// Setters:
template<class T> void set_translation(T &itm, const Vec2crd &v)
{
ArrangeItemTraits<T>::set_translation(itm, v);
}
template<class T> void set_rotation(T &itm, double v)
{
ArrangeItemTraits<T>::set_rotation(itm, v);
}
template<class T> void set_bed_index(T &itm, int v)
{
ArrangeItemTraits<T>::set_bed_index(itm, v);
}
// Helper functions for arrange items
template<class ArrItem> bool is_arranged(const ArrItem &ap)
{
return get_bed_index(ap) > Unarranged;
}
template<class ArrItem> bool is_fixed(const ArrItem &ap)
{
return get_bed_index(ap) >= PhysicalBedId;
}
template<class ArrItem> bool is_on_physical_bed(const ArrItem &ap)
{
return get_bed_index(ap) == PhysicalBedId;
}
template<class ArrItem> void translate(ArrItem &ap, const Vec2crd &t)
{
set_translation(ap, get_translation(ap) + t);
}
template<class ArrItem> void rotate(ArrItem &ap, double rads)
{
set_rotation(ap, get_rotation(ap) + rads);
}
}} // namespace Slic3r::arr2
#endif // ARRANGE_ITEM_HPP

View File

@@ -0,0 +1,130 @@
#include "Beds.hpp"
namespace Slic3r { namespace arr2 {
BoundingBox bounding_box(const InfiniteBed &bed)
{
BoundingBox ret;
using C = coord_t;
// It is important for Mx and My to be strictly less than half of the
// range of type C. width(), height() and area() will not overflow this way.
C Mx = C((std::numeric_limits<C>::lowest() + 2 * bed.center.x()) / 4.01);
C My = C((std::numeric_limits<C>::lowest() + 2 * bed.center.y()) / 4.01);
ret.max = bed.center - Point{Mx, My};
ret.min = bed.center + Point{Mx, My};
return ret;
}
Polygon to_rectangle(const BoundingBox &bb)
{
Polygon ret;
ret.points = {
bb.min,
Point{bb.max.x(), bb.min.y()},
bb.max,
Point{bb.min.x(), bb.max.y()}
};
return ret;
}
Polygon approximate_circle_with_polygon(const arr2::CircleBed &bed, int nedges)
{
Polygon ret;
double angle_incr = (2 * M_PI) / nedges; // Angle increment for each edge
double angle = 0; // Starting angle
// Loop to generate vertices for each edge
for (int i = 0; i < nedges; i++) {
// Calculate coordinates of the vertices using trigonometry
auto x = bed.center().x() + static_cast<coord_t>(bed.radius() * std::cos(angle));
auto y = bed.center().y() + static_cast<coord_t>(bed.radius() * std::sin(angle));
// Add vertex to the vector
ret.points.emplace_back(x, y);
// Update the angle for the next iteration
angle += angle_incr;
}
return ret;
}
inline coord_t width(const BoundingBox &box)
{
return box.max.x() - box.min.x();
}
inline coord_t height(const BoundingBox &box)
{
return box.max.y() - box.min.y();
}
inline double poly_area(const Points &pts)
{
return std::abs(Polygon::area(pts));
}
inline double distance_to(const Point &p1, const Point &p2)
{
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
return std::sqrt(dx * dx + dy * dy);
}
static CircleBed to_circle(const Point &center, const Points &points)
{
std::vector<double> vertex_distances;
double avg_dist = 0;
for (const Point &pt : points) {
double distance = distance_to(center, pt);
vertex_distances.push_back(distance);
avg_dist += distance;
}
avg_dist /= vertex_distances.size();
CircleBed ret(center, avg_dist);
for (auto el : vertex_distances) {
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
ret = {};
break;
}
}
return ret;
}
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
{
if (bed.empty())
return fn(InfiniteBed{});
else if (bed.size() == 1)
return fn(InfiniteBed{bed.front()});
else {
auto bb = BoundingBox(bed);
CircleBed circ = to_circle(bb.center(), bed);
auto parea = poly_area(bed);
if ((1.0 - parea / area(bb)) < 1e-3) {
return fn(RectangleBed{bb});
} else if (!std::isnan(circ.radius()) && (1.0 - parea / area(circ)) < 1e-2)
return fn(circ);
else
return fn(IrregularBed{{ExPolygon(bed)}});
}
}
ArrangeBed to_arrange_bed(const Points &bedpts)
{
ArrangeBed ret;
call_with_bed(bedpts, [&](const auto &bed) { ret = bed; });
return ret;
}
}} // namespace Slic3r::arr2

View File

@@ -0,0 +1,192 @@
#ifndef BEDS_HPP
#define BEDS_HPP
#include <numeric>
#include <libslic3r/Point.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/BoundingBox.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <boost/variant.hpp>
namespace Slic3r { namespace arr2 {
// Bed types to be used with arrangement. Most generic bed is a simple polygon
// with holes, but other special bed types are also valid, like a bed without
// boundaries, or a special case of a rectangular or circular bed which leaves
// a lot of room for optimizations.
// Representing an unbounded bed.
struct InfiniteBed {
Point center;
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
};
BoundingBox bounding_box(const InfiniteBed &bed);
inline InfiniteBed offset(const InfiniteBed &bed, coord_t) { return bed; }
struct RectangleBed {
BoundingBox bb;
explicit RectangleBed(const BoundingBox &bedbb) : bb{bedbb} {}
explicit RectangleBed(coord_t w, coord_t h, Point c = {0, 0}):
bb{{c.x() - w / 2, c.y() - h / 2}, {c.x() + w / 2, c.y() + h / 2}}
{}
coord_t width() const { return bb.size().x(); }
coord_t height() const { return bb.size().y(); }
};
inline BoundingBox bounding_box(const RectangleBed &bed) { return bed.bb; }
inline RectangleBed offset(RectangleBed bed, coord_t v)
{
bed.bb.offset(v);
return bed;
}
Polygon to_rectangle(const BoundingBox &bb);
inline Polygon to_rectangle(const RectangleBed &bed)
{
return to_rectangle(bed.bb);
}
class CircleBed {
Point m_center;
double m_radius;
public:
CircleBed(): m_center(0, 0), m_radius(NaNd) {}
explicit CircleBed(const Point& c, double r)
: m_center(c)
, m_radius(r)
{}
double radius() const { return m_radius; }
const Point& center() const { return m_center; }
};
// Function to approximate a circle with a convex polygon
Polygon approximate_circle_with_polygon(const CircleBed &bed, int nedges = 24);
inline BoundingBox bounding_box(const CircleBed &bed)
{
auto r = static_cast<coord_t>(std::round(bed.radius()));
Point R{r, r};
return {bed.center() - R, bed.center() + R};
}
inline CircleBed offset(const CircleBed &bed, coord_t v)
{
return CircleBed{bed.center(), bed.radius() + v};
}
struct IrregularBed { ExPolygons poly; };
inline BoundingBox bounding_box(const IrregularBed &bed)
{
return get_extents(bed.poly);
}
inline IrregularBed offset(IrregularBed bed, coord_t v)
{
bed.poly = offset_ex(bed.poly, v);
return bed;
}
using ArrangeBed =
boost::variant<InfiniteBed, RectangleBed, CircleBed, IrregularBed>;
inline BoundingBox bounding_box(const ArrangeBed &bed)
{
BoundingBox ret;
auto visitor = [&ret](const auto &b) { ret = bounding_box(b); };
boost::apply_visitor(visitor, bed);
return ret;
}
inline ArrangeBed offset(ArrangeBed bed, coord_t v)
{
auto visitor = [v](auto &b) { b = offset(b, v); };
boost::apply_visitor(visitor, bed);
return bed;
}
inline double area(const BoundingBox &bb)
{
auto bbsz = bb.size();
return double(bbsz.x()) * bbsz.y();
}
inline double area(const RectangleBed &bed)
{
auto bbsz = bed.bb.size();
return double(bbsz.x()) * bbsz.y();
}
inline double area(const InfiniteBed &bed)
{
return std::numeric_limits<double>::infinity();
}
inline double area(const IrregularBed &bed)
{
return std::accumulate(bed.poly.begin(), bed.poly.end(), 0.,
[](double s, auto &p) { return s + p.area(); });
}
inline double area(const CircleBed &bed)
{
return bed.radius() * bed.radius() * PI;
}
inline double area(const ArrangeBed &bed)
{
double ret = 0.;
auto visitor = [&ret](auto &b) { ret = area(b); };
boost::apply_visitor(visitor, bed);
return ret;
}
inline ExPolygons to_expolygons(const InfiniteBed &bed)
{
return {ExPolygon{to_rectangle(RectangleBed{scaled(1000.), scaled(1000.)})}};
}
inline ExPolygons to_expolygons(const RectangleBed &bed)
{
return {ExPolygon{to_rectangle(bed)}};
}
inline ExPolygons to_expolygons(const CircleBed &bed)
{
return {ExPolygon{approximate_circle_with_polygon(bed)}};
}
inline ExPolygons to_expolygons(const IrregularBed &bed) { return bed.poly; }
inline ExPolygons to_expolygons(const ArrangeBed &bed)
{
ExPolygons ret;
auto visitor = [&ret](const auto &b) { ret = to_expolygons(b); };
boost::apply_visitor(visitor, bed);
return ret;
}
ArrangeBed to_arrange_bed(const Points &bedpts);
} // namespace arr2
inline BoundingBox &bounding_box(BoundingBox &bb) { return bb; }
inline const BoundingBox &bounding_box(const BoundingBox &bb) { return bb; }
inline BoundingBox bounding_box(const Polygon &p) { return get_extents(p); }
} // namespace Slic3r
#endif // BEDS_HPP

View File

@@ -0,0 +1,79 @@
#ifndef DATASTORETRAITS_HPP
#define DATASTORETRAITS_HPP
#include <string_view>
#include "libslic3r/libslic3r.h"
namespace Slic3r { namespace arr2 {
// Some items can be containers of arbitrary data stored under string keys.
template<class ArrItem, class En = void> struct DataStoreTraits_
{
static constexpr bool Implemented = false;
template<class T> static const T *get(const ArrItem &, const std::string &key)
{
return nullptr;
}
// Same as above just not const.
template<class T> static T *get(ArrItem &, const std::string &key)
{
return nullptr;
}
static bool has_key(const ArrItem &itm, const std::string &key)
{
return false;
}
};
template<class ArrItem, class En = void> struct WritableDataStoreTraits_
{
static constexpr bool Implemented = false;
template<class T> static void set(ArrItem &, const std::string &key, T &&data)
{
}
};
template<class T> using DataStoreTraits = DataStoreTraits_<StripCVRef<T>>;
template<class T> constexpr bool IsDataStore = DataStoreTraits<StripCVRef<T>>::Implemented;
template<class T, class TT = T> using DataStoreOnly = std::enable_if_t<IsDataStore<T>, TT>;
template<class T, class ArrItem>
const T *get_data(const ArrItem &itm, const std::string &key)
{
return DataStoreTraits<ArrItem>::template get<T>(itm, key);
}
template<class ArrItem>
bool has_key(const ArrItem &itm, const std::string &key)
{
return DataStoreTraits<ArrItem>::has_key(itm, key);
}
template<class T, class ArrItem>
T *get_data(ArrItem &itm, const std::string &key)
{
return DataStoreTraits<ArrItem>::template get<T>(itm, key);
}
template<class T> using WritableDataStoreTraits = WritableDataStoreTraits_<StripCVRef<T>>;
template<class T> constexpr bool IsWritableDataStore = WritableDataStoreTraits<StripCVRef<T>>::Implemented;
template<class T, class TT = T> using WritableDataStoreOnly = std::enable_if_t<IsWritableDataStore<T>, TT>;
template<class T, class ArrItem>
void set_data(ArrItem &itm, const std::string &key, T &&data)
{
WritableDataStoreTraits<ArrItem>::template set(itm, key, std::forward<T>(data));
}
template<class T> constexpr bool IsReadWritableDataStore = IsDataStore<T> && IsWritableDataStore<T>;
template<class T, class TT = T> using ReadWritableDataStoreOnly = std::enable_if_t<IsReadWritableDataStore<T>, TT>;
}} // namespace Slic3r::arr2
#endif // DATASTORETRAITS_HPP

View File

@@ -0,0 +1,111 @@
#ifndef CIRCULAR_EDGEITERATOR_HPP
#define CIRCULAR_EDGEITERATOR_HPP
#include <libslic3r/Polygon.hpp>
#include <libslic3r/Line.hpp>
namespace Slic3r {
// Circular iterator over a polygon yielding individual edges as Line objects
// if flip_lines is true, the orientation of each line is flipped (not the
// direction of traversal)
template<bool flip_lines = false>
class CircularEdgeIterator_ {
const Polygon *m_poly = nullptr;
size_t m_i = 0;
size_t m_c = 0; // counting how many times the iterator has circled over
public:
// i: vertex position of first line's starting vertex
// poly: target polygon
CircularEdgeIterator_(size_t i, const Polygon &poly)
: m_poly{&poly}
, m_i{!poly.empty() ? i % poly.size() : 0}
, m_c{!poly.empty() ? i / poly.size() : 0}
{}
explicit CircularEdgeIterator_ (const Polygon &poly)
: CircularEdgeIterator_(0, poly) {}
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = Line;
using pointer = Line*;
using reference = Line&;
CircularEdgeIterator_ & operator++()
{
assert (m_poly);
++m_i;
if (m_i == m_poly->size()) { // faster than modulo (?)
m_i = 0;
++m_c;
}
return *this;
}
CircularEdgeIterator_ operator++(int)
{
auto cpy = *this; ++(*this); return cpy;
}
Line operator*() const
{
size_t nx = m_i == m_poly->size() - 1 ? 0 : m_i + 1;
Line ret;
if constexpr (flip_lines)
ret = Line((*m_poly)[nx], (*m_poly)[m_i]);
else
ret = Line((*m_poly)[m_i], (*m_poly)[nx]);
return ret;
}
Line operator->() const { return *(*this); }
bool operator==(const CircularEdgeIterator_& other) const
{
return m_i == other.m_i && m_c == other.m_c;
}
bool operator!=(const CircularEdgeIterator_& other) const
{
return !(*this == other);
}
CircularEdgeIterator_& operator +=(size_t dist)
{
m_i = (m_i + dist) % m_poly->size();
m_c = (m_i + (m_c * m_poly->size()) + dist) / m_poly->size();
return *this;
}
CircularEdgeIterator_ operator +(size_t dist)
{
auto cpy = *this;
cpy += dist;
return cpy;
}
};
using CircularEdgeIterator = CircularEdgeIterator_<>;
using CircularReverseEdgeIterator = CircularEdgeIterator_<true>;
inline Range<CircularEdgeIterator> line_range(const Polygon &poly)
{
return Range{CircularEdgeIterator{0, poly}, CircularEdgeIterator{poly.size(), poly}};
}
inline Range<CircularReverseEdgeIterator> line_range_flp(const Polygon &poly)
{
return Range{CircularReverseEdgeIterator{0, poly}, CircularReverseEdgeIterator{poly.size(), poly}};
}
} // namespace Slic3r
#endif // CIRCULAR_EDGEITERATOR_HPP

View File

@@ -0,0 +1,100 @@
#include "EdgeCache.hpp"
#include "CircularEdgeIterator.hpp"
namespace Slic3r { namespace arr2 {
void EdgeCache::create_cache(const ExPolygon &sh)
{
m_contour.distances.reserve(sh.contour.size());
m_holes.reserve(sh.holes.size());
m_contour.poly = &sh.contour;
fill_distances(sh.contour, m_contour.distances);
for (const Polygon &hole : sh.holes) {
auto &hc = m_holes.emplace_back();
hc.poly = &hole;
fill_distances(hole, hc.distances);
}
}
Vec2crd EdgeCache::coords(const ContourCache &cache, double distance) const
{
assert(cache.poly);
return arr2::coords(*cache.poly, cache.distances, distance);
}
void EdgeCache::sample_contour(double accuracy, std::vector<ContourLocation> &samples)
{
const auto N = m_contour.distances.size();
const auto S = stride(N, accuracy);
if (N == 0 || S == 0)
return;
samples.reserve(N / S + 1);
for(size_t i = 0; i < N; i += S) {
samples.emplace_back(
ContourLocation{0, m_contour.distances[i] / m_contour.distances.back()});
}
for (size_t hidx = 1; hidx <= m_holes.size(); ++hidx) {
auto& hc = m_holes[hidx - 1];
const auto NH = hc.distances.size();
const auto SH = stride(NH, accuracy);
if (NH == 0 || SH == 0)
continue;
samples.reserve(samples.size() + NH / SH + 1);
for (size_t i = 0; i < NH; i += SH) {
samples.emplace_back(
ContourLocation{hidx, hc.distances[i] / hc.distances.back()});
}
}
}
Vec2crd coords(const Polygon &poly, const std::vector<double> &distances, double distance)
{
assert(poly.size() > 1 && distance >= .0 && distance <= 1.0);
// distance is from 0.0 to 1.0, we scale it up to the full length of
// the circumference
double d = distance * distances.back();
// Magic: we find the right edge in log time
auto it = std::lower_bound(distances.begin(), distances.end(), d);
assert(it != distances.end());
auto idx = it - distances.begin(); // get the index of the edge
auto &pts = poly.points;
auto edge = idx == long(pts.size() - 1) ? Line(pts.back(), pts.front()) :
Line(pts[idx], pts[idx + 1]);
// Get the remaining distance on the target edge
auto ed = d - (idx > 0 ? *std::prev(it) : 0 );
double t = ed / edge.length();
Vec2d n {double(edge.b.x()) - edge.a.x(), double(edge.b.y()) - edge.a.y()};
Vec2crd ret = (edge.a.cast<double>() + t * n).cast<coord_t>();
return ret;
}
void fill_distances(const Polygon &poly, std::vector<double> &distances)
{
distances.reserve(poly.size());
double dist = 0.;
auto lrange = line_range(poly);
for (const Line &l : lrange) {
dist += l.length();
distances.emplace_back(dist);
}
}
}} // namespace Slic3r::arr2

View File

@@ -0,0 +1,72 @@
#ifndef EDGECACHE_HPP
#define EDGECACHE_HPP
#include <vector>
#include <libslic3r/ExPolygon.hpp>
namespace Slic3r { namespace arr2 {
// Position on the circumference of an ExPolygon.
// countour_id: 0th is contour, 1..N are holes
// dist: position given as a floating point number within <0., 1.>
struct ContourLocation { size_t contour_id; double dist; };
void fill_distances(const Polygon &poly, std::vector<double> &distances);
Vec2crd coords(const Polygon &poly, const std::vector<double>& distances, double distance);
// A class for getting a point on the circumference of the polygon (in log time)
//
// This is a transformation of the provided polygon to be able to pinpoint
// locations on the circumference. The optimizer will pass a floating point
// value e.g. within <0,1> and we have to transform this value quickly into a
// coordinate on the circumference. By definition 0 should yield the first
// vertex and 1.0 would be the last (which should coincide with first).
//
// We also have to make this work for the holes of the captured polygon.
class EdgeCache {
struct ContourCache {
const Polygon *poly;
std::vector<double> distances;
} m_contour;
std::vector<ContourCache> m_holes;
void create_cache(const ExPolygon& sh);
Vec2crd coords(const ContourCache& cache, double distance) const;
public:
explicit EdgeCache(const ExPolygon *sh)
{
create_cache(*sh);
}
// Given coeff for accuracy <0., 1.>, return the number of vertices to skip
// when fetching corners.
static inline size_t stride(const size_t N, double accuracy)
{
size_t n = std::max(size_t{1}, N);
return static_cast<coord_t>(
std::round(N / std::pow(n, std::pow(accuracy, 1./3.)))
);
}
void sample_contour(double accuracy, std::vector<ContourLocation> &samples);
Vec2crd coords(const ContourLocation &loc) const
{
assert(loc.contour_id <= m_holes.size());
return loc.contour_id > 0 ?
coords(m_holes[loc.contour_id - 1], loc.dist) :
coords(m_contour, loc.dist);
}
};
}} // namespace Slic3r::arr2
#endif // EDGECACHE_HPP

View File

@@ -0,0 +1,62 @@
#ifndef COMPACTIFYKERNEL_HPP
#define COMPACTIFYKERNEL_HPP
#include <numeric>
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
#include <libslic3r/Geometry/ConvexHull.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include "KernelUtils.hpp"
namespace Slic3r { namespace arr2 {
struct CompactifyKernel {
ExPolygons merged_pile;
template<class ArrItem>
double placement_fitness(const ArrItem &itm, const Vec2crd &transl) const
{
auto pile = merged_pile;
ExPolygons itm_tr = to_expolygons(envelope_outline(itm));
for (auto &p : itm_tr)
p.translate(transl);
append(pile, std::move(itm_tr));
pile = union_ex(pile);
Polygon chull = Geometry::convex_hull(pile);
return -(chull.area());
}
template<class ArrItem, class Bed, class Context, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Context &packing_context,
const Range<RemIt> & /*remaining_items*/)
{
bool ret = find_initial_position(itm, bounding_box(bed).center(), bed,
packing_context);
merged_pile.clear();
for (const auto &gitm : all_items_range(packing_context)) {
append(merged_pile, to_expolygons(fixed_outline(gitm)));
}
merged_pile = union_ex(merged_pile);
return ret;
}
template<class ArrItem>
bool on_item_packed(ArrItem &itm) { return true; }
};
}} // namespace Slic3r::arr2
#endif // COMPACTIFYKERNEL_HPP

View File

@@ -0,0 +1,59 @@
#ifndef GRAVITYKERNEL_HPP
#define GRAVITYKERNEL_HPP
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
#include "KernelUtils.hpp"
namespace Slic3r { namespace arr2 {
struct GravityKernel {
std::optional<Vec2crd> sink;
std::optional<Vec2crd> item_sink;
Vec2d active_sink;
GravityKernel(Vec2crd gravity_center) : sink{gravity_center} {}
GravityKernel() = default;
template<class ArrItem>
double placement_fitness(const ArrItem &itm, const Vec2crd &transl) const
{
Vec2d center = unscaled(envelope_centroid(itm));
center += unscaled(transl);
return - (center - active_sink).squaredNorm();
}
template<class ArrItem, class Bed, class Ctx, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Ctx &packing_context,
const Range<RemIt> & /*remaining_items*/)
{
bool ret = false;
item_sink = get_gravity_sink(itm);
if (!sink) {
sink = bounding_box(bed).center();
}
if (item_sink)
active_sink = unscaled(*item_sink);
else
active_sink = unscaled(*sink);
ret = find_initial_position(itm, scaled(active_sink), bed, packing_context);
return ret;
}
template<class ArrItem> bool on_item_packed(ArrItem &itm) { return true; }
};
}} // namespace Slic3r::arr2
#endif // GRAVITYKERNEL_HPP

View File

@@ -0,0 +1,58 @@
#ifndef KERNELTRAITS_HPP
#define KERNELTRAITS_HPP
#include "libslic3r/Arrange/Core/ArrangeItemTraits.hpp"
namespace Slic3r { namespace arr2 {
// An arrangement kernel that specifies the object function to the arrangement
// optimizer and additional callback functions to be able to track the state
// of the arranged pile during arrangement.
template<class Kernel, class En = void> struct KernelTraits_
{
// Has to return a score value marking the quality of the arrangement. The
// higher this value is, the better a particular placement of the item is.
// parameter transl is the translation needed for the item to be moved to
// the candidate position.
// To discard the item, return NaN as score for every translation.
template<class ArrItem>
static double placement_fitness(const Kernel &k,
const ArrItem &itm,
const Vec2crd &transl)
{
return k.placement_fitness(itm, transl);
}
// Called whenever a new item is about to be processed by the optimizer.
// The current state of the arrangement can be saved by the kernel: the
// already placed items and the remaining items that need to fit into a
// particular bed.
// Returns true if the item is can be packed immediately, false if it
// should be processed further. This way, a kernel have the power to
// choose an initial position for the item that is not on the NFP.
template<class ArrItem, class Bed, class Ctx, class RemIt>
static bool on_start_packing(Kernel &k,
ArrItem &itm,
const Bed &bed,
const Ctx &packing_context,
const Range<RemIt> &remaining_items)
{
return k.on_start_packing(itm, bed, packing_context, remaining_items);
}
// Called when an item has been succesfully packed. itm should have the
// final translation and rotation already set.
// Can return false to discard the item after the optimization.
template<class ArrItem>
static bool on_item_packed(Kernel &k, ArrItem &itm)
{
return k.on_item_packed(itm);
}
};
template<class K> using KernelTraits = KernelTraits_<StripCVRef<K>>;
}} // namespace Slic3r::arr2
#endif // KERNELTRAITS_HPP

View File

@@ -0,0 +1,76 @@
#ifndef ARRANGEKERNELUTILS_HPP
#define ARRANGEKERNELUTILS_HPP
#include <type_traits>
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
#include "libslic3r/Arrange/Core/DataStoreTraits.hpp"
namespace Slic3r { namespace arr2 {
template<class Itm, class Bed, class Context>
bool find_initial_position(Itm &itm,
const Vec2crd &sink,
const Bed &bed,
const Context &packing_context)
{
bool ret = false;
if constexpr (std::is_convertible_v<Bed, RectangleBed> ||
std::is_convertible_v<Bed, InfiniteBed> ||
std::is_convertible_v<Bed, CircleBed>)
{
if (all_items_range(packing_context).empty()) {
auto rotations = allowed_rotations(itm);
auto chull = envelope_convex_hull(itm);
for (double rot : rotations) {
auto chullcpy = chull;
chullcpy.rotate(rot);
auto bbitm = bounding_box(chullcpy);
Vec2crd cb = sink;
Vec2crd ci = bbitm.center();
Vec2crd d = cb - ci;
bbitm.translate(d);
if (bounding_box(bed).contains(bbitm)) {
rotate(itm, rot);
translate(itm, d);
ret = true;
break;
}
}
}
}
return ret;
}
template<class ArrItem> std::optional<Vec2crd> get_gravity_sink(const ArrItem &itm)
{
constexpr const char * SinkKey = "sink";
std::optional<Vec2crd> ret;
auto ptr = get_data<Vec2crd>(itm, SinkKey);
if (ptr)
ret = *ptr;
return ret;
}
template<class ArrItem> bool is_wipe_tower(const ArrItem &itm)
{
constexpr const char * Key = "is_wipe_tower";
return has_key(itm, Key);
}
}} // namespace Slic3r::arr2
#endif // ARRANGEKERNELUTILS_HPP

View File

@@ -0,0 +1,95 @@
#ifndef RECTANGLEOVERFITKERNELWRAPPER_HPP
#define RECTANGLEOVERFITKERNELWRAPPER_HPP
#include "KernelTraits.hpp"
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
namespace Slic3r { namespace arr2 {
// This is a kernel wrapper that will apply a penality to the object function
// if the result cannot fit into the given rectangular bounds. This can be used
// to arrange into rectangular boundaries without calculating the IFP of the
// rectangle bed. Note that after the arrangement, what is garanteed is that
// the resulting pile will fit into the rectangular boundaries, but it will not
// be within the given rectangle. The items need to be moved afterwards manually.
// Use RectangeOverfitPackingStrategy to automate this post process step.
template<class Kernel>
struct RectangleOverfitKernelWrapper {
Kernel &k;
BoundingBox binbb;
BoundingBox pilebb;
RectangleOverfitKernelWrapper(Kernel &kern, const BoundingBox &limits)
: k{kern}
, binbb{limits}
{}
double overfit(const BoundingBox &itmbb) const
{
auto fullbb = pilebb;
fullbb.merge(itmbb);
auto fullbbsz = fullbb.size();
auto binbbsz = binbb.size();
auto wdiff = fullbbsz.x() - binbbsz.x() - SCALED_EPSILON;
auto hdiff = fullbbsz.y() - binbbsz.y() - SCALED_EPSILON;
double miss = .0;
if (wdiff > 0)
miss += double(wdiff);
if (hdiff > 0)
miss += double(hdiff);
miss = miss > 0? miss : 0;
return miss;
}
template<class ArrItem>
double placement_fitness(const ArrItem &item, const Vec2crd &transl) const
{
double score = KernelTraits<Kernel>::placement_fitness(k, item, transl);
auto itmbb = envelope_bounding_box(item);
itmbb.translate(transl);
double miss = overfit(itmbb);
score -= miss * miss;
return score;
}
template<class ArrItem, class Bed, class Ctx, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Ctx &packing_context,
const Range<RemIt> &remaining_items)
{
pilebb = BoundingBox{};
for (auto &fitm : all_items_range(packing_context))
pilebb.merge(fixed_bounding_box(fitm));
return KernelTraits<Kernel>::on_start_packing(k, itm, RectangleBed{binbb},
packing_context,
remaining_items);
}
template<class ArrItem>
bool on_item_packed(ArrItem &itm)
{
bool ret = KernelTraits<Kernel>::on_item_packed(k, itm);
double miss = overfit(envelope_bounding_box(itm));
if (miss > 0.)
ret = false;
return ret;
}
};
}} // namespace Slic3r::arr2
#endif // RECTANGLEOVERFITKERNELWRAPPER_H

View File

@@ -0,0 +1,97 @@
#ifndef SVGDEBUGOUTPUTKERNELWRAPPER_HPP
#define SVGDEBUGOUTPUTKERNELWRAPPER_HPP
#include <memory>
#include "KernelTraits.hpp"
#include "libslic3r/Arrange/Core/PackingContext.hpp"
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
#include <libslic3r/SVG.hpp>
namespace Slic3r { namespace arr2 {
template<class Kernel>
struct SVGDebugOutputKernelWrapper {
Kernel &k;
std::unique_ptr<Slic3r::SVG> svg;
BoundingBox drawbounds;
template<class... Args>
SVGDebugOutputKernelWrapper(const BoundingBox &bounds, Kernel &kern)
: k{kern}, drawbounds{bounds}
{}
template<class ArrItem, class Bed, class Context, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Context &packing_context,
const Range<RemIt> &rem)
{
using namespace Slic3r;
bool ret = KernelTraits<Kernel>::on_start_packing(k, itm, bed,
packing_context,
rem);
if (arr2::get_bed_index(itm) < 0)
return ret;
svg.reset();
auto bounds = drawbounds;
auto fixed = all_items_range(packing_context);
svg = std::make_unique<SVG>(std::string("arrange_bed") +
std::to_string(
arr2::get_bed_index(itm)) +
"_" + std::to_string(fixed.size()) +
".svg",
bounds, 0, false);
svg->draw(ExPolygon{arr2::to_rectangle(drawbounds)}, "blue", .2f);
auto nfp = calculate_nfp(itm, packing_context, bed);
svg->draw_outline(nfp);
svg->draw(nfp, "green", 0.2f);
for (const auto &fixeditm : fixed) {
ExPolygons fixeditm_outline = to_expolygons(fixed_outline(fixeditm));
svg->draw_outline(fixeditm_outline);
svg->draw(fixeditm_outline, "yellow", 0.5f);
}
return ret;
}
template<class ArrItem>
double placement_fitness(const ArrItem &item, const Vec2crd &transl) const
{
return KernelTraits<Kernel>::placement_fitness(k, item, transl);
}
template<class ArrItem>
bool on_item_packed(ArrItem &itm)
{
using namespace Slic3r;
using namespace Slic3r::arr2;
bool ret = KernelTraits<Kernel>::on_item_packed(k, itm);
if (svg) {
ExPolygons itm_outline = to_expolygons(fixed_outline(itm));
svg->draw_outline(itm_outline);
svg->draw(itm_outline, "grey");
svg->Close();
}
return ret;
}
};
}} // namespace Slic3r::arr2
#endif // SVGDEBUGOUTPUTKERNELWRAPPER_HPP

View File

@@ -0,0 +1,271 @@
#ifndef TMARRANGEKERNEL_HPP
#define TMARRANGEKERNEL_HPP
#include "libslic3r/Arrange/Core/NFP/NFPArrangeItemTraits.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
#include "KernelUtils.hpp"
#include <boost/geometry/index/rtree.hpp>
#include <libslic3r/BoostAdapter.hpp>
namespace Slic3r { namespace arr2 {
// Summon the spatial indexing facilities from boost
namespace bgi = boost::geometry::index;
using SpatElement = std::pair<BoundingBox, unsigned>;
using SpatIndex = bgi::rtree<SpatElement, bgi::rstar<16, 4> >;
class TMArrangeKernel {
SpatIndex m_rtree; // spatial index for the normal (bigger) objects
SpatIndex m_smallsrtree; // spatial index for only the smaller items
BoundingBox m_pilebb;
double m_bin_area = NaNd;
double m_norm;
size_t m_rem_cnt = 0;
size_t m_item_cnt = 0;
struct ItemStats { double area = 0.; BoundingBox bb; };
std::vector<ItemStats> m_itemstats;
// A coefficient used in separating bigger items and smaller items.
static constexpr double BigItemTreshold = 0.02;
template<class T> ArithmeticOnly<T, double> norm(T val) const
{
return double(val) / m_norm;
}
// Treat big items (compared to the print bed) differently
bool is_big(double a) const { return a / m_bin_area > BigItemTreshold; }
protected:
std::optional<Point> sink;
std::optional<Point> item_sink;
Point active_sink;
const BoundingBox & pilebb() const { return m_pilebb; }
public:
TMArrangeKernel() = default;
TMArrangeKernel(Vec2crd gravity_center, size_t itm_cnt, double bedarea = NaNd)
: sink{gravity_center}
, m_bin_area(bedarea)
, m_item_cnt{itm_cnt}
{}
TMArrangeKernel(size_t itm_cnt, double bedarea = NaNd)
: m_bin_area(bedarea), m_item_cnt{itm_cnt}
{}
template<class ArrItem>
double placement_fitness(const ArrItem &item, const Vec2crd &transl) const
{
// Candidate item bounding box
auto ibb = envelope_bounding_box(item);
ibb.translate(transl);
auto itmcntr = envelope_centroid(item);
itmcntr += transl;
// Calculate the full bounding box of the pile with the candidate item
auto fullbb = m_pilebb;
fullbb.merge(ibb);
// The bounding box of the big items (they will accumulate in the center
// of the pile
BoundingBox bigbb;
if(m_rtree.empty()) {
bigbb = fullbb;
}
else {
auto boostbb = m_rtree.bounds();
boost::geometry::convert(boostbb, bigbb);
}
// Will hold the resulting score
double score = 0;
// Density is the pack density: how big is the arranged pile
double density = 0;
// Distinction of cases for the arrangement scene
enum e_cases {
// This branch is for big items in a mixed (big and small) scene
// OR for all items in a small-only scene.
BIG_ITEM,
// This branch is for the last big item in a mixed scene
LAST_BIG_ITEM,
// For small items in a mixed scene.
SMALL_ITEM,
WIPE_TOWER,
} compute_case;
bool is_wt = is_wipe_tower(item);
bool bigitems = is_big(envelope_area(item)) || m_rtree.empty();
if (is_wt)
compute_case = WIPE_TOWER;
else if (bigitems && m_rem_cnt > 0)
compute_case = BIG_ITEM;
else if (bigitems && m_rem_cnt == 0)
compute_case = LAST_BIG_ITEM;
else
compute_case = SMALL_ITEM;
switch (compute_case) {
case WIPE_TOWER: {
score = (unscaled(itmcntr) - unscaled(active_sink)).squaredNorm();
break;
}
case BIG_ITEM: {
const Point& minc = ibb.min; // bottom left corner
const Point& maxc = ibb.max; // top right corner
// top left and bottom right corners
Point top_left{minc.x(), maxc.y()};
Point bottom_right{maxc.x(), minc.y()};
// Now the distance of the gravity center will be calculated to the
// five anchor points and the smallest will be chosen.
std::array<double, 5> dists;
auto cc = fullbb.center(); // The gravity center
dists[0] = (minc - cc).cast<double>().norm();
dists[1] = (maxc - cc).cast<double>().norm();
dists[2] = (itmcntr - cc).template cast<double>().norm();
dists[3] = (top_left - cc).cast<double>().norm();
dists[4] = (bottom_right - cc).cast<double>().norm();
// The smalles distance from the arranged pile center:
double dist = norm(*(std::min_element(dists.begin(), dists.end())));
double bindist = norm((ibb.center() - active_sink).template cast<double>().norm());
dist = 0.8 * dist + 0.2 * bindist;
// Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item
// aligned with its neighbors. We will check the alignment
// with all neighbors and return the score for the best
// alignment. So it is enough for the candidate to be
// aligned with only one item.
auto alignment_score = 1.0;
auto query = bgi::intersects(ibb);
auto& index = is_big(envelope_area(item)) ? m_rtree : m_smallsrtree;
// Query the spatial index for the neighbors
std::vector<SpatElement> result;
result.reserve(index.size());
index.query(query, std::back_inserter(result));
// now get the score for the best alignment
for(auto& e : result) {
auto idx = e.second;
const ItemStats& p = m_itemstats[idx];
auto parea = p.area;
if(std::abs(1.0 - parea / fixed_area(item)) < 1e-6) {
auto bb = p.bb;
bb.merge(ibb);
auto bbarea = area(bb);
auto ascore = 1.0 - (fixed_area(item) + parea) / bbarea;
if(ascore < alignment_score)
alignment_score = ascore;
}
}
auto fullbbsz = fullbb.size();
density = std::sqrt(norm(fullbbsz.x()) * norm(fullbbsz.y()));
double R = double(m_rem_cnt) / (m_item_cnt);
// The final mix of the score is the balance between the
// distance from the full pile center, the pack density and
// the alignment with the neighbors
if (result.empty())
score = 0.50 * dist + 0.50 * density;
else
// Let the density matter more when fewer objects remain
score = 0.50 * dist + (1.0 - R) * 0.20 * density +
0.30 * alignment_score;
break;
}
case LAST_BIG_ITEM: {
score = norm((itmcntr - m_pilebb.center()).template cast<double>().norm());
break;
}
case SMALL_ITEM: {
// Here there are the small items that should be placed around the
// already processed bigger items.
// No need to play around with the anchor points, the center will be
// just fine for small items
score = norm((itmcntr - bigbb.center()).template cast<double>().norm());
break;
}
}
return -score;
}
template<class ArrItem, class Bed, class Context, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Context &packing_context,
const Range<RemIt> &remaining_items)
{
item_sink = get_gravity_sink(itm);
if (!sink) {
sink = bounding_box(bed).center();
}
if (item_sink)
active_sink = *item_sink;
else
active_sink = *sink;
auto fixed = all_items_range(packing_context);
bool ret = find_initial_position(itm, active_sink, bed, packing_context);
m_rem_cnt = remaining_items.size();
if (m_item_cnt == 0)
m_item_cnt = m_rem_cnt + fixed.size() + 1;
if (std::isnan(m_bin_area))
m_bin_area = area(bed);
m_norm = std::sqrt(m_bin_area);
m_itemstats.clear();
m_itemstats.reserve(fixed.size());
m_rtree.clear();
m_smallsrtree.clear();
m_pilebb = {};
unsigned idx = 0;
for (auto &fixitem : fixed) {
auto fixitmbb = fixed_bounding_box(fixitem);
m_itemstats.emplace_back(ItemStats{fixed_area(fixitem), fixitmbb});
m_pilebb.merge(fixitmbb);
if(is_big(fixed_area(fixitem)))
m_rtree.insert({fixitmbb, idx});
m_smallsrtree.insert({fixitmbb, idx});
idx++;
}
return ret;
}
template<class ArrItem>
bool on_item_packed(ArrItem &itm) { return true; }
};
}} // namespace Slic3r::arr2
#endif // TMARRANGEKERNEL_HPP

View File

@@ -0,0 +1,419 @@
#ifndef NFP_CPP
#define NFP_CPP
#include "NFP.hpp"
#include "CircularEdgeIterator.hpp"
#include "NFPConcave_Tesselate.hpp"
#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__)
namespace Slic3r { using LargeInt = __int128; }
#else
#include <boost/multiprecision/integer.hpp>
namespace Slic3r { using LargeInt = boost::multiprecision::int128_t; }
#endif
#include <boost/rational.hpp>
namespace Slic3r {
static bool line_cmp(const Line& e1, const Line& e2)
{
using Ratio = boost::rational<LargeInt>;
const Vec<2, int64_t> ax(1, 0); // Unit vector for the X axis
Vec<2, int64_t> p1 = (e1.b - e1.a).cast<int64_t>();
Vec<2, int64_t> p2 = (e2.b - e2.a).cast<int64_t>();
// Quadrant mapping array. The quadrant of a vector can be determined
// from the dot product of the vector and its perpendicular pair
// with the unit vector X axis. The products will carry the values
// lcos = dot(p, ax) = l * cos(phi) and
// lsin = -dotperp(p, ax) = l * sin(phi) where
// l is the length of vector p. From the signs of these values we can
// construct an index which has the sign of lcos as MSB and the
// sign of lsin as LSB. This index can be used to retrieve the actual
// quadrant where vector p resides using the following map:
// (+ is 0, - is 1)
// cos | sin | decimal | quadrant
// + | + | 0 | 0
// + | - | 1 | 3
// - | + | 2 | 1
// - | - | 3 | 2
std::array<int, 4> quadrants {0, 3, 1, 2 };
std::array<int, 2> q {0, 0}; // Quadrant indices for p1 and p2
using TDots = std::array<int64_t, 2>;
TDots lcos { p1.dot(ax), p2.dot(ax) };
TDots lsin { -dotperp(p1, ax), -dotperp(p2, ax) };
// Construct the quadrant indices for p1 and p2
for(size_t i = 0; i < 2; ++i) {
if (lcos[i] == 0)
q[i] = lsin[i] > 0 ? 1 : 3;
else if (lsin[i] == 0)
q[i] = lcos[i] > 0 ? 0 : 2;
else
q[i] = quadrants[((lcos[i] < 0) << 1) + (lsin[i] < 0)];
}
if (q[0] == q[1]) { // only bother if p1 and p2 are in the same quadrant
auto lsq1 = p1.squaredNorm(); // squared magnitudes, avoid sqrt
auto lsq2 = p2.squaredNorm(); // squared magnitudes, avoid sqrt
// We will actually compare l^2 * cos^2(phi) which saturates the
// cos function. But with the quadrant info we can get the sign back
int sign = q[0] == 1 || q[0] == 2 ? -1 : 1;
// If Ratio is an actual rational type, there is no precision loss
auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0];
auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1];
return q[0] < 2 ? pcos1 > pcos2 : pcos1 < pcos2;
}
// If in different quadrants, compare the quadrant indices only.
return q[0] < q[1];
}
static inline bool vsort(const Vec2crd& v1, const Vec2crd& v2)
{
return v1.y() == v2.y() ? v1.x() < v2.x() : v1.y() < v2.y();
}
ExPolygons ifp_convex(const arr2::RectangleBed &obed, const Polygon &convexpoly)
{
ExPolygon ret;
auto sbox = bounding_box(convexpoly);
auto sboxsize = sbox.size();
coord_t sheight = sboxsize.y();
coord_t swidth = sboxsize.x();
Point sliding_top = reference_vertex(convexpoly);
auto leftOffset = sliding_top.x() - sbox.min.x();
auto rightOffset = sliding_top.x() - sbox.max.x();
coord_t topOffset = 0;
auto bottomOffset = sheight;
auto bedbb = obed.bb;
// bedbb.offset(1);
auto bedsz = bedbb.size();
auto boxWidth = bedsz.x();
auto boxHeight = bedsz.y();
auto bedMinx = bedbb.min.x();
auto bedMiny = bedbb.min.y();
auto bedMaxx = bedbb.max.x();
auto bedMaxy = bedbb.max.y();
Polygon innerNfp{ Point{bedMinx + leftOffset, bedMaxy + topOffset},
Point{bedMaxx + rightOffset, bedMaxy + topOffset},
Point{bedMaxx + rightOffset, bedMiny + bottomOffset},
Point{bedMinx + leftOffset, bedMiny + bottomOffset},
Point{bedMinx + leftOffset, bedMaxy + topOffset} };
if (sheight <= boxHeight && swidth <= boxWidth)
ret.contour = std::move(innerNfp);
return {ret};
}
Polygon ifp_convex_convex(const Polygon &fixed, const Polygon &movable)
{
auto subnfps = reserve_polygons(fixed.size());
// For each edge of the bed polygon, determine the nfp of convexpoly and
// the zero area polygon formed by the edge. The union of all these sub-nfps
// will contain a hole that is the actual ifp.
auto lrange = line_range(fixed);
for (const Line &l : lrange) { // Older mac compilers generate warnging if line_range is called in-place
Polygon fixed = {l.a, l.b};
subnfps.emplace_back(nfp_convex_convex_legacy(fixed, movable));
}
// Do the union and then keep only the holes (should be only one or zero, if
// the convexpoly cannot fit into the bed)
Polygons ifp = union_(subnfps);
Polygon ret;
// find the first hole
auto it = std::find_if(ifp.begin(), ifp.end(), [](const Polygon &subifp){
return subifp.is_clockwise();
});
if (it != ifp.end()) {
ret = std::move(*it);
std::reverse(ret.begin(), ret.end());
}
return ret;
}
ExPolygons ifp_convex(const arr2::CircleBed &bed, const Polygon &convexpoly)
{
Polygon circle = approximate_circle_with_polygon(bed);
return {ExPolygon{ifp_convex_convex(circle, convexpoly)}};
}
ExPolygons ifp_convex(const arr2::IrregularBed &bed, const Polygon &convexpoly)
{
auto bb = get_extents(bed.poly);
bb.offset(scaled(1.));
Polygon rect = arr2::to_rectangle(bb);
ExPolygons blueprint = diff_ex(rect, bed.poly);
Polygons ifp;
for (const ExPolygon &part : blueprint) {
Polygons triangles = Slic3r::convex_decomposition_tess(part);
for (const Polygon &tr : triangles) {
Polygon subifp = nfp_convex_convex_legacy(tr, convexpoly);
ifp.emplace_back(std::move(subifp));
}
}
ifp = union_(ifp);
Polygons ret;
std::copy_if(ifp.begin(), ifp.end(), std::back_inserter(ret),
[](const Polygon &p) { return p.is_clockwise(); });
for (Polygon &p : ret)
std::reverse(p.begin(), p.end());
return to_expolygons(ret);
}
Vec2crd reference_vertex(const Polygon &poly)
{
Vec2crd ret{std::numeric_limits<coord_t>::min(),
std::numeric_limits<coord_t>::min()};
auto it = std::max_element(poly.points.begin(), poly.points.end(), vsort);
if (it != poly.points.end())
ret = std::max(ret, static_cast<const Vec2crd &>(*it), vsort);
return ret;
}
Vec2crd reference_vertex(const ExPolygon &expoly)
{
return reference_vertex(expoly.contour);
}
Vec2crd reference_vertex(const Polygons &outline)
{
Vec2crd ret{std::numeric_limits<coord_t>::min(),
std::numeric_limits<coord_t>::min()};
for (const Polygon &poly : outline)
ret = std::max(ret, reference_vertex(poly), vsort);
return ret;
}
Vec2crd reference_vertex(const ExPolygons &outline)
{
Vec2crd ret{std::numeric_limits<coord_t>::min(),
std::numeric_limits<coord_t>::min()};
for (const ExPolygon &expoly : outline)
ret = std::max(ret, reference_vertex(expoly), vsort);
return ret;
}
Vec2crd min_vertex(const Polygon &poly)
{
Vec2crd ret{std::numeric_limits<coord_t>::max(),
std::numeric_limits<coord_t>::max()};
auto it = std::min_element(poly.points.begin(), poly.points.end(), vsort);
if (it != poly.points.end())
ret = std::min(ret, static_cast<const Vec2crd&>(*it), vsort);
return ret;
}
// Find the vertex corresponding to the edge with minimum angle to X axis.
// Only usable with CircularEdgeIterator<> template.
template<class It> It find_min_anglex_edge(It from)
{
bool found = false;
auto it = from;
while (!found ) {
found = !line_cmp(*it, *std::next(it));
++it;
}
return it;
}
// Only usable if both fixed and movable polygon is convex. In that case,
// their edges are already sorted by angle to X axis, only the starting
// (lowest X axis) edge needs to be found first.
void nfp_convex_convex(const Polygon &fixed, const Polygon &movable, Polygon &poly)
{
if (fixed.empty() || movable.empty())
return;
// Clear poly and adjust its capacity. Nothing happens if poly is
// already sufficiently large and and empty.
poly.clear();
poly.points.reserve(fixed.size() + movable.size());
// Find starting positions on the fixed and moving polygons
auto it_fx = find_min_anglex_edge(CircularEdgeIterator{fixed});
auto it_mv = find_min_anglex_edge(CircularReverseEdgeIterator{movable});
// End positions are at the same vertex after completing one full circle
auto end_fx = it_fx + fixed.size();
auto end_mv = it_mv + movable.size();
// Pos zero is just fine as starting point:
poly.points.emplace_back(0, 0);
// Output iterator adapter for std::merge
struct OutItAdaptor {
using value_type [[maybe_unused]] = Line;
using difference_type [[maybe_unused]] = std::ptrdiff_t;
using pointer [[maybe_unused]] = Line*;
using reference [[maybe_unused]] = Line& ;
using iterator_category [[maybe_unused]] = std::output_iterator_tag;
Polygon *outpoly;
OutItAdaptor(Polygon &out) : outpoly{&out} {}
OutItAdaptor &operator *() { return *this; }
void operator=(const Line &l)
{
// Yielding l.b, offsetted so that l.a touches the last vertex in
// in outpoly
outpoly->points.emplace_back(l.b + outpoly->back() - l.a);
}
OutItAdaptor& operator++() { return *this; };
};
// Use std algo to merge the edges from the two polygons
std::merge(it_fx, end_fx, it_mv, end_mv, OutItAdaptor{poly}, line_cmp);
}
Polygon nfp_convex_convex(const Polygon &fixed, const Polygon &movable)
{
Polygon ret;
nfp_convex_convex(fixed, movable, ret);
return ret;
}
static void buildPolygon(const std::vector<Line>& edgelist,
Polygon& rpoly,
Point& top_nfp)
{
auto& rsh = rpoly.points;
rsh.reserve(2 * edgelist.size());
// Add the two vertices from the first edge into the final polygon.
rsh.emplace_back(edgelist.front().a);
rsh.emplace_back(edgelist.front().b);
// Sorting function for the nfp reference vertex search
// the reference (rightmost top) vertex so far
top_nfp = *std::max_element(std::cbegin(rsh), std::cend(rsh), vsort);
auto tmp = std::next(std::begin(rsh));
// Construct final nfp by placing each edge to the end of the previous
for(auto eit = std::next(edgelist.begin()); eit != edgelist.end(); ++eit) {
auto d = *tmp - eit->a;
Vec2crd p = eit->b + d;
rsh.emplace_back(p);
// Set the new reference vertex
if (vsort(top_nfp, p))
top_nfp = p;
tmp = std::next(tmp);
}
}
Polygon nfp_convex_convex_legacy(const Polygon &fixed, const Polygon &movable)
{
assert (!fixed.empty());
assert (!movable.empty());
Polygon rsh; // Final nfp placeholder
Point max_nfp;
std::vector<Line> edgelist;
auto cap = fixed.points.size() + movable.points.size();
// Reserve the needed memory
edgelist.reserve(cap);
rsh.points.reserve(cap);
auto add_edge = [&edgelist](const Point &v1, const Point &v2) {
Line e{v1, v2};
if ((e.b - e.a).cast<int64_t>().squaredNorm() > 0)
edgelist.emplace_back(e);
};
Point max_fixed = fixed.points.front();
{ // place all edges from fixed into edgelist
auto first = std::cbegin(fixed);
auto next = std::next(first);
while(next != std::cend(fixed)) {
add_edge(*(first), *(next));
max_fixed = std::max(max_fixed, *first, vsort);
++first; ++next;
}
add_edge(*std::crbegin(fixed), *std::cbegin(fixed));
max_fixed = std::max(max_fixed, *std::crbegin(fixed), vsort);
}
Point max_movable = movable.points.front();
Point min_movable = movable.points.front();
{ // place all edges from movable into edgelist
auto first = std::cbegin(movable);
auto next = std::next(first);
while(next != std::cend(movable)) {
add_edge(*(next), *(first));
min_movable = std::min(min_movable, *first, vsort);
max_movable = std::max(max_movable, *first, vsort);
++first; ++next;
}
add_edge(*std::cbegin(movable), *std::crbegin(movable));
min_movable = std::min(min_movable, *std::crbegin(movable), vsort);
max_movable = std::max(max_movable, *std::crbegin(movable), vsort);
}
std::sort(edgelist.begin(), edgelist.end(), line_cmp);
buildPolygon(edgelist, rsh, max_nfp);
auto dtouch = max_fixed - min_movable;
auto top_other = max_movable + dtouch;
auto dnfp = top_other - max_nfp;
rsh.translate(dnfp);
return rsh;
}
} // namespace Slic3r
#endif // NFP_CPP

View File

@@ -0,0 +1,51 @@
#ifndef NFP_HPP
#define NFP_HPP
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/Arrange/Core/Beds.hpp>
namespace Slic3r {
template<class Unit = int64_t, class T>
Unit dotperp(const Vec<2, T> &a, const Vec<2, T> &b)
{
return Unit(a.x()) * Unit(b.y()) - Unit(a.y()) * Unit(b.x());
}
// Convex-Convex nfp in linear time (fixed.size() + movable.size()),
// no memory allocations (if out param is used).
// FIXME: Currently broken for very sharp triangles.
Polygon nfp_convex_convex(const Polygon &fixed, const Polygon &movable);
void nfp_convex_convex(const Polygon &fixed, const Polygon &movable, Polygon &out);
Polygon nfp_convex_convex_legacy(const Polygon &fixed, const Polygon &movable);
Polygon ifp_convex_convex(const Polygon &fixed, const Polygon &movable);
ExPolygons ifp_convex(const arr2::RectangleBed &bed, const Polygon &convexpoly);
ExPolygons ifp_convex(const arr2::CircleBed &bed, const Polygon &convexpoly);
ExPolygons ifp_convex(const arr2::IrregularBed &bed, const Polygon &convexpoly);
inline ExPolygons ifp_convex(const arr2::InfiniteBed &bed, const Polygon &convexpoly)
{
return {};
}
inline ExPolygons ifp_convex(const arr2::ArrangeBed &bed, const Polygon &convexpoly)
{
ExPolygons ret;
auto visitor = [&ret, &convexpoly](const auto &b) { ret = ifp_convex(b, convexpoly); };
boost::apply_visitor(visitor, bed);
return ret;
}
Vec2crd reference_vertex(const Polygon &outline);
Vec2crd reference_vertex(const ExPolygon &outline);
Vec2crd reference_vertex(const Polygons &outline);
Vec2crd reference_vertex(const ExPolygons &outline);
Vec2crd min_vertex(const Polygon &outline);
} // namespace Slic3r
#endif // NFP_HPP

View File

@@ -0,0 +1,197 @@
#ifndef NFPARRANGEITEMTRAITS_HPP
#define NFPARRANGEITEMTRAITS_HPP
#include <numeric>
#include "libslic3r/Arrange/Core/ArrangeBase.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/BoundingBox.hpp"
namespace Slic3r { namespace arr2 {
// Additional methods that an ArrangeItem object has to implement in order
// to be usable with PackStrategyNFP.
template<class ArrItem, class En = void> struct NFPArrangeItemTraits_
{
template<class Context, class Bed, class StopCond = DefaultStopCondition>
static ExPolygons calculate_nfp(const ArrItem &item,
const Context &packing_context,
const Bed &bed,
StopCond stop_condition = {})
{
static_assert(always_false<ArrItem>::value,
"NFP unimplemented for this item type.");
return {};
}
static Vec2crd reference_vertex(const ArrItem &item)
{
return item.reference_vertex();
}
static BoundingBox envelope_bounding_box(const ArrItem &itm)
{
return itm.envelope_bounding_box();
}
static BoundingBox fixed_bounding_box(const ArrItem &itm)
{
return itm.fixed_bounding_box();
}
static const Polygons & envelope_outline(const ArrItem &itm)
{
return itm.envelope_outline();
}
static const Polygons & fixed_outline(const ArrItem &itm)
{
return itm.fixed_outline();
}
static const Polygon & envelope_convex_hull(const ArrItem &itm)
{
return itm.envelope_convex_hull();
}
static const Polygon & fixed_convex_hull(const ArrItem &itm)
{
return itm.fixed_convex_hull();
}
static double envelope_area(const ArrItem &itm)
{
return itm.envelope_area();
}
static double fixed_area(const ArrItem &itm)
{
return itm.fixed_area();
}
static auto allowed_rotations(const ArrItem &)
{
return std::array{0.};
}
static Vec2crd fixed_centroid(const ArrItem &itm)
{
return fixed_bounding_box(itm).center();
}
static Vec2crd envelope_centroid(const ArrItem &itm)
{
return envelope_bounding_box(itm).center();
}
};
template<class T>
using NFPArrangeItemTraits = NFPArrangeItemTraits_<StripCVRef<T>>;
template<class ArrItem,
class Context,
class Bed,
class StopCond = DefaultStopCondition>
ExPolygons calculate_nfp(const ArrItem &itm,
const Context &context,
const Bed &bed,
StopCond stopcond = {})
{
return NFPArrangeItemTraits<ArrItem>::calculate_nfp(itm, context, bed,
std::move(stopcond));
}
template<class ArrItem> Vec2crd reference_vertex(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::reference_vertex(itm);
}
template<class ArrItem> BoundingBox envelope_bounding_box(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::envelope_bounding_box(itm);
}
template<class ArrItem> BoundingBox fixed_bounding_box(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::fixed_bounding_box(itm);
}
template<class ArrItem> decltype(auto) envelope_convex_hull(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::envelope_convex_hull(itm);
}
template<class ArrItem> decltype(auto) fixed_convex_hull(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::fixed_convex_hull(itm);
}
template<class ArrItem> decltype(auto) envelope_outline(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::envelope_outline(itm);
}
template<class ArrItem> decltype(auto) fixed_outline(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::fixed_outline(itm);
}
template<class ArrItem> double envelope_area(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::envelope_area(itm);
}
template<class ArrItem> double fixed_area(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::fixed_area(itm);
}
template<class ArrItem> Vec2crd fixed_centroid(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::fixed_centroid(itm);
}
template<class ArrItem> Vec2crd envelope_centroid(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::envelope_centroid(itm);
}
template<class ArrItem>
auto allowed_rotations(const ArrItem &itm)
{
return NFPArrangeItemTraits<ArrItem>::allowed_rotations(itm);
}
template<class It>
BoundingBox bounding_box(const Range<It> &itms) noexcept
{
auto pilebb =
std::accumulate(itms.begin(), itms.end(), BoundingBox{},
[](BoundingBox bb, const auto &itm) {
bb.merge(fixed_bounding_box(itm));
return bb;
});
return pilebb;
}
template<class It>
BoundingBox bounding_box_on_bedidx(const Range<It> &itms, int bed_index) noexcept
{
auto pilebb =
std::accumulate(itms.begin(), itms.end(), BoundingBox{},
[bed_index](BoundingBox bb, const auto &itm) {
if (bed_index == get_bed_index(itm))
bb.merge(fixed_bounding_box(itm));
return bb;
});
return pilebb;
}
}} // namespace Slic3r::arr2
#endif // ARRANGEITEMTRAITSNFP_HPP

View File

@@ -0,0 +1,112 @@
#include "NFP.hpp"
#include "NFPConcave_CGAL.hpp"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/partition_2.h>
#include <CGAL/Partition_traits_2.h>
#include <CGAL/property_map.h>
#include <CGAL/Polygon_vertical_decomposition_2.h>
#include "libslic3r/ClipperUtils.hpp"
namespace Slic3r {
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Partition_traits_2 = CGAL::Partition_traits_2<K, CGAL::Pointer_property_map<K::Point_2>::type >;
using Point_2 = Partition_traits_2::Point_2;
using Polygon_2 = Partition_traits_2::Polygon_2; // a polygon of indices
ExPolygons nfp_concave_concave_cgal(const ExPolygon &fixed, const ExPolygon &movable)
{
Polygons fixed_decomp = convex_decomposition_cgal(fixed);
Polygons movable_decomp = convex_decomposition_cgal(movable);
auto refs_mv = reserve_vector<Vec2crd>(movable_decomp.size());
for (const Polygon &p : movable_decomp)
refs_mv.emplace_back(reference_vertex(p));
auto nfps = reserve_polygons(fixed_decomp.size() *movable_decomp.size());
Vec2crd ref_whole = reference_vertex(movable);
for (const Polygon &fixed_part : fixed_decomp) {
size_t mvi = 0;
for (const Polygon &movable_part : movable_decomp) {
Polygon subnfp = nfp_convex_convex(fixed_part, movable_part);
const Vec2crd &ref_mp = refs_mv[mvi];
auto d = ref_whole - ref_mp;
subnfp.translate(d);
nfps.emplace_back(subnfp);
mvi++;
}
}
return union_ex(nfps);
}
// TODO: holes
Polygons convex_decomposition_cgal(const ExPolygon &expoly)
{
CGAL::Polygon_vertical_decomposition_2<K> decomp;
CGAL::Polygon_2<K> contour;
for (auto &p : expoly.contour.points)
contour.push_back({unscaled(p.x()), unscaled(p.y())});
CGAL::Polygon_with_holes_2<K> cgalpoly{contour};
for (const Polygon &h : expoly.holes) {
CGAL::Polygon_2<K> hole;
for (auto &p : h.points)
hole.push_back({unscaled(p.x()), unscaled(p.y())});
cgalpoly.add_hole(hole);
}
std::vector<CGAL::Polygon_2<K>> out;
decomp(cgalpoly, std::back_inserter(out));
Polygons ret;
for (auto &pwh : out) {
Polygon poly;
for (auto &p : pwh)
poly.points.emplace_back(scaled(p.x()), scaled(p.y()));
ret.emplace_back(std::move(poly));
}
return ret; //convex_decomposition_cgal(expoly.contour);
}
Polygons convex_decomposition_cgal(const Polygon &poly)
{
auto pts = reserve_vector<K::Point_2>(poly.size());
for (const Point &p : poly.points)
pts.emplace_back(unscaled(p.x()), unscaled(p.y()));
Partition_traits_2 traits(CGAL::make_property_map(pts));
Polygon_2 polyidx;
for (size_t i = 0; i < pts.size(); ++i)
polyidx.push_back(i);
std::vector<Polygon_2> outp;
CGAL::optimal_convex_partition_2(polyidx.vertices_begin(),
polyidx.vertices_end(),
std::back_inserter(outp),
traits);
Polygons ret;
for (const Polygon_2& poly : outp){
Polygon r;
for(Point_2 p : poly.container())
r.points.emplace_back(scaled(pts[p].x()), scaled(pts[p].y()));
ret.emplace_back(std::move(r));
}
return ret;
}
} // namespace Slic3r

View File

@@ -0,0 +1,15 @@
#ifndef NFPCONCAVE_CGAL_HPP
#define NFPCONCAVE_CGAL_HPP
#include <libslic3r/ExPolygon.hpp>
namespace Slic3r {
Polygons convex_decomposition_cgal(const Polygon &expoly);
Polygons convex_decomposition_cgal(const ExPolygon &expoly);
ExPolygons nfp_concave_concave_cgal(const ExPolygon &fixed, const ExPolygon &movable);
} // namespace Slic3r
#endif // NFPCONCAVE_CGAL_HPP

View File

@@ -0,0 +1,71 @@
#include "NFPConcave_Tesselate.hpp"
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Tesselate.hpp>
#include "NFP.hpp"
namespace Slic3r {
Polygons convex_decomposition_tess(const Polygon &expoly)
{
return convex_decomposition_tess(ExPolygon{expoly});
}
Polygons convex_decomposition_tess(const ExPolygon &expoly)
{
std::vector<Vec2d> tr = Slic3r::triangulate_expolygon_2d(expoly);
auto ret = Slic3r::reserve_polygons(tr.size() / 3);
for (size_t i = 0; i < tr.size(); i += 3) {
ret.emplace_back(
Polygon{scaled(tr[i]), scaled(tr[i + 1]), scaled(tr[i + 2])});
}
return ret;
}
Polygons convex_decomposition_tess(const ExPolygons &expolys)
{
constexpr size_t AvgTriangleCountGuess = 50;
auto ret = reserve_polygons(AvgTriangleCountGuess * expolys.size());
for (const ExPolygon &expoly : expolys) {
Polygons convparts = convex_decomposition_tess(expoly);
std::move(convparts.begin(), convparts.end(), std::back_inserter(ret));
}
return ret;
}
ExPolygons nfp_concave_concave_tess(const ExPolygon &fixed,
const ExPolygon &movable)
{
Polygons fixed_decomp = convex_decomposition_tess(fixed);
Polygons movable_decomp = convex_decomposition_tess(movable);
auto refs_mv = reserve_vector<Vec2crd>(movable_decomp.size());
for (const Polygon &p : movable_decomp)
refs_mv.emplace_back(reference_vertex(p));
auto nfps = reserve_polygons(fixed_decomp.size() * movable_decomp.size());
Vec2crd ref_whole = reference_vertex(movable);
for (const Polygon &fixed_part : fixed_decomp) {
size_t mvi = 0;
for (const Polygon &movable_part : movable_decomp) {
Polygon subnfp = nfp_convex_convex(fixed_part, movable_part);
const Vec2crd &ref_mp = refs_mv[mvi];
auto d = ref_whole - ref_mp;
subnfp.translate(d);
nfps.emplace_back(subnfp);
mvi++;
}
}
return union_ex(nfps);
}
} // namespace Slic3r

View File

@@ -0,0 +1,16 @@
#ifndef NFPCONCAVE_TESSELATE_HPP
#define NFPCONCAVE_TESSELATE_HPP
#include <libslic3r/ExPolygon.hpp>
namespace Slic3r {
Polygons convex_decomposition_tess(const Polygon &expoly);
Polygons convex_decomposition_tess(const ExPolygon &expoly);
Polygons convex_decomposition_tess(const ExPolygons &expolys);
ExPolygons nfp_concave_concave_tess(const ExPolygon &fixed, const ExPolygon &movable);
} // namespace Slic3r
#endif // NFPCONCAVE_TESSELATE_HPP

View File

@@ -0,0 +1,286 @@
#ifndef PACKSTRATEGYNFP_HPP
#define PACKSTRATEGYNFP_HPP
#include "libslic3r/Arrange/Core/ArrangeBase.hpp"
#include "EdgeCache.hpp"
#include "Kernels/KernelTraits.hpp"
#include "NFPArrangeItemTraits.hpp"
#include "libslic3r/Optimize/NLoptOptimizer.hpp"
#include "libslic3r/Execution/ExecutionSeq.hpp"
namespace Slic3r { namespace arr2 {
struct NFPPackingTag{};
struct DummyArrangeKernel
{
template<class ArrItem>
double placement_fitness(const ArrItem &itm, const Vec2crd &dest_pos) const
{
return NaNd;
}
template<class ArrItem, class Bed, class Context, class RemIt>
bool on_start_packing(ArrItem &itm,
const Bed &bed,
const Context &packing_context,
const Range<RemIt> &remaining_items)
{
return true;
}
template<class ArrItem> bool on_item_packed(ArrItem &itm) { return true; }
};
template<class Strategy> using OptAlg = typename Strategy::OptAlg;
template<class ArrangeKernel = DummyArrangeKernel,
class ExecPolicy = ExecutionSeq,
class OptMethod = opt::AlgNLoptSubplex,
class StopCond = DefaultStopCondition>
struct PackStrategyNFP {
using OptAlg = OptMethod;
ArrangeKernel kernel;
ExecPolicy ep;
double accuracy = 1.;
opt::Optimizer<OptMethod> solver;
StopCond stop_condition;
PackStrategyNFP(opt::Optimizer<OptMethod> slv,
ArrangeKernel k = {},
ExecPolicy execpolicy = {},
double accur = 1.,
StopCond stop_cond = {})
: kernel{std::move(k)},
ep{std::move(execpolicy)},
accuracy{accur},
solver{std::move(slv)},
stop_condition{std::move(stop_cond)}
{}
PackStrategyNFP(ArrangeKernel k = {},
ExecPolicy execpolicy = {},
double accur = 1.,
StopCond stop_cond = {})
: PackStrategyNFP{opt::Optimizer<OptMethod>{}, std::move(k),
std::move(execpolicy), accur, std::move(stop_cond)}
{
// Defaults for AlgNLoptSubplex
auto iters = static_cast<unsigned>(std::floor(1000 * accuracy));
auto optparams =
opt::StopCriteria{}.max_iterations(iters).rel_score_diff(
1e-20) /*.abs_score_diff(1e-20)*/;
solver.set_criteria(optparams);
}
};
template<class...Args>
struct PackStrategyTag_<PackStrategyNFP<Args...>>
{
using Tag = NFPPackingTag;
};
template<class ArrItem, class Bed, class PStrategy>
double pick_best_spot_on_nfp_verts_only(ArrItem &item,
const ExPolygons &nfp,
const Bed &bed,
const PStrategy &strategy)
{
using KernelT = KernelTraits<decltype(strategy.kernel)>;
auto score = -std::numeric_limits<double>::infinity();
Vec2crd orig_tr = get_translation(item);
Vec2crd translation{0, 0};
auto eval_fitness = [&score, &strategy, &item, &translation,
&orig_tr](const Vec2crd &p) {
set_translation(item, orig_tr);
Vec2crd ref_v = reference_vertex(item);
Vec2crd tr = p - ref_v;
double fitness = KernelT::placement_fitness(strategy.kernel, item, tr);
if (fitness > score) {
score = fitness;
translation = tr;
}
};
for (const ExPolygon &expoly : nfp) {
for (const Point &p : expoly.contour) {
eval_fitness(p);
}
for (const Polygon &h : expoly.holes)
for (const Point &p : h.points)
eval_fitness(p);
}
set_translation(item, orig_tr + translation);
return score;
}
struct CornerResult
{
size_t contour_id;
opt::Result<1> oresult;
};
template<class ArrItem, class Bed, class... Args>
double pick_best_spot_on_nfp(ArrItem &item,
const ExPolygons &nfp,
const Bed &bed,
const PackStrategyNFP<Args...> &strategy)
{
auto &ex_policy = strategy.ep;
using KernelT = KernelTraits<decltype(strategy.kernel)>;
auto score = -std::numeric_limits<double>::infinity();
Vec2crd orig_tr = get_translation(item);
Vec2crd translation{0, 0};
Vec2crd ref_v = reference_vertex(item);
auto edge_caches = reserve_vector<EdgeCache>(nfp.size());
auto sample_sets = reserve_vector<std::vector<ContourLocation>>(
nfp.size());
for (const ExPolygon &expoly : nfp) {
edge_caches.emplace_back(EdgeCache{&expoly});
edge_caches.back().sample_contour(strategy.accuracy,
sample_sets.emplace_back());
}
auto nthreads = execution::max_concurrency(ex_policy);
std::vector<CornerResult> gresults(edge_caches.size());
auto resultcmp = [](auto &a, auto &b) {
return a.oresult.score < b.oresult.score;
};
execution::for_each(
ex_policy, size_t(0), edge_caches.size(),
[&](size_t edge_cache_idx) {
auto &ec_contour = edge_caches[edge_cache_idx];
auto &corners = sample_sets[edge_cache_idx];
std::vector<CornerResult> results(corners.size());
auto cornerfn = [&](size_t i) {
ContourLocation cr = corners[i];
auto objfn = [&](opt::Input<1> &in) {
Vec2crd p = ec_contour.coords(ContourLocation{cr.contour_id, in[0]});
Vec2crd tr = p - ref_v;
return KernelT::placement_fitness(strategy.kernel, item, tr);
};
// Assuming that solver is a lightweight object
auto solver = strategy.solver;
solver.to_max();
auto oresult = solver.optimize(objfn,
opt::initvals({cr.dist}),
opt::bounds({{0., 1.}}));
results[i] = CornerResult{cr.contour_id, oresult};
};
execution::for_each(ex_policy, size_t(0), results.size(),
cornerfn, nthreads);
auto it = std::max_element(results.begin(), results.end(),
resultcmp);
if (it != results.end())
gresults[edge_cache_idx] = *it;
},
nthreads);
auto it = std::max_element(gresults.begin(), gresults.end(), resultcmp);
if (it != gresults.end()) {
score = it->oresult.score;
size_t path_id = std::distance(gresults.begin(), it);
size_t contour_id = it->contour_id;
double dist = it->oresult.optimum[0];
Vec2crd pos = edge_caches[path_id].coords(ContourLocation{contour_id, dist});
Vec2crd tr = pos - ref_v;
set_translation(item, orig_tr + tr);
}
return score;
}
template<class Strategy, class ArrItem, class Bed, class RemIt>
bool pack(Strategy &strategy,
const Bed &bed,
ArrItem &item,
const PackStrategyContext<Strategy, ArrItem> &packing_context,
const Range<RemIt> &remaining_items,
const NFPPackingTag &)
{
using KernelT = KernelTraits<decltype(strategy.kernel)>;
// The kernel might pack the item immediately
bool packed = KernelT::on_start_packing(strategy.kernel, item, bed,
packing_context, remaining_items);
double orig_rot = get_rotation(item);
double final_rot = 0.;
double final_score = -std::numeric_limits<double>::infinity();
Vec2crd orig_tr = get_translation(item);
Vec2crd final_tr = orig_tr;
bool cancelled = strategy.stop_condition();
const auto & rotations = allowed_rotations(item);
// Check all rotations but only if item is not already packed
for (auto rot_it = rotations.begin();
!cancelled && !packed && rot_it != rotations.end(); ++rot_it) {
double rot = *rot_it;
set_rotation(item, orig_rot + rot);
set_translation(item, orig_tr);
auto nfp = calculate_nfp(item, packing_context, bed,
strategy.stop_condition);
double score = NaNd;
if (!nfp.empty()) {
score = pick_best_spot_on_nfp(item, nfp, bed, strategy);
cancelled = strategy.stop_condition();
if (score > final_score) {
final_score = score;
final_rot = rot;
final_tr = get_translation(item);
}
}
}
// If the score is not valid, and the item is not already packed, or
// the packing was cancelled asynchronously by stop condition, then
// discard the packing
bool is_score_valid = !std::isnan(final_score) && !std::isinf(final_score);
packed = !cancelled && (packed || is_score_valid);
if (packed) {
set_translation(item, final_tr);
set_rotation(item, orig_rot + final_rot);
// Finally, consult the kernel if the packing is sane
packed = KernelT::on_item_packed(strategy.kernel, item);
}
return packed;
}
}} // namespace Slic3r::arr2
#endif // PACKSTRATEGYNFP_HPP

View File

@@ -0,0 +1,142 @@
#ifndef RECTANGLEOVERFITPACKINGSTRATEGY_HPP
#define RECTANGLEOVERFITPACKINGSTRATEGY_HPP
#include "Kernels/RectangleOverfitKernelWrapper.hpp"
#include "libslic3r/Arrange/Core/NFP/PackStrategyNFP.hpp"
#include "libslic3r/Arrange/Core/Beds.hpp"
namespace Slic3r { namespace arr2 {
using PostAlignmentFn = std::function<Vec2crd(const BoundingBox &bedbb,
const BoundingBox &pilebb)>;
struct CenterAlignmentFn {
Vec2crd operator() (const BoundingBox &bedbb,
const BoundingBox &pilebb)
{
return bedbb.center() - pilebb.center();
}
};
template<class ArrItem>
struct RectangleOverfitPackingContext : public DefaultPackingContext<ArrItem>
{
BoundingBox limits;
int bed_index;
PostAlignmentFn post_alignment_fn;
explicit RectangleOverfitPackingContext(const BoundingBox limits,
int bedidx,
PostAlignmentFn alignfn = CenterAlignmentFn{})
: limits{limits}, bed_index{bedidx}, post_alignment_fn{alignfn}
{}
void align_pile()
{
// Here, the post alignment can be safely done. No throwing
// functions are called!
if (fixed_items_range(*this).empty()) {
auto itms = packed_items_range(*this);
auto pilebb = bounding_box(itms);
for (auto &itm : itms) {
translate(itm, post_alignment_fn(limits, pilebb));
}
}
}
~RectangleOverfitPackingContext() { align_pile(); }
};
// With rectange bed, and no fixed items, an infinite bed with
// RectangleOverfitKernelWrapper can produce better results than a pure
// RectangleBed with inner-fit polygon calculation.
template<class ...Args>
struct RectangleOverfitPackingStrategy {
PackStrategyNFP<Args...> base_strategy;
PostAlignmentFn post_alignment_fn = CenterAlignmentFn{};
template<class ArrItem>
using Context = RectangleOverfitPackingContext<ArrItem>;
RectangleOverfitPackingStrategy(PackStrategyNFP<Args...> s,
PostAlignmentFn post_align_fn)
: base_strategy{std::move(s)}, post_alignment_fn{post_align_fn}
{}
RectangleOverfitPackingStrategy(PackStrategyNFP<Args...> s)
: base_strategy{std::move(s)}
{}
};
struct RectangleOverfitPackingStrategyTag {};
template<class... Args>
struct PackStrategyTag_<RectangleOverfitPackingStrategy<Args...>> {
using Tag = RectangleOverfitPackingStrategyTag;
};
template<class... Args>
struct PackStrategyTraits_<RectangleOverfitPackingStrategy<Args...>> {
template<class ArrItem>
using Context = typename RectangleOverfitPackingStrategy<
Args...>::template Context<StripCVRef<ArrItem>>;
template<class ArrItem, class Bed>
static Context<ArrItem> create_context(
RectangleOverfitPackingStrategy<Args...> &ps,
const Bed &bed,
int bed_index)
{
return Context<ArrItem>{bounding_box(bed), bed_index,
ps.post_alignment_fn};
}
};
template<class ArrItem>
struct PackingContextTraits_<RectangleOverfitPackingContext<ArrItem>>
: public PackingContextTraits_<DefaultPackingContext<ArrItem>>
{
static void add_packed_item(RectangleOverfitPackingContext<ArrItem> &ctx, ArrItem &itm)
{
ctx.add_packed_item(itm);
// to prevent coords going out of range
ctx.align_pile();
}
};
template<class Strategy, class ArrItem, class Bed, class RemIt>
bool pack(Strategy &strategy,
const Bed &bed,
ArrItem &item,
const PackStrategyContext<Strategy, ArrItem> &packing_context,
const Range<RemIt> &remaining_items,
const RectangleOverfitPackingStrategyTag &)
{
bool ret = false;
if (fixed_items_range(packing_context).empty()) {
auto &base = strategy.base_strategy;
PackStrategyNFP modded_strategy{
base.solver,
RectangleOverfitKernelWrapper{base.kernel, packing_context.limits},
base.ep, base.accuracy};
ret = pack(modded_strategy,
InfiniteBed{packing_context.limits.center()}, item,
packing_context, remaining_items, NFPPackingTag{});
} else {
ret = pack(strategy.base_strategy, bed, item, packing_context,
remaining_items, NFPPackingTag{});
}
return ret;
}
}} // namespace Slic3r::arr2
#endif // RECTANGLEOVERFITPACKINGSTRATEGY_HPP

Some files were not shown because too many files have changed in this diff Show More