Merge branch 'et_world_coordinates' into fs_emboss

# Conflicts:
#	src/libslic3r/CMakeLists.txt
#	src/libslic3r/Format/3mf.cpp
#	src/libslic3r/Model.hpp
#	src/libslic3r/Technologies.hpp
#	src/slic3r/GUI/GLCanvas3D.cpp
#	src/slic3r/GUI/GUI_App.cpp
#	src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
#	src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
#	src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
#	src/slic3r/GUI/Selection.cpp
#	src/slic3r/GUI/Selection.hpp
This commit is contained in:
Filip Sykala - NTB T15p 2022-07-20 13:02:07 +02:00
commit 79a7f588b9
300 changed files with 80473 additions and 59230 deletions

View File

@ -41,8 +41,8 @@
# hypertext_type = gallery
#
#Open top menubar item
#hypertext_menubar_menu_name = (Name in english visible as menu name: File, )
#hypertext_menubar_item_name = (Name of item in english, if there are three dots at the end of name, put name without three dots)
#hypertext_menubar_menu_name = (Exact Name in english visible as menu name: File, ) Note: If it contains "&", you have to leave it
#hypertext_menubar_item_name = (Exact Name of item in english, if there are three dots at the end of name, put name without three dots) Note: If it contains "&", you have to leave it
#
#
# Each notification can have disabled and enabled modes and techs - divided by ; and space
@ -200,8 +200,8 @@ disabled_tags = SLA
text = Configuration snapshots\nDid you know that you can roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - <a>Configuration snapshots menu</a>.
documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776
hypertext_type = menubar
hypertext_menubar_menu_name = Configuration
hypertext_menubar_item_name = Configuration Snapshots
hypertext_menubar_menu_name = &Configuration
hypertext_menubar_item_name = &Configuration Snapshots
[hint:Minimum shell thickness]
text = Minimum shell thickness\nDid you know that instead of the number of top and bottom layers, you can define the<a>Minimum shell thickness</a>in millimeters? This feature is especially useful when using the variable layer height function.
@ -222,6 +222,11 @@ text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Su
documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130
disabled_tags = SLA
[hint:Lightning infill]
text = Lightning infill\nDid you know that you can use the Lightning infill to support only the top surfaces, save a lot of the filament, and decrease the print time? Read more in the documentation.
documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130
disabled_tags = SLA
[hint:Fullscreen mode]
text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the <b>F11</b> hotkey.
enabled_tags = Windows

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 311 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
min_slic3r_version = 2.5.0-alpha0
0.1.5 Added Ender-3 S1 Pro
min_slic3r_version = 2.4.1
0.1.4 Added Ender-3 Pro. Added M25 support for some printers.
min_slic3r_version = 2.4.0-rc

View File

@ -5,7 +5,7 @@
name = Creality
# 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.
config_version = 0.1.4
config_version = 0.1.5
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -21,7 +21,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3BLTOUCH]
name = Creality Ender-3 BLTouch
@ -30,7 +30,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3PRO]
name = Creality Ender-3 Pro
@ -39,7 +39,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3V2]
name = Creality Ender-3 V2
@ -48,7 +48,7 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3S1]
name = Creality Ender-3 S1
@ -57,7 +57,16 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3S1PRO]
name = Creality Ender-3 S1 Pro
variants = 0.4
technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER3MAX]
name = Creality Ender-3 Max
@ -66,7 +75,7 @@ technology = FFF
family = ENDER
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER4]
name = Creality Ender-4
@ -75,7 +84,7 @@ technology = FFF
family = ENDER
bed_model = ender3v2_bed.stl
bed_texture = ender3v2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER5]
name = Creality Ender-5
@ -84,7 +93,7 @@ technology = FFF
family = ENDER
bed_model = ender3_bed.stl
bed_texture = ender3.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER5PLUS]
name = Creality Ender-5 Plus
@ -93,7 +102,7 @@ technology = FFF
family = ENDER
bed_model = ender5plus_bed.stl
bed_texture = ender5plus.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER6]
name = Creality Ender-6
@ -102,7 +111,7 @@ technology = FFF
family = ENDER
bed_model = ender6_bed.stl
bed_texture = ender6.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER7]
name = Creality Ender-7
@ -111,7 +120,7 @@ technology = FFF
family = ENDER
bed_model = ender7_bed.stl
bed_texture = ender7.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER2]
name = Creality Ender-2
@ -120,7 +129,7 @@ technology = FFF
family = ENDER
bed_model = ender2_bed.stl
bed_texture = ender2.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:ENDER2PRO]
name = Creality Ender-2 Pro
@ -129,7 +138,7 @@ technology = FFF
family = ENDER
bed_model = ender2pro_bed.stl
bed_texture = ender2pro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR5PRO]
name = Creality CR-5 Pro
@ -138,7 +147,7 @@ technology = FFF
family = CR
bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR5PROH]
name = Creality CR-5 Pro H
@ -147,7 +156,7 @@ technology = FFF
family = CR
bed_model = cr5pro_bed.stl
bed_texture = cr5pro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR6SE]
name = Creality CR-6 SE
@ -156,7 +165,7 @@ technology = FFF
family = CR
bed_model = cr6se_bed.stl
bed_texture = cr6se.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR6MAX]
name = Creality CR-6 Max
@ -165,7 +174,7 @@ technology = FFF
family = CR
bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SMART]
name = Creality CR-10 SMART
@ -174,7 +183,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10MINI]
name = Creality CR-10 Mini
@ -183,7 +192,7 @@ technology = FFF
family = CR
bed_model = cr10mini_bed.stl
bed_texture = cr10mini.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10MAX]
name = Creality CR-10 Max
@ -192,7 +201,7 @@ technology = FFF
family = CR
bed_model = cr10max_bed.stl
bed_texture = cr10max.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10]
name = Creality CR-10
@ -201,7 +210,7 @@ technology = FFF
family = CR
bed_model = cr10_bed.stl
bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10V2]
name = Creality CR-10 V2
@ -210,7 +219,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10V3]
name = Creality CR-10 V3
@ -219,7 +228,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S]
name = Creality CR-10 S
@ -228,7 +237,7 @@ technology = FFF
family = CR
bed_model = cr10_bed.stl
bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SPRO]
name = Creality CR-10 S Pro
@ -237,7 +246,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10spro.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10SPROV2]
name = Creality CR-10 S Pro V2
@ -246,7 +255,7 @@ technology = FFF
family = CR
bed_model = cr10v2_bed.stl
bed_texture = cr10.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S4]
name = Creality CR-10 S4
@ -255,7 +264,7 @@ technology = FFF
family = CR
bed_model = cr10s4_bed.stl
bed_texture = cr10s4.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR10S5]
name = Creality CR-10 S5
@ -264,7 +273,7 @@ technology = FFF
family = CR
bed_model = cr10s5_bed.stl
bed_texture = cr10s5.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR20]
name = Creality CR-20
@ -273,7 +282,7 @@ technology = FFF
family = CR
bed_model = ender3_bed.stl
bed_texture = cr20.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR20PRO]
name = Creality CR-20 Pro
@ -282,7 +291,7 @@ technology = FFF
family = CR
bed_model = ender3_bed.stl
bed_texture = cr20.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR200B]
name = Creality CR-200B
@ -291,7 +300,7 @@ technology = FFF
family = CR
bed_model = cr200b_bed.stl
bed_texture = cr200b.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:CR8]
name = Creality CR-8
@ -300,7 +309,7 @@ technology = FFF
family = CR
bed_model = cr8_bed.stl
bed_texture = cr8.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
#[printer_model:CRX]
#name = Creality CR-X
@ -309,7 +318,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X
#bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
#[printer_model:CRXPRO]
#name = Creality CR-X Pro
@ -318,7 +327,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @
#family = CR-X
#bed_model = cr10v2_bed.stl
#bed_texture = cr10spro.svg
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
[printer_model:SERMOOND1]
name = Creality Sermoon-D1
@ -327,7 +336,7 @@ technology = FFF
family = SERMOON
bed_model = sermoond1_bed.stl
bed_texture = sermoond1.svg
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY
default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@ -842,6 +851,17 @@ filament_cost = 27.44
filament_density = 1.29
filament_colour = #C7F935
[filament:Verbatim PLA @CREALITY]
inherits = *PLA*
filament_vendor = Verbatim
temperature = 205
bed_temperature = 60
first_layer_temperature = 210
first_layer_bed_temperature = 60
filament_cost = 22.99
filament_density = 1.24
filament_colour = #001ca8
# Common printer preset
[printer:*common*]
printer_technology = FFF
@ -989,6 +1009,13 @@ max_print_height = 270
printer_model = ENDER3S1
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1
[printer:Creality Ender-3 S1 Pro]
inherits = *common*; *pauseprint*; *spriteextruder*
bed_shape = 5x0,215x0,215x220,5x220
max_print_height = 270
printer_model = ENDER3S1PRO
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PRO
[printer:Creality Ender-3 Max]
inherits = *common*; *pauseprint*
retract_length = 6

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,4 +1,6 @@
min_slic3r_version = 2.3.1-beta
min_slic3r_version = 2.5.0-alpha0
0.0.4 Improve Proton X profiles, Add Proton XE-750 printer
min_slic3r_version = 2.4.1
0.0.3 Set default filament profile.
0.0.2 Improved start gcode, changed filename format
0.0.1 Initial version

View File

@ -3,7 +3,7 @@
[vendor]
# Vendor name will be shown by the Config Wizard.
name = INAT
config_version = 0.0.3
config_version = 0.0.4
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/INAT/
###
@ -24,9 +24,16 @@ technology = FFF
family = Proton
default_materials = PLA @PROTON_X
[printer_model:PROTON_XE750]
name = INAT Proton XE-750
variants = 0.4
technology = FFF
family = Proton
default_materials = PLA @PROTON_XE750
###
### QUALITY DEFINITIONS
### COMMON QUALITY DEFINITIONS
###
[print:*common*]
@ -35,14 +42,15 @@ layer_height = 0.2
first_layer_height = 0.2
perimeters = 3
spiral_vase = 0
top_solid_layers = 4
top_solid_layers = 5
bottom_solid_layers = 3
top_solid_min_thickness = 0.8
top_solid_min_thickness = 1
bottom_solid_min_thickness = 0.6
extra_perimeters = 1
ensure_vertical_shell_thickness = 1
avoid_crossing_perimeters = 0
thin_walls = 0
thick_bridges = 0
overhangs = 1
seam_position = aligned
external_perimeters_first = 0
@ -76,7 +84,9 @@ support_material_auto = 1
support_material_threshold = 0
support_material_enforce_layers = 0
raft_layers = 0
support_material_contact_distance = 0.2
support_material_style = grid
support_material_contact_distance = 0.25
support_material_bottom_contact_distance = 0.3
support_material_pattern = rectilinear
support_material_with_sheath = 0
support_material_spacing = 5
@ -92,8 +102,8 @@ perimeter_speed = 60
small_perimeter_speed = 75%
external_perimeter_speed = 50%
infill_speed = 80
solid_infill_speed = 100%
top_solid_infill_speed = 30
solid_infill_speed = 80%
top_solid_infill_speed = 20
support_material_speed = 80
support_material_interface_speed = 100%
bridge_speed = 60
@ -126,7 +136,7 @@ infill_overlap = 25%
bridge_flow_ratio = 1
slice_closing_radius = 0.049
resolution = 0
xy_size_compensation = 0
xy_size_compensation = -0.05
elefant_foot_compensation = 0.3
clip_multipart_objects = 0
#output
@ -138,47 +148,50 @@ gcode_label_objects = 0
output_filename_format = {input_filename_base}_{filament_type[0]}.gcode
[print:0.2mm Standard @PROTON_X]
[print:*common 0.2mm Standard @INAT*]
inherits = *common*
[print:0.2mm Strong @PROTON_X]
[print:*common 0.2mm Strong @INAT*]
inherits = *common*
fill_density = 50%
perimeters = 6
[print:0.2mm Advanced Material @PROTON_X]
[print:*common 0.2mm Advanced Material @INAT*]
inherits = *common*
bottom_solid_layers = 5
top_solid_layers = 6
skirts = 0
brim_width = 30
brim_width = 20
infill_speed = 60
support_material_speed = 60
travel_speed = 100
first_layer_speed = 20
elefant_foot_compensation = 0
[print:0.12mm Fine @PROTON_X]
[print:*common 0.12mm Fine @INAT*]
inherits = *common*
layer_height = 0.12
bottom_solid_layers = 7
top_solid_layers = 7
infill_every_layers = 2
perimeter_speed = 50
infill_speed = 50
[print:0.32mm Draft @PROTON_X]
[print:*common 0.32mm Draft @INAT*]
inherits = *common*
layer_height = 0.32
perimeter_speed = 80
external_perimeter_speed = 75%
infill_speed = 100
top_solid_infill_speed = 60
fill_density = 15%
support_material_style = snug
###
### PRINTER DEFINITIONS
### COMMON PRINTER DEFINITIONS
###
[printer:*common*]
[printer:*proton_x_common*]
printer_vendor = INAT s.r.o.
default_filament_profile = "PLA @PROTON_X"
#general
@ -206,14 +219,14 @@ machine_max_feedrate_z = 10,10
machine_max_feedrate_e = 100,100
machine_max_acceleration_x = 500,500
machine_max_acceleration_y = 500,500
machine_max_acceleration_z = 100,100
machine_max_acceleration_e = 2000,2000
machine_max_acceleration_z = 200,200
machine_max_acceleration_e = 8000,8000
machine_max_acceleration_extruding = 1000,1000
machine_max_acceleration_retracting = 1500,1500
machine_max_acceleration_retracting = 8000,8000
machine_max_jerk_x = 8,8
machine_max_jerk_y = 8,8
machine_max_jerk_z = 1,1
machine_max_jerk_e = 2.5,2.5
machine_max_jerk_z = 3,3
machine_max_jerk_e = 10,10
machine_min_extruding_rate = 5
#extruder 1
nozzle_diameter = 0.4
@ -233,24 +246,65 @@ wipe = 1
retract_before_wipe = 100%
[printer:Proton X Rail]
inherits = *common*
printer_model = PROTON_X_RAIL
printer_variant = 0.4
default_print_profile = 0.2mm Standard @PROTON_X
gcode_flavor = marlin
machine_max_acceleration_y = 800,800
[printer:Proton X Rod]
inherits = *common*
printer_model = PROTON_X_ROD
printer_variant = 0.4
default_print_profile = 0.2mm Standard @PROTON_X
[printer:*proton_xe750_common*]
printer_vendor = INAT s.r.o.
default_filament_profile = "PLA @PROTON_XE750"
#general
printer_technology = FFF
bed_shape = 0x0,600x0,600x500,0x500
max_print_height = 750
z_offset = 0
extruders_count = 2
gcode_flavor = marlin
silent_mode = 0
remaining_times = 1
use_relative_e_distances = 0
use_firmware_retraction = 0
use_volumetric_e = 0
variable_layer_height = 1
#gcodes
start_gcode = G28 ;Home\nG0 Z10 F1000\nG29\nG0 X0 Y0 Z30 F6000\nM84 E\nM0\nG1 Z15.0 F6000 ;Move the platform down 15mm\n
end_gcode = M400\nM104 S0\nM140 S0\nM107\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X R5\nG0 Y300 F2000\nM84\nG4 S180\nM81 S30\n
color_change_gcode = M600
#limits
machine_limits_usage = emit_to_gcode
machine_max_feedrate_x = 200,200
machine_max_feedrate_y = 200,200
machine_max_feedrate_z = 10,10
machine_max_feedrate_e = 100,100
machine_max_acceleration_x = 1500,1500
machine_max_acceleration_y = 1500,1500
machine_max_acceleration_z = 500,500
machine_max_acceleration_e = 20000,20000
machine_max_acceleration_extruding = 2000,2000
machine_max_acceleration_retracting = 8000,8000
machine_max_jerk_x = 12,12
machine_max_jerk_y = 12,12
machine_max_jerk_z = 3,3
machine_max_jerk_e = 20,20
machine_min_extruding_rate = 5
#extruder 1
nozzle_diameter = 0.4,0.4
min_layer_height = 0.05,0.05
max_layer_height = 0.33,0.33
extruder_offset = 0x0,0x0
retract_length = 1.5,1.5
retract_lift = 0.6,0.6
retract_lift_above = 0,0
retract_lift_below = 0,0
retract_speed = 45,45
deretract_speed = 0,0
retract_restart_extra = 0,0
retract_before_travel = 2,2
retract_layer_change = 0,0
wipe = 1,1
retract_before_wipe = 100%,100%
retract_length_toolchange = 37,37
extruder_colour = #33CC33;#3399FF
###
### MATERIAL DEFINITIONS
### COMMON MATERIAL DEFINITIONS
###
[filament:*common*]
@ -272,7 +326,7 @@ min_print_speed = 10
filament_soluble = 0
[filament:PLA @PROTON_X]
[filament:*common PLA @INAT*]
inherits = *common*
temperature = 210
bed_temperature = 60
@ -281,11 +335,12 @@ first_layer_bed_temperature = 60
filament_type = PLA
filament_cost = 20
filament_density = 1.25
fan_always_on = 1
min_fan_speed = 50
max_fan_speed = 100
[filament:PETG @PROTON_X]
[filament:*common PETG @INAT*]
inherits = *common*
temperature = 240
bed_temperature = 80
@ -294,10 +349,11 @@ first_layer_bed_temperature = 80
filament_type = PETG
filament_cost = 25
filament_density = 1.27
min_fan_speed = 0
fan_always_on = 1
min_fan_speed = 25
max_fan_speed = 50
[filament:ABS @PROTON_X]
[filament:*common ABS @INAT*]
inherits = *common*
temperature = 235
bed_temperature = 100
@ -309,7 +365,7 @@ filament_density = 1.01
cooling = 0
bridge_fan_speed = 0
[filament:ASA @PROTON_X]
[filament:*common ASA @INAT*]
inherits = *common*
temperature = 240
bed_temperature = 110
@ -320,7 +376,7 @@ filament_cost = 22
filament_density = 1.07
cooling = 0
[filament:TPE @PROTON_X]
[filament:*common TPE @INAT*]
inherits = *common*
temperature = 220
bed_temperature = 40
@ -334,7 +390,7 @@ max_fan_speed = 50
filament_retract_length = 0.8
filament_retract_speed = 25
[filament:HIPS @PROTON_X]
[filament:*common HIPS @INAT*]
inherits = *common*
temperature = 245
bed_temperature = 100
@ -347,7 +403,7 @@ min_fan_speed = 0
max_fan_speed = 50
filament_soluble = 1
[filament:Nylon @PROTON_X]
[filament:*common Nylon @INAT*]
inherits = *common*
temperature = 235
bed_temperature = 130
@ -359,19 +415,19 @@ filament_density = 1.01
cooling = 0
bridge_fan_speed = 0
[filament:PC @PROTON_X]
[filament:*common PC @INAT*]
inherits = *common*
temperature = 270
bed_temperature = 130
bed_temperature = 115
first_layer_temperature = 270
first_layer_bed_temperature = 130
first_layer_bed_temperature = 115
filament_type = PC
filament_cost = 65
filament_density = 1.19
cooling = 0
bridge_fan_speed = 0
[filament:CPE @PROTON_X]
[filament:*common CPE @INAT*]
inherits = *common*
temperature = 280
bed_temperature = 90
@ -383,7 +439,7 @@ filament_density = 1.27
cooling = 0
bridge_fan_speed = 0
[filament:PEEK @PROTON_X]
[filament:*common PEEK @INAT*]
inherits = *common*
temperature = 440
bed_temperature = 150
@ -395,7 +451,7 @@ filament_density = 1.3
cooling = 0
bridge_fan_speed = 0
[filament:PEI @PROTON_X]
[filament:*common PEI @INAT*]
inherits = *common*
temperature = 400
bed_temperature = 150
@ -407,7 +463,7 @@ filament_density = 1.27
cooling = 0
bridge_fan_speed = 0
[filament:Polymaker PolyMide CoPA @PROTON_X]
[filament:*common Polymaker PolyMide CoPA @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 265
@ -419,7 +475,7 @@ filament_cost = 93
filament_density = 1.12
cooling = 0
[filament:Polymaker PolyMide PA6-CF @PROTON_X]
[filament:*common Polymaker PolyMide PA6-CF @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 300
@ -431,7 +487,7 @@ filament_cost = 95
filament_density = 1.17
cooling = 0
[filament:Polymaker PolyMide PA6-GF @PROTON_X]
[filament:*common Polymaker PolyMide PA6-GF @INAT*]
inherits = *common*
filament_vendor = Polymaker
temperature = 300
@ -443,20 +499,21 @@ filament_cost = 95
filament_density = 1.2
cooling = 0
[filament:Devil Design PETG @PROTON_X]
[filament:*common Devil Design PETG @INAT*]
inherits = *common*
filament_vendor = Devil Design
temperature = 250
temperature = 245
bed_temperature = 80
first_layer_temperature = 250
first_layer_temperature = 245
first_layer_bed_temperature = 80
filament_type = PETG
filament_cost = 22
filament_density = 1.23
min_fan_speed = 0
fan_always_on = 1
min_fan_speed = 25
max_fan_speed = 50
[filament:Filament PM PETG FRJet @PROTON_X]
[filament:*common Filament PM PETG FRJet @INAT*]
inherits = *common*
filament_vendor = Filament PM
temperature = 250
@ -467,3 +524,207 @@ filament_type = PETG
filament_cost = 45.5
filament_density = 1.27
cooling = 0
######
###### PROTON X PRINTERS
######
[printer:Proton X Rail]
inherits = *proton_x_common*
printer_model = PROTON_X_RAIL
printer_variant = 0.4
default_print_profile = 0.2mm Standard @PROTON_X
gcode_flavor = marlin
machine_max_acceleration_x = 800,800
machine_max_acceleration_y = 800,800
machine_max_jerk_x = 10,10
machine_max_jerk_y = 10,10
[printer:Proton X Rod]
inherits = *proton_x_common*
printer_model = PROTON_X_ROD
printer_variant = 0.4
default_print_profile = 0.2mm Standard @PROTON_X
gcode_flavor = marlin
[print:0.2mm Standard @PROTON_X]
inherits = *common 0.2mm Standard @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[print:0.2mm Strong @PROTON_X]
inherits = *common 0.2mm Strong @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[print:0.2mm Advanced Material @PROTON_X]
inherits = *common 0.2mm Advanced Material @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[print:0.12mm Fine @PROTON_X]
inherits = *common 0.12mm Fine @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[print:0.32mm Draft @PROTON_X]
inherits = *common 0.32mm Draft @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:PLA @PROTON_X]
inherits =*common PLA @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:PETG @PROTON_X]
inherits =*common PETG @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:ABS @PROTON_X]
inherits =*common ABS @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:ASA @PROTON_X]
inherits =*common ASA @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:TPE @PROTON_X]
inherits =*common TPE @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:HIPS @PROTON_X]
inherits =*common HIPS @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Nylon @PROTON_X]
inherits =*common Nylon @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:PC @PROTON_X]
inherits =*common PC @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:CPE @PROTON_X]
inherits =*common CPE @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:PEEK @PROTON_X]
inherits =*common PEEK @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:PEI @PROTON_X]
inherits =*common PEI @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Polymaker PolyMide CoPA @PROTON_X]
inherits =*common Polymaker PolyMide CoPA @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Polymaker PolyMide PA6-CF @PROTON_X]
inherits =*common Polymaker PolyMide PA6-CF @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Polymaker PolyMide PA6-GF @PROTON_X]
inherits =*common Polymaker PolyMide PA6-GF @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Devil Design PETG @PROTON_X]
inherits =*common Devil Design PETG @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
[filament:Filament PM PETG FRJet @PROTON_X]
inherits =*common Filament PM PETG FRJet @INAT*
compatible_printers = "Proton X Rail";"Proton X Rod"
######### #########
######### Proton XE 750 #########
######### #########
[printer:Proton XE-750]
inherits = *proton_xe750_common*
printer_model = PROTON_XE750
printer_variant = 0.4
default_print_profile = 0.2mm Standard @PROTON_XE750
gcode_flavor = marlin
[print:0.2mm Standard @PROTON_XE750]
inherits = *common 0.2mm Standard @INAT*
compatible_printers = "Proton XE-750"
[print:0.2mm Strong @PROTON_XE750]
inherits = *common 0.2mm Strong @INAT*
compatible_printers = "Proton XE-750"
[print:0.2mm Advanced Material @PROTON_XE750]
inherits = *common 0.2mm Advanced Material @INAT*
compatible_printers = "Proton XE-750"
[print:0.12mm Fine @PROTON_XE750]
inherits = *common 0.12mm Fine @INAT*
compatible_printers = "Proton XE-750"
[print:0.32mm Draft @PROTON_XE750]
inherits = *common 0.32mm Draft @INAT*
compatible_printers = "Proton XE-750"
[filament:*start_end_gcode @PROTON_XE750*]
start_filament_gcode = "; Filament start gcode BEGIN\nM104 S[temperature[current_extruder]]\nG4 S20\n; Filament start gcode END\n"
end_filament_gcode = "; Filament end gcode BEGIN\nG0 X-5 Y250 F10000\nM104 S{temperature[current_extruder] - 50}\n; Filament end gcode END\n"
compatible_printers = "Proton XE-750"
[filament:PLA @PROTON_XE750]
inherits =*common PLA @INAT*; *start_end_gcode @PROTON_XE750*
[filament:PETG @PROTON_XE750]
inherits =*common PETG @INAT*; *start_end_gcode @PROTON_XE750*
[filament:ABS @PROTON_XE750]
inherits =*common ABS @INAT*; *start_end_gcode @PROTON_XE750*
[filament:ASA @PROTON_XE750]
inherits =*common ASA @INAT*; *start_end_gcode @PROTON_XE750*
[filament:TPE @PROTON_XE750]
inherits =*common TPE @INAT*; *start_end_gcode @PROTON_XE750*
[filament:HIPS @PROTON_XE750]
inherits =*common HIPS @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Nylon @PROTON_XE750]
inherits =*common Nylon @INAT*; *start_end_gcode @PROTON_XE750*
[filament:PC @PROTON_XE750]
inherits =*common PC @INAT*; *start_end_gcode @PROTON_XE750*
[filament:CPE @PROTON_XE750]
inherits =*common CPE @INAT*; *start_end_gcode @PROTON_XE750*
[filament:PEEK @PROTON_XE750]
inherits =*common PEEK @INAT*; *start_end_gcode @PROTON_XE750*
[filament:PEI @PROTON_XE750]
inherits =*common PEI @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Polymaker PolyMide CoPA @PROTON_XE750]
inherits =*common Polymaker PolyMide CoPA @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Polymaker PolyMide PA6-CF @PROTON_XE750]
inherits =*common Polymaker PolyMide PA6-CF @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Polymaker PolyMide PA6-GF @PROTON_XE750]
inherits =*common Polymaker PolyMide PA6-GF @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Devil Design PETG @PROTON_XE750]
inherits =*common Devil Design PETG @INAT*; *start_end_gcode @PROTON_XE750*
[filament:Filament PM PETG FRJet @PROTON_XE750]
inherits =*common Filament PM PETG FRJet @INAT*; *start_end_gcode @PROTON_XE750*

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,2 @@
min_slic3r_version = 2.5.0-alpha0
1.0.0 Initial Infinity3D bundle

View File

@ -0,0 +1,812 @@
# Infinity3D profiles
[vendor]
# Vendor name will be shown by the Config Wizard.
name = Infinity3D
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the Slic3r configuration to be downgraded.
config_version = 1.0.0
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Infinity3D/
# The printer models will be shown by the Configuration Wizard in this order,
[printer_model:DEV-200]
name = Infinity3D DEV-200
variants = 0.4
technology = FFF
bed_model = DEV_200_bed.stl
bed_texture = DEV_200_texture.svg
default_materials = Generic PLA @Infinity3D; Generic PETG @Infinity3D
[printer_model:DEV-350]
name = Infinity3D DEV-350
variants = 0.4
technology = FFF
bed_model = DEV_350_bed.stl
bed_texture = DEV_350_texture.svg
default_materials = Generic PLA @Infinity3D; Generic PETG @Infinity3D
[print:*common*]
avoid_crossing_perimeters = 1
avoid_crossing_perimeters_max_detour = 0
bottom_fill_pattern = monotonic
bottom_solid_layers = 4
bottom_solid_min_thickness = 0
bridge_acceleration = 0
bridge_angle = 0
bridge_flow_ratio = 1
bridge_speed = 60
brim_separation = 0
brim_type = outer_only
brim_width = 0
clip_multipart_objects = 1
complete_objects = 0
default_acceleration = 0
dont_support_bridges = 1
draft_shield = disabled
elefant_foot_compensation = 0.1
ensure_vertical_shell_thickness = 1
external_perimeter_extrusion_width = 0.45
external_perimeter_speed = 80%
external_perimeters_first = 0
extra_perimeters = 0
extruder_clearance_height = 25
extruder_clearance_radius = 75
extrusion_width = 0.45
fill_angle = 45
fill_density = 20%
fill_pattern = gyroid
first_layer_acceleration = 0
first_layer_acceleration_over_raft = 0
first_layer_extrusion_width = 0.45
first_layer_height = 0.2
first_layer_speed = 45
first_layer_speed_over_raft = 45
fuzzy_skin = none
fuzzy_skin_point_dist = 0.8
fuzzy_skin_thickness = 0.3
gap_fill_enabled = 1
gap_fill_speed = 20
gcode_comments = 0
gcode_label_objects = 0
gcode_resolution = 0.0125
gcode_substitutions =
infill_acceleration = 0
infill_anchor = 600%
infill_anchor_max = 50
infill_every_layers = 1
infill_extruder = 1
infill_extrusion_width = 0.45
infill_first = 0
infill_only_where_needed = 0
infill_overlap = 25%
infill_speed = 60
inherits =
interface_shells = 0
ironing = 0
ironing_flowrate = 15%
ironing_spacing = 0.25
ironing_speed = 30
ironing_type = top
layer_height = 0.2
max_print_speed = 100
max_volumetric_speed = 0
min_skirt_length = 4
mmu_segmented_region_max_width = 0
notes =
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
overhangs = 0
perimeter_acceleration = 0
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
perimeter_speed = 60
perimeters = 2
post_process =
print_settings_id =
raft_contact_distance = 0.1
raft_expansion = 1.5
raft_first_layer_density = 90%
raft_first_layer_expansion = 3
raft_layers = 0
resolution = 0
seam_position = nearest
single_extruder_multi_material_priming = 1
skirt_distance = 5
skirt_height = 1
skirts = 3
slice_closing_radius = 0.049
slicing_mode = regular
small_perimeter_speed = 70%
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_extrusion_width= 0.45
solid_infill_speed = 80%
spiral_vase = 0
standby_temperature_delta = -5
support_material = 0
support_material_angle = 0
support_material_auto = 1
support_material_bottom_contact_distance = 0
support_material_bottom_interface_layers = -1
support_material_buildplate_only = 0
support_material_closing_radius = 2
support_material_contact_distance = 0.15
support_material_enforce_layers = 0
support_material_extruder = 0
support_material_extrusion_width = 0.38
support_material_interface_contact_loops = 0
support_material_interface_extruder = 0
support_material_interface_layers = 2
support_material_interface_pattern = rectilinear
support_material_interface_spacing = 0.2
support_material_interface_speed = 100%
support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 60
support_material_style = grid
support_material_synchronize_layers = 0
support_material_threshold = 45
support_material_with_sheath = 0
support_material_xy_spacing = 60%
thick_bridges = 1
thin_walls = 0
threads = 8
top_fill_pattern = monotonic
top_infill_extrusion_width = 0.45
top_solid_infill_speed = 60%
top_solid_layers = 4
top_solid_min_thickness = 0
travel_speed = 70
travel_speed_z = 0
wipe_tower = 0
wipe_tower_bridging = 10
wipe_tower_brim_width = 2
wipe_tower_no_sparse_layers = 0
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
wipe_tower_x = 170
wipe_tower_y = 140
xy_size_compensation = 0
compatible_printers_condition = nozzle_diameter[0]==0.4
[print:0.06mm SUPERFINE @Infinity3D_DEV_200]
inherits = *common*
layer_height = 0.06
bottom_solid_layers = 12
top_solid_layers = 12
top_solid_min_thickness = 0.72
bottom_solid_min_thickness = 0.72
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
[print:0.10mm Fine @Infinity3D_DEV_200]
inherits = *common*
layer_height = 0.10
top_solid_layers = 8
bottom_solid_layers = 8
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
[print:0.20mm GOOD @Infinity3D_DEV_200]
inherits = *common*
layer_height = 0.20
top_solid_layers = 5
bottom_solid_layers = 5
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
[print:0.30mm RAPID @Infinity3D_DEV_200]
inherits = *common*
layer_height = 0.30
top_solid_layers = 3
bottom_solid_layers = 3
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
[print:0.40mm FAST @Infinity3D_DEV_200]
inherits = *common*
layer_height = 0.40
top_solid_layers = 3
bottom_solid_layers = 3
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
support_material_extrusion_width = 0.45
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-200"
[print:0.06mm SUPERFINE @Infinity3D_DEV_350]
inherits = *common*
layer_height = 0.06
bottom_solid_layers = 12
top_solid_layers = 12
top_solid_min_thickness = 0.72
bottom_solid_min_thickness = 0.72
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
[print:0.10mm Fine @Infinity3D_DEV_350]
inherits = *common*
layer_height = 0.10
top_solid_layers = 8
bottom_solid_layers = 8
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
[print:0.20mm GOOD @Infinity3D_DEV_350]
inherits = *common*
layer_height = 0.20
top_solid_layers = 5
bottom_solid_layers = 5
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
first_layer_extrusion_width = 0.45
perimeter_extrusion_width = 0.4
external_perimeter_extrusion_width = 0.4
infill_extrusion_width = 0.4
solid_infill_extrusion_width = 0.4
top_infill_extrusion_width = 0.4
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
[print:0.30mm RAPID @Infinity3D_DEV_350]
inherits = *common*
layer_height = 0.30
top_solid_layers = 3
bottom_solid_layers = 3
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
[print:0.40mm FAST @Infinity3D_DEV_350]
inherits = *common*
layer_height = 0.40
top_solid_layers = 3
bottom_solid_layers = 3
bridge_speed = 45
infill_speed = 60
perimeter_speed = 50
support_material_speed = 50
max_print_speed = 60
skirt_distance = 10
first_layer_speed = 80%
support_material_extrusion_width = 0.45
compatible_printers_condition = nozzle_diameter[0]==0.4 and printer_model=="DEV-350"
[filament:*common*]
bed_temperature = 60
bridge_fan_speed = 100
compatible_printers =
compatible_printers_condition =
compatible_prints =
compatible_prints_condition =
cooling = 1
disable_fan_first_layers = 2
end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
extrusion_multiplier = 1
fan_always_on = 1
fan_below_layer_time = 60
filament_colour = #29B2B2
filament_cooling_final_speed = 3.4
filament_cooling_initial_speed = 2.2
filament_cooling_moves = 4
filament_cost = 0
filament_density = 0
filament_deretract_speed = nil
filament_diameter = 1.75
filament_load_time = 0
filament_loading_speed = 28
filament_loading_speed_start = 3
filament_max_volumetric_speed = 0
filament_minimal_purge_on_wipe_tower = 15
filament_notes = ""
filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_retract_before_travel = nil
filament_retract_before_wipe = nil
filament_retract_layer_change = nil
filament_retract_length = nil
filament_retract_lift = nil
filament_retract_lift_above = nil
filament_retract_lift_below = nil
filament_retract_restart_extra = nil
filament_retract_speed = nil
filament_settings_id = ""
filament_soluble = 0
filament_spool_weight = 0
filament_toolchange_delay = 0
filament_type = PLA
filament_unload_time = 0
filament_unloading_speed = 90
filament_unloading_speed_start = 100
filament_wipe = nil
first_layer_bed_temperature = 60
first_layer_temperature = 210
full_fan_speed_layer = 0
inherits =
max_fan_speed = 100
min_fan_speed = 35
min_print_speed = 10
slowdown_below_layer_time = 5
start_filament_gcode = "; Filament gcode\n"
temperature = 210
[filament:*PLA*]
inherits = *common*
bed_temperature = 60
fan_below_layer_time = 100
filament_colour = #DDDDDD
filament_max_volumetric_speed = 15
filament_type = PLA
filament_density = 1.24
filament_cost = 20
first_layer_bed_temperature = 60
first_layer_temperature = 210
fan_always_on = 1
cooling = 1
max_fan_speed = 100
min_fan_speed = 100
bridge_fan_speed = 100
disable_fan_first_layers = 1
temperature = 205
[filament:*PET*]
inherits = *common*
bed_temperature = 70
cooling = 1
disable_fan_first_layers = 3
fan_below_layer_time = 20
filament_colour = #DDDDDD
filament_max_volumetric_speed = 8
filament_type = PETG
filament_density = 1.27
filament_cost = 30
first_layer_bed_temperature = 70
first_layer_temperature = 240
fan_always_on = 1
max_fan_speed = 50
min_fan_speed = 20
bridge_fan_speed = 100
temperature = 240
[filament:*ABS*]
inherits = *common*
bed_temperature = 100
cooling = 0
disable_fan_first_layers = 3
fan_below_layer_time = 20
filament_colour = #DDDDDD
filament_max_volumetric_speed = 11
filament_type = ABS
filament_density = 1.04
filament_cost = 20
first_layer_bed_temperature = 100
first_layer_temperature = 245
fan_always_on = 0
max_fan_speed = 0
min_fan_speed = 0
bridge_fan_speed = 30
top_fan_speed = 0
temperature = 245
[filament:Generic PLA @Infinity3D]
inherits = *PLA*
renamed_from = "Generic PLA @Infinity3D"
filament_vendor = Generic
[filament:Generic PETG @Infinity3D]
inherits = *PET*
renamed_from = "Generic PETG @Infinity3D"
filament_vendor = Generic
[filament:Generic ABS @Infinity3D]
inherits = *ABS*
renamed_from = "Generic ABS @Infinity3D"
first_layer_bed_temperature = 90
bed_temperature = 90
filament_vendor = Generic
[filament:Infinity3D PLA @Infinity3D]
inherits = *PLA*
renamed_from = "Infinity3D PLA @Infinity3D"
filament_vendor = Infinity3D
temperature = 200
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_colour = #42BDD8
[filament:Infinity3D PETG @Infinity3D]
inherits = *PET*
renamed_from = "Infinity3D PETG @Infinity3D"
filament_vendor = Infinity3D
temperature = 240
bed_temperature = 70
first_layer_temperature = 240
first_layer_bed_temperature = 70
max_fan_speed = 40
min_fan_speed = 20
filament_colour = #42BDD8
[filament:Infinity3D ABS @Infinity3D]
inherits = *ABS*
renamed_from = "Infinity3D ABS @Infinity3D"
filament_vendor = Infinity3D
temperature = 240
bed_temperature = 90
first_layer_temperature = 240
first_layer_bed_temperature = 90
filament_colour = #42BDD8
[filament:Prusament PLA @Infinity3D]
inherits = *PLA*
renamed_from = "Prusament PLA @Infinity3D"
filament_vendor = Prusa Polymers
temperature = 210
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 24.99
filament_density = 1.24
filament_colour = #F94D0C
[filament:Prusament PETG @Infinity3D]
inherits = *PET*
renamed_from = "Prusament PETG @Infinity3D"
filament_vendor = Prusa Polymers
temperature = 245
bed_temperature = 70
first_layer_temperature = 245
first_layer_bed_temperature = 70
filament_cost = 24.99
filament_density = 1.27
filament_colour = #F94D0C
[filament:AzureFilm PLA @Infinity3D]
inherits = *PLA*
filament_vendor = AzureFilm
temperature = 210
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 19.97
filament_density = 1.24
filament_colour = #006AA6
[filament:Devil Design PLA @Infinity3D]
inherits = *PLA*
filament_vendor = Devil Design
temperature = 215
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 19.00
filament_density = 1.24
filament_colour = #FF0000
filament_spool_weight = 256
[filament:Devil Design PLA Matt @Infinity3D]
inherits = *PLA*
filament_vendor = Devil Design
temperature = 205
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 20.00
filament_density = 1.38
filament_colour = #FF0000
filament_spool_weight = 256
[filament:Devil Design PLA Galaxy @Infinity3D]
inherits = *PLA*
renamed_from = "Devil Design PLA (Galaxy) @Infinity3D"
filament_vendor = Devil Design
temperature = 225
bed_temperature = 65
first_layer_temperature = 225
first_layer_bed_temperature = 65
filament_cost = 19.00
filament_density = 1.24
filament_colour = #FF0000
filament_spool_weight = 256
[filament:Extrudr PLA NX2 @Infinity3D]
inherits = *PLA*
filament_vendor = Extrudr
temperature = 200
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 23.63
filament_density = 1.3
filament_colour = #3C4547
filament_spool_weight = 256
[filament:Extrudr GreenTEC Pro @Infinity3D]
inherits = *PLA*
filament_vendor = Extrudr
temperature = 210
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 56.24
filament_density = 1.35
filament_colour = #3C4547
[filament:Real Filament PLA @Infinity3D]
inherits = *PLA*
filament_vendor = Real Filament
temperature = 195
bed_temperature = 60
first_layer_temperature = 200
first_layer_bed_temperature = 60
filament_cost = 24.99
filament_density = 1.24
filament_colour = #007ABF
[filament:Velleman PLA @Infinity3D]
inherits = *PLA*
filament_vendor = Velleman
temperature = 200
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 27.99
filament_density = 1.24
filament_colour = #7EA60D
[filament:3DJAKE ecoPLA @Infinity3D]
inherits = *PLA*
filament_vendor = 3DJAKE
temperature = 200
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 21.99
filament_density = 1.24
filament_colour = #125467
filament_spool_weight = 238
[filament:3DJAKE ecoPLA Matt @Infinity3D]
inherits = *PLA*
filament_vendor = 3DJAKE
temperature = 195
bed_temperature = 60
first_layer_temperature = 195
first_layer_bed_temperature = 60
filament_cost = 24.99
filament_density = 1.38
filament_colour = #125467
filament_spool_weight = 238
[filament:3DJAKE ecoPLA Tough @Infinity3D]
inherits = *PLA*
filament_vendor = 3DJAKE
temperature = 215
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 29.99
filament_density = 1.21
filament_colour = #125467
[filament:FormFutura Tough PLA @Infinity3D]
inherits = *PLA*
filament_vendor = FormFutura
temperature = 215
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 46.65
filament_density = 1.21
filament_colour = #ed000e
[filament:123-3D Jupiter PLA @Infinity3D]
inherits = *PLA*
filament_vendor = 123-3D
temperature = 200
bed_temperature = 60
first_layer_temperature = 205
first_layer_bed_temperature = 60
filament_cost = 19.50
filament_density = 1.24
filament_colour = #FFE200
[filament:Das Filament PLA @Infinity3D]
inherits = *PLA*
filament_vendor = Das Filament
temperature = 210
bed_temperature = 60
first_layer_temperature = 215
first_layer_bed_temperature = 60
filament_cost = 20.56
filament_density = 1.24
filament_colour = #C7F935
[filament:Das Filament PETG @Infinity3D]
inherits = *PET*
filament_vendor = Das Filament
temperature = 240
bed_temperature = 70
first_layer_temperature = 240
first_layer_bed_temperature = 70
filament_cost = 27.44
filament_density = 1.29
filament_colour = #C7F935
[filament:Verbatim PLA @Infinity3D]
inherits = *PLA*
filament_vendor = Verbatim
temperature = 205
bed_temperature = 60
first_layer_temperature = 210
first_layer_bed_temperature = 60
filament_cost = 22.99
filament_density = 1.24
filament_colour = #001ca8
# Common printer preset
[printer:*common*]
bed_shape = 0x0,350x0,350x350,0x350
color_change_gcode = M600
cooling_tube_length = 5
cooling_tube_retraction = 91.5
default_filament_profile = ""
default_print_profile =
end_gcode = ;End GCode begin\nM140 S0 ;Heated bed heater off\nM104 S0 ;Extruder heater off\nG90 ;absolute positioning\nG92 E0 ;Retract the filament\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z350 E-1 F3000 ;move Z up a bit and retract filament even more\nG1 X0 F3000 ;move X to min endstops, so the head is out of the way\nG1 Y350 F3000 ;so the head is out of the way and Plate is moved forward\nM84 ;stepper off\nM107 ; fan off\nM82 ; absolute extrusion\n;End GCode end
extra_loading_move = -2
extruder_colour = ""
extruder_offset = 0x0
gcode_flavor = marlin
high_current_on_filament_swap = 0
machine_limits_usage = time_estimate_only
machine_max_acceleration_e = 10000
machine_max_acceleration_extruding = 1000
machine_max_acceleration_retracting = 1000
machine_max_acceleration_travel = 1500
machine_max_acceleration_x = 3000
machine_max_acceleration_y = 3000
machine_max_acceleration_z = 100
machine_max_feedrate_e = 25
machine_max_feedrate_x = 150
machine_max_feedrate_y = 150
machine_max_feedrate_z = 50
machine_max_jerk_e = 2.5
machine_max_jerk_x = 10
machine_max_jerk_y = 10
machine_max_jerk_z = 0.2
machine_min_extruding_rate = 0
machine_min_travel_rate = 0
max_layer_height = 0.4
max_print_height = 350
min_layer_height = 0.08
nozzle_diameter = 0.4
parking_pos_retraction = 92
pause_print_gcode =
printer_technology = FFF
remaining_times = 0
retract_before_travel = 2
retract_before_wipe = 0%
retract_layer_change = 1
retract_length = 2
retract_length_toolchange = 10
retract_lift = 0
retract_lift_above = 0
retract_lift_below = 328
retract_restart_extra = 0
retract_restart_extra_toolchange = 0
retract_speed = 60
deretract_speed = 40
silent_mode = 0
single_extruder_multi_material = 0
start_gcode = Start GCode begin\nM140 S[first_layer_bed_temperature] ;Start Warming Bed\nM104 S[first_layer_temperature] ;Preheat\nG28 ;home\nG29 ;Auto Bed-level\nG90 ;absolute positioning\nG1 X-10 Y-10 F3000 ;Move to corner\nM190 S[first_layer_bed_temperature] ;Wait For Bed Temperature\nM109 S[first_layer_temperature] ;Wait for Hotend Temperature\nG92 E0 ;Zero set extruder position\nG1 E3 F200 ;Feed filament to clear nozzle\nG92 E0 ;Zero set extruder position
thumbnails = 16x16,220x124
use_firmware_retraction = 0
use_relative_e_distances = 0
use_volumetric_e = 0
variable_layer_height = 1
wipe = 0
z_offset = 0
[printer:Infinity3D DEV-350]
inherits = *common*
printer_model = DEV-350
printer_variant = 0.4
default_filament_profile = Generic PLA @Infinity3D
default_print_profile = 0.20mm GOOD @Infinity3D_DEV_350
[printer:Infinity3D DEV-200]
inherits = *common*
printer_model = DEV-200
printer_variant = 0.4
bed_shape = 0x0,200x0,200x200,0x200
thumbnails =
variable_layer_height = 0
retract_lift_below = 0
max_print_height = 235
start_gcode = Start GCode begin\nM140 S[first_layer_bed_temperature] ;Start Warming Bed\nM104 S[first_layer_temperature] ;Preheat\nG28 ;home\nG29 ;Auto Bed-level\nG90 ;absolute positioning\nG1 X-10 Y-10 F3000 ;Move to corner\nM190 S[first_layer_bed_temperature] ;Wait For Bed Temperature\nM109 S[first_layer_temperature] ;Wait for Hotend Temperature\nG92 E0 ;Zero set extruder position\nG1 E3 F200 ;Feed filament to clear nozzle\nG92 E0 ;Zero set extruder position
end_gcode = ;End GCode begin\nM140 S0 ;Heated bed heater off\nM104 S0 ;Extruder heater off\nG90 ;absolute positioning\nG92 E0 ;Retract the filament\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z240 E-1 F3000 ;move Z up a bit and retract filament even more\nG1 X0 F3000 ;move X to min endstops, so the head is out of the way\nG1 Y200 F3000 ;so the head is out of the way and Plate is moved forward\nM84 ;stepper off\nM107 ; fan off\nM82 ; absolute extrusion\n;End GCode end
default_filament_profile = Generic PLA @Infinity3D
default_print_profile = 0.20mm GOOD @Infinity3D_DEV_200

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

View File

@ -0,0 +1,487 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="2717.000000pt" height="2953.000000pt" viewBox="0 0 2717.000000 2953.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,2953.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1160 28950 l0 -580 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580
-20 l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -570 0 -570 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-509 0 -580 -2 -580 -15 0 -13 71 -15 580 -15
l580 0 0 -575 0 -575 -580 0 c-573 0 -580 0 -580 -20 0 -20 7 -20 580 -20
l580 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20 580 l0 580 575 0 575 0 0
-580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0
-509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -509 2
-580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -509 2 -580 15
-580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -573 0 -580 20 -580 20
0 20 7 20 580 l0 580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20
580 l0 580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20 580 l0
580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20 580 l0 580 575 0
575 0 0 -580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0
-580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0
-509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -509 2
-580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -573 0 -580 20
-580 20 0 20 7 20 580 l0 580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0
20 7 20 580 l0 580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20
580 l0 580 570 0 570 0 0 -580 c0 -573 0 -580 20 -580 20 0 20 7 20 580 l0
580 575 0 575 0 0 -580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575
0 575 0 0 -580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0
0 -580 c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580
c0 -509 2 -580 15 -580 13 0 15 71 15 580 l0 580 575 0 575 0 0 -580 c0 -573
0 -580 20 -580 20 0 20 7 20 580 l0 580 580 0 c573 0 580 0 580 20 0 20 -7 20
-580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580 20 0 20 -7 20 -580 20
l-580 0 0 575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0
575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575
580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0
c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0 c573 0
580 0 580 20 0 20 -7 20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580
20 0 20 -7 20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580 20 0 20 -7
20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580 20 0 20 -7 20 -580 20
l-580 0 0 575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0
575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575
580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0
c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0 c573 0
580 0 580 20 0 20 -7 20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580
20 0 20 -7 20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580 20 0 20 -7
20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580 20 0 20 -7 20 -580 20
l-580 0 0 575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0
575 0 575 580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575
580 0 c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0
c509 0 580 2 580 15 0 13 -71 15 -580 15 l-580 0 0 575 0 575 580 0 c573 0
580 0 580 20 0 20 -7 20 -580 20 l-580 0 0 570 0 570 580 0 c573 0 580 0 580
20 0 20 -7 20 -580 20 l-580 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20
-580 l0 -580 -575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15
-580 l0 -580 -575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15
-580 l0 -580 -575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15
-580 l0 -580 -575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15
-580 l0 -580 -575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15
-580 l0 -580 -575 0 -575 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580
l0 -580 -570 0 -570 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0
-580 -570 0 -570 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0 -580
-575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580
-575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580
-575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580
-575 0 -575 0 0 580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580
-575 0 -575 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0 -580 -570
0 -570 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0 -580 -570 0
-570 0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0 -580 -570 0 -570
0 0 580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580 l0 -580 -575 0 -575 0 0
580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580 -575 0 -575 0 0
580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580 -575 0 -575 0 0
580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580 -575 0 -575 0 0
580 c0 509 -2 580 -15 580 -13 0 -15 -71 -15 -580 l0 -580 -575 0 -575 0 0
580 c0 573 0 580 -20 580 -20 0 -20 -7 -20 -580z m1190 -1190 l0 -570 -575 0
-575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570
0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0
575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z
m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0
-570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0
-570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570
0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0
575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z
m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0
-570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0
-575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570
0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0
570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z
m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0
-570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0
-575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570
0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0
575 0 0 -570z m-23620 -1185 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1185 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570
575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570
0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570
0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m-23620 -1180 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0
0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180
0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570
570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0
-570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1190 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m-23620 -1180 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570
575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570
0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570
0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m-23620 -1185 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1185 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570
575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570
0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570
0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m-23620 -1180 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0
0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180
0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570
570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0
-570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1190 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m-23620 -1180 l0 -570
-575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0
0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570
575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -570 0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570
0 -570 0 0 570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0
570 0 570 570 0 570 0 0 -570z m1180 0 l0 -570 -570 0 -570 0 0 570 0 570 570
0 570 0 0 -570z m1190 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0
-570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0
l0 -570 -575 0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575
0 -575 0 0 570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0
570 0 570 575 0 575 0 0 -570z m1180 0 l0 -570 -575 0 -575 0 0 570 0 570 575
0 575 0 0 -570z m-23620 -1185 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m-23620 -1180 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575
575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570
0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0
575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570
0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0
-575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575
0 575 0 0 -575z m-23620 -1180 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0
0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180
0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575
-575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0
0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575
570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0
-575z m1180 0 l0 -575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1190 0
l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575
0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0
575 0 575 575 0 575 0 0 -575z m11810 -590 l0 -1165 -4705 0 c-4176 0 -4705 2
-4705 15 0 8 -9 15 -20 15 -11 0 -20 -7 -20 -15 0 -13 -71 -15 -575 -15 -504
0 -575 2 -575 15 0 8 -7 15 -15 15 -8 0 -15 -7 -15 -15 0 -13 -71 -15 -575
-15 l-575 0 0 575 c0 377 3 575 10 575 6 0 10 7 10 15 0 8 -4 15 -10 15 -7 0
-10 198 -10 575 l0 575 5890 0 5890 0 0 -1165z m-23620 -590 l0 -575 -575 0
-575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575
0 575 575 0 575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0
575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z
m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z m1180 0 l0
-575 -570 0 -570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0
-570 0 0 575 0 575 570 0 570 0 0 -575z m1180 0 l0 -575 -570 0 -570 0 0 575
0 575 570 0 570 0 0 -575z m1190 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0
575 0 0 -575z m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z
m1180 0 l0 -575 -575 0 -575 0 0 575 0 575 575 0 575 0 0 -575z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

View File

@ -0,0 +1,589 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="3780.000000pt" height="4134.000000pt" viewBox="0 0 3780.000000 4134.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,4134.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1618 40273 l-3 -808 -807 -3 c-799 -2 -808 -2 -808 -22 0 -20 9 -20
808 -22 l807 -3 0 -795 0 -795 -808 -3 -807 -2 0 -25 0 -25 810 0 810 0 -2
-797 -3 -798 -805 -2 c-800 -2 -805 -3 -808 -23 -3 -20 -1 -20 807 -20 l811 0
-2 -797 -3 -798 -807 -3 c-800 -2 -808 -2 -808 -22 0 -20 7 -20 810 -20 l810
0 0 -800 0 -800 -810 0 -810 0 0 -25 0 -25 810 0 810 0 0 -795 0 -795 -810 0
-810 0 0 -25 0 -25 810 0 810 0 -2 -797 -3 -798 -808 -3 c-798 -2 -807 -2
-807 -22 0 -20 9 -20 807 -22 l808 -3 0 -795 0 -795 -808 -3 c-805 -2 -808 -2
-805 -22 3 -20 9 -21 808 -23 l805 -2 3 -797 2 -798 -810 0 -810 0 0 -25 0
-25 810 0 810 0 -2 -797 -3 -798 -807 -3 c-799 -2 -808 -2 -808 -22 0 -20 9
-20 808 -22 l807 -3 3 -797 2 -798 -810 0 -810 0 0 -25 0 -25 810 0 810 0 0
-795 0 -795 -810 0 -810 0 0 -25 0 -25 810 0 810 0 0 -800 0 -800 -810 0
c-803 0 -810 0 -810 -20 0 -20 8 -20 808 -22 l807 -3 3 -797 2 -798 -810 0
c-803 0 -810 0 -810 -20 0 -20 8 -20 807 -22 l808 -3 3 -797 2 -798 -810 0
-810 0 0 -25 0 -25 808 -2 807 -3 0 -795 0 -795 -808 -3 c-798 -2 -807 -2
-807 -22 0 -20 9 -20 808 -22 l807 -3 3 -797 2 -798 -810 0 -810 0 0 -25 0
-25 810 0 810 0 0 -795 0 -795 -810 0 -810 0 0 -25 0 -25 810 0 810 0 0 -800
0 -800 -810 0 c-803 0 -810 0 -810 -20 0 -20 8 -20 808 -22 l807 -3 3 -797 2
-798 -810 0 -810 0 0 -25 0 -25 810 0 810 0 -2 -797 -3 -798 -808 -3 c-799 -2
-807 -2 -807 -22 0 -20 7 -20 810 -20 l810 0 -2 -797 -3 -798 -807 -3 c-799
-2 -808 -2 -808 -22 0 -20 9 -20 807 -22 l808 -3 0 -800 0 -800 -807 -3 c-709
-2 -808 -4 -808 -17 0 -13 98 -15 810 -15 l810 0 0 -800 0 -800 -810 0 -810 0
0 -25 0 -25 810 0 810 0 0 -810 0 -810 25 0 25 0 0 808 0 807 795 0 795 0 0
-807 0 -808 25 0 25 0 0 810 0 810 798 -2 797 -3 3 -807 c2 -800 2 -808 22
-808 20 0 20 7 20 810 l0 810 800 0 800 0 0 -810 0 -810 25 0 25 0 0 808 0
807 795 3 795 2 0 -810 0 -810 25 0 25 0 0 810 0 810 798 -2 797 -3 3 -807 c2
-799 2 -808 22 -808 20 0 20 9 22 808 l3 807 798 3 797 2 0 -811 c0 -808 0
-810 20 -807 20 3 21 8 23 808 l2 805 798 3 797 2 0 -810 0 -810 25 0 25 0 0
810 0 810 798 -2 797 -3 3 -807 c2 -799 2 -808 22 -808 20 0 20 9 22 807 l3
808 798 3 797 2 0 -810 0 -810 25 0 25 0 0 810 0 810 795 0 795 0 0 -810 0
-810 25 0 25 0 0 810 0 810 800 0 800 0 0 -810 c0 -803 0 -810 20 -810 20 0
20 8 22 808 l3 807 798 3 797 2 0 -810 c0 -803 0 -810 20 -810 20 0 20 8 22
807 l3 808 798 3 797 2 0 -810 0 -810 25 0 25 0 0 810 0 810 798 -2 797 -3 2
-805 c2 -799 3 -805 23 -808 20 -3 20 0 22 805 l3 808 798 3 797 2 0 -810 0
-810 25 0 25 0 0 810 0 810 795 0 795 0 0 -810 0 -810 25 0 25 0 0 810 0 810
800 0 800 0 0 -810 c0 -803 0 -810 20 -810 20 0 20 8 22 808 l3 807 798 3 797
2 0 -810 0 -810 25 0 25 0 0 810 0 810 798 -2 797 -3 3 -807 c2 -800 2 -808
22 -808 20 0 20 7 20 810 l0 810 798 -2 797 -3 3 -807 c2 -800 2 -808 22 -808
20 0 20 7 20 810 l0 810 800 0 800 0 0 -810 0 -810 25 0 25 0 0 810 0 810 811
0 810 0 -3 22 -3 23 -807 3 -808 2 0 795 0 795 808 2 807 3 3 23 3 22 -810 0
-811 0 0 800 0 800 810 0 c803 0 810 0 810 20 0 20 -7 20 -810 20 l-810 0 0
800 0 800 811 0 c808 0 810 0 807 20 -3 20 -8 21 -808 23 l-805 2 -3 797 -2
798 807 2 808 3 3 23 3 22 -810 0 -811 0 2 798 3 797 808 3 c799 2 807 2 807
22 0 20 -7 20 -810 20 l-810 0 0 800 0 800 811 0 810 0 -3 23 -3 22 -807 3
-808 2 0 795 0 795 811 0 810 0 -3 23 -3 22 -807 3 -808 2 0 800 0 800 810 0
c803 0 810 0 810 20 0 20 -8 20 -807 22 l-808 3 -3 798 -2 797 811 0 810 0 -3
23 -3 22 -808 3 -807 2 2 798 3 797 805 2 c800 2 805 3 808 23 3 20 1 20 -807
20 l-811 0 0 800 0 800 810 0 c803 0 810 0 810 20 0 20 -7 20 -810 20 l-810 0
0 800 0 800 811 0 810 0 -3 23 -3 22 -807 3 -808 2 0 795 0 795 808 2 c799 3
807 3 807 23 0 20 -8 20 -807 23 l-808 2 0 800 0 800 810 0 c803 0 810 0 810
20 0 20 -8 20 -807 22 l-808 3 -3 798 -2 797 811 0 810 0 -3 23 -3 22 -805 2
-805 3 0 797 0 798 805 2 c800 2 805 3 808 23 3 20 1 20 -807 20 l-811 0 2
798 3 797 808 3 c799 2 807 2 807 22 0 20 -7 20 -810 20 l-810 0 0 800 0 800
808 2 807 3 3 23 3 22 -810 0 -811 0 2 795 3 795 805 3 805 2 3 23 3 22 -810
0 -811 0 0 800 0 800 810 0 c803 0 810 0 810 20 0 20 -7 20 -810 20 l-810 0 0
800 0 800 811 0 c808 0 810 0 807 20 -3 20 -8 21 -808 23 l-805 2 0 798 0 797
805 3 805 2 3 23 3 22 -810 0 -811 0 2 798 3 797 808 3 c799 2 807 2 807 22 0
20 -7 20 -810 20 l-810 0 0 810 0 810 -25 0 -25 0 0 -810 0 -810 -797 2 -798
3 -3 808 c-2 798 -2 807 -22 807 -20 0 -20 -9 -22 -807 l-3 -808 -797 -3 -798
-2 0 810 c0 803 0 810 -20 810 -20 0 -20 -8 -22 -807 l-3 -808 -797 -3 -798
-2 0 810 0 810 -25 0 -25 0 0 -810 0 -810 -797 2 -798 3 -3 808 c-2 798 -2
807 -22 807 -20 0 -20 -9 -22 -807 l-3 -808 -797 -3 -798 -2 0 810 c0 803 0
810 -20 810 -20 0 -20 -8 -22 -807 l-3 -808 -800 0 -800 0 -3 808 c-2 798 -2
807 -22 807 -20 0 -20 -9 -22 -807 l-3 -808 -795 0 -795 0 -3 808 c-2 798 -2
807 -22 807 -20 0 -20 -9 -22 -807 l-3 -808 -795 0 -795 0 -3 808 -2 807 -25
0 -25 0 0 -810 0 -810 -797 2 -798 3 -3 808 c-2 799 -2 807 -22 807 -20 0 -20
-7 -20 -810 l0 -810 -797 2 -798 3 -3 808 c-2 798 -2 807 -22 807 -20 0 -20
-9 -22 -807 l-3 -808 -797 -3 -798 -2 0 810 0 810 -25 0 -25 0 0 -810 0 -810
-795 0 -795 0 0 810 0 810 -25 0 -25 0 0 -810 0 -810 -797 2 -798 3 -3 808
c-2 798 -2 807 -22 807 -20 0 -20 -9 -22 -807 l-3 -808 -795 0 -795 0 -3 808
-2 807 -25 0 -25 0 0 -810 0 -810 -797 2 -798 3 0 808 0 807 -22 0 -23 0 -2
-807 -3 -808 -795 0 -795 0 -3 808 c-2 798 -2 807 -22 807 -20 0 -20 -9 -22
-807 l-3 -808 -797 -3 -798 -2 0 810 0 810 -25 0 -25 0 0 -810 0 -810 -795 0
c-629 0 -795 3 -795 13 0 6 0 371 0 810 l0 797 -25 0 -25 0 0 -810 0 -810
-800 0 -800 0 0 810 c0 803 0 810 -20 810 -20 0 -20 -8 -22 -807 l-3 -808
-797 -3 -798 -2 0 810 0 810 -25 0 -25 0 0 -797 c0 -439 0 -804 0 -810 0 -10
-166 -13 -795 -13 -629 0 -795 3 -795 13 0 6 0 371 0 810 l0 797 -25 0 -25 0
-2 -807z m1642 -1648 c0 -434 0 -793 0 -797 0 -5 -358 -8 -795 -8 -437 0 -795
3 -795 8 0 22 0 1578 0 1585 0 4 358 6 795 5 l795 -3 0 -790z m1645 -5 l0
-795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800
0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1640 0 l0
-800 -795 0 c-523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0 3
358 5 795 5 l795 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798
-2 797 -3 0 -795z m1640 0 l0 -795 -795 0 -795 0 -3 785 c-1 432 0 791 3 798
3 10 168 12 797 10 l793 -3 0 -795z m1645 0 l0 -800 -797 2 -798 3 -3 785 c-1
432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1645 0 l0 -795 -797 -3
-798 -2 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 0 -795z m1645 0 l0
-800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0
-800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1648
-2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1640 -1 l2 -797
-797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 10 168 12 797 10 l793 -3 3 -798z
m1642 3 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12
l795 0 0 -800z m1648 -3 l2 -797 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3
10 168 12 797 10 l793 -3 3 -798z m1640 0 l2 -797 -797 2 -798 3 -3 785 c-1
432 0 791 3 798 3 10 168 12 797 10 l793 -3 3 -798z m1647 3 l0 -795 -800 0
-800 0 -3 785 c-1 432 0 791 3 798 3 10 169 12 802 10 l798 -3 0 -795z m1643
-2 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3
-797z m1642 2 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12
800 12 l795 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2
797 -3 0 -795z m1643 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3
-797z m1640 -1 l2 -797 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 10 168 12
797 10 l793 -3 3 -798z m-31218 -1642 l0 -795 -22 -5 c-13 -3 -372 -4 -798 -3
l-775 3 -3 790 c-1 435 0 795 2 800 2 7 276 9 800 7 l796 -2 0 -795z m1635 -2
l0 -798 -795 0 -795 0 -3 790 c-1 435 0 795 2 800 2 7 275 9 797 8 l794 -3 0
-797z m1645 -4 l0 -799 -789 0 c-473 0 -792 4 -796 9 -3 5 -5 364 -5 798 l0
788 787 5 c434 3 791 4 796 2 4 -2 7 -363 7 -803z m1648 0 l-3 -794 -795 0
-795 0 -3 797 -2 798 792 2 c436 2 796 0 801 -3 4 -3 6 -363 5 -800z m1637 4
l0 -798 -775 -3 c-426 -1 -785 0 -797 3 l-23 5 0 794 c0 521 3 795 10 798 5 2
364 2 797 1 l788 -3 0 -797z m1640 0 l0 -798 -784 -3 c-571 -1 -787 1 -797 9
-12 10 -14 147 -14 799 0 517 3 789 10 792 5 2 364 2 797 1 l788 -3 0 -797z
m1648 797 c4 0 7 -358 7 -795 l0 -795 -22 -5 c-13 -3 -372 -4 -798 -3 l-775 3
-3 790 c-1 435 0 795 2 800 3 8 901 11 1589 5z m1645 -798 l-3 -797 -795 0
-795 0 -3 790 c-1 435 0 795 2 800 2 7 276 9 800 7 l796 -2 -2 -798z m1640 0
l-3 -797 -792 -3 -793 -2 0 802 0 803 795 -3 795 -2 -2 -798z m1647 1 l0 -798
-797 -3 -798 -2 0 798 c0 439 2 801 4 803 2 2 361 3 798 2 l793 -3 0 -797z
m1643 -4 l-3 -794 -775 -3 c-426 -1 -785 0 -797 3 l-23 5 0 795 0 795 793 2
c435 2 795 0 800 -3 4 -3 6 -363 5 -800z m1640 0 l-3 -794 -775 -3 c-426 -1
-785 0 -797 3 l-23 5 0 795 0 795 793 2 c435 2 795 0 800 -3 4 -3 6 -363 5
-800z m3290 0 l-3 -794 -795 0 -795 0 -3 797 -2 798 792 2 c436 2 796 0 801
-3 4 -3 6 -363 5 -800z m1652 7 c0 -715 -2 -794 -16 -800 -20 -8 -1558 -8
-1578 0 -14 6 -16 85 -16 799 0 520 3 794 10 797 5 2 368 2 805 0 l795 -2 0
-794z m1640 -1 l0 -795 -22 -5 c-13 -3 -372 -4 -798 -3 l-775 3 -3 790 c-1
435 0 795 2 800 2 7 276 9 800 7 l796 -2 0 -795z m1640 -3 l0 -802 -797 2
-798 3 -3 790 c-1 435 0 795 2 800 2 7 278 10 800 10 l796 0 0 -803z m1648 21
c1 -428 0 -788 -3 -800 l-5 -23 -795 0 -795 0 0 803 0 802 797 -2 798 -3 3
-777z m1637 -20 l0 -798 -797 -3 -798 -2 0 802 0 803 797 -2 798 -3 0 -797z
m1643 -4 l-3 -794 -775 -3 c-426 -1 -785 0 -797 3 l-23 5 0 795 0 795 793 2
c435 2 795 0 800 -3 4 -3 6 -363 5 -800z m-32865 10 c-2 -435 -5 -795 -9 -800
-6 -11 -1584 -13 -1584 -1 0 4 -1 364 -3 800 l-2 792 800 0 800 0 -2 -791z
m21365 -6 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m-21368
-1635 c0 -434 0 -792 0 -798 0 -7 -272 -10 -795 -10 -523 0 -795 3 -795 10 0
6 0 363 0 795 0 432 0 787 0 790 0 3 358 4 795 3 l795 -3 0 -787z m1648 -5
l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0 -800
-797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -795 0
c-523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0 3 358 5 795 5
l795 0 0 -800z m1648 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2
-797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z
m1643 793 c2 -2 3 -362 1 -800 l-2 -796 -794 0 c-715 0 -794 2 -800 16 -8 21
-8 1569 1 1577 7 7 1587 10 1594 3z m1647 -793 l-3 -798 -795 0 -795 0 -3 798
-2 797 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797
800 0 800 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0
-800z m1648 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z
m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0
-800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1648 3 l-3 -798 -795
0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0
-3 798 -2 797 800 0 800 0 -2 -797z m1650 0 l-3 -798 -800 0 -800 0 -3 798 -2
797 805 0 805 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0
800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0
-800z m1648 20 c1 -426 0 -785 -3 -797 l-5 -23 -795 0 -795 0 0 800 0 800 798
-2 797 -3 3 -775z m1640 -17 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800
0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z
m-32870 -1640 l0 -795 -795 0 -795 0 0 793 c0 435 0 794 0 797 0 3 358 4 795
3 l795 -3 0 -795z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3
3 -797z m1642 2 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640
0 l0 -800 -795 2 -795 3 0 793 c0 435 0 794 0 797 0 3 358 5 795 5 l795 0 0
-800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z
m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793
0 0 -800z m1641 796 c2 -2 3 -362 1 -800 l-2 -796 -798 2 -797 3 -3 785 c-1
432 0 791 3 798 3 9 172 12 798 12 437 0 796 -2 798 -4z m1647 -798 l2 -798
-800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800
0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800
795 0 795 0 0 -800z m1650 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m1640 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0
l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0
-800z m1650 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0
l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1650 0 l0 -800 -805
0 -805 0 0 793 c0 437 3 797 7 800 3 4 366 7 805 7 l798 0 0 -800z m1640 0 l0
-800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -800 0
-800 0 0 800 0 800 800 0 800 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0
800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 800 0 800
800 0 800 0 0 -800z m1640 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m-31220 -1645 l0 -795 -800 0 -800 0 0 793 c0 437 2 796 4 798 2 2 362
3 800 1 l796 -2 0 -795z m1638 0 l2 -795 -800 0 -800 0 0 793 c0 437 2 796 4
798 2 2 361 3 798 2 l793 -3 3 -795z m3292 -1 l0 -794 -800 0 -800 0 0 795 0
795 793 2 c435 2 795 2 800 0 4 -2 7 -361 7 -798z m1640 0 l0 -794 -800 0
-800 0 0 795 0 795 793 2 c435 2 795 2 800 0 4 -2 7 -361 7 -798z m1638 1 l2
-795 -800 0 -800 0 0 788 c0 484 4 792 9 798 6 5 310 8 798 7 l788 -3 3 -795z
m1645 795 c4 0 7 -358 7 -795 l0 -795 -800 0 -800 0 0 793 c0 437 2 796 4 798
3 3 1205 2 1589 -1z m1647 -795 l0 -795 -800 0 -800 0 0 793 c0 437 2 796 4
798 2 2 362 3 800 1 l796 -2 0 -795z m1640 0 l0 -795 -795 0 -795 0 0 798 0
797 795 -3 795 -2 0 -795z m1650 -1 l0 -794 -800 0 -800 0 0 795 0 795 793 2
c435 2 795 2 800 0 4 -2 7 -361 7 -798z m1640 0 l0 -794 -800 0 -800 0 0 795
0 795 793 2 c435 2 795 2 800 0 4 -2 7 -361 7 -798z m4930 0 l0 -794 -800 0
-800 0 0 795 0 795 793 2 c435 2 795 2 800 0 4 -2 7 -361 7 -798z m3290 1 l0
-795 -800 0 -800 0 0 793 c0 437 2 796 4 798 2 2 362 3 800 1 l796 -2 0 -795z
m1640 3 l0 -798 -800 0 -800 0 0 793 c0 437 2 796 4 798 2 2 362 4 800 4 l796
0 0 -797z m1642 787 c4 -5 8 -364 8 -797 l0 -788 -800 0 -800 0 0 798 0 797
792 0 c517 0 796 -3 800 -10z m1646 -790 l2 -795 -800 0 -800 0 0 798 0 797
797 -2 798 -3 3 -795z m1642 -1 l0 -794 -800 0 -800 0 0 795 0 795 793 2 c435
2 795 2 800 0 4 -2 7 -361 7 -798z m-32867 -2 l2 -792 -800 0 -800 0 2 792 c2
436 3 794 3 796 0 1 358 1 795 0 l795 -3 3 -793z m4927 3 l0 -795 -801 0
c-726 0 -801 1 -795 16 3 9 6 366 6 795 l0 779 795 0 795 0 0 -795z m14790 0
l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1650 0 l0 -795 -800
0 -800 0 0 795 0 795 800 0 800 0 0 -795z m3290 0 l0 -795 -805 0 -805 0 0
795 0 795 805 0 805 0 0 -795z m-24660 -855 c0 -5 0 -364 0 -797 l0 -788 -795
0 -795 0 0 788 c0 433 0 792 0 797 0 7 272 10 795 10 523 0 795 -3 795 -10z
m1648 -787 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642
-3 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800
-793 0 c-437 0 -796 2 -798 4 -2 2 -3 362 -1 800 l2 796 795 0 795 0 0 -800z
m1648 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1640 0
l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1642 7 c0 -434 0
-793 0 -797 0 -5 -359 -8 -797 -8 l-798 0 -3 785 c-1 432 0 791 3 798 3 9 173
12 800 12 l795 0 0 -790z m1645 -10 l0 -795 -795 0 -795 0 -3 798 -2 797 797
-2 798 -3 0 -795z m1645 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0
-800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1648 3
l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0 -800
-797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798
3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1648 3 l-3
-798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1640 0 l-3 -798 -795
0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1650 0 l-3 -798 -800 0 -800 0
-3 798 -2 797 805 0 805 0 -2 -797z m1640 0 l-3 -798 -797 -3 -798 -2 0 800 0
800 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0
800 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0
-795z m1643 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z
m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m-32870
-850 c0 -5 0 -364 0 -797 l0 -788 -795 0 -795 0 0 788 c0 433 0 792 0 797 0 7
272 10 795 10 523 0 795 -3 795 -10z m1645 -790 l0 -795 -797 -3 -798 -2 0
800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 793 c0 437
3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 0
788 c0 433 0 792 0 797 0 7 272 10 795 10 l795 0 0 -800z m1645 0 l0 -795
-797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1640 0 l0 -795 -795 0
-795 0 -3 785 c-1 432 0 791 3 798 3 10 168 12 797 10 l793 -3 0 -795z m1645
10 c0 -434 0 -793 0 -797 0 -5 -359 -8 -797 -8 l-798 0 -3 775 c-1 426 0 785
3 798 l5 22 795 0 795 0 0 -790z m1645 -10 l0 -795 -797 -3 -798 -2 0 793 c0
437 3 797 7 800 3 4 362 6 797 5 l791 -3 0 -795z m1645 0 l0 -800 -797 2 -798
3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1640 0 l0
-800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1648 -2 l2 -798 -800 0
-800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -797 2 -798 3 -3
785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1640 0 l0 -800
-797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z
m1648 -2 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5
l791 -3 3 -797z m1640 -1 l2 -797 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3
10 168 12 797 10 l793 -3 3 -798z m1647 3 l0 -795 -800 0 -800 0 -3 785 c-1
432 0 791 3 798 3 10 169 12 802 10 l798 -3 0 -795z m1643 -2 l2 -798 -800 0
-800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1642 2 l0
-800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z
m1645 778 c3 -13 4 -372 3 -798 l-3 -775 -797 -3 -798 -2 0 800 0 800 795 0
795 0 5 -22z m1643 -780 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3
-797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800
7 l793 0 0 -800z m-32875 -849 c3 -5 5 -364 5 -798 l0 -788 -795 0 -795 0 0
788 c0 433 0 792 0 797 0 13 1576 14 1585 1z m1639 3 c14 -6 16 -85 16 -800
l0 -794 -796 -2 c-438 -2 -798 -1 -800 1 -7 7 -4 1587 3 1594 8 9 1556 9 1577
1z m1651 -797 l0 -797 -795 0 -795 0 -3 787 c-1 434 0 794 3 801 3 10 168 12
797 10 l793 -3 0 -798z m1645 3 l0 -800 -795 2 -795 3 0 788 c0 434 2 793 5
798 4 5 323 9 796 9 l789 0 0 -800z m1645 -2 c0 -439 -3 -798 -7 -798 -5 0
-363 0 -798 0 l-790 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 0
-797z m1640 0 c0 -439 -3 -798 -7 -798 -5 0 -363 0 -798 0 l-790 0 0 794 c0
626 3 795 13 799 6 3 365 5 797 4 l785 -2 0 -797z m1640 -1 l0 -797 -788 0
c-434 0 -793 2 -798 5 -15 10 -12 1568 3 1583 9 9 195 12 798 10 l785 -3 0
-798z m1643 791 c15 -15 18 -1573 3 -1583 -5 -3 -364 -5 -798 -5 l-788 0 -3
787 c-1 434 0 794 3 801 6 17 1565 18 1583 0z m1650 -790 l2 -798 -796 -2
c-438 -2 -798 -1 -800 1 -7 7 -4 1587 3 1594 3 4 362 6 797 5 l791 -3 3 -797z
m1640 0 l2 -798 -795 -2 -795 -3 0 803 0 802 793 -2 792 -3 3 -797z m1647 -1
l0 -797 -795 0 -795 0 -3 800 -2 800 797 -2 798 -3 0 -798z m1640 1 c0 -439
-3 -798 -7 -798 -5 0 -363 0 -798 0 l-790 0 0 794 c0 626 3 795 13 799 6 3
365 5 797 4 l785 -2 0 -797z m1640 -1 l0 -797 -789 0 c-433 0 -792 3 -797 6
-5 3 -9 347 -9 794 0 621 3 789 13 793 6 3 365 5 797 4 l785 -2 0 -798z m1653
1 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3
-797z m1637 0 c0 -439 -3 -798 -7 -798 -5 0 -363 0 -798 0 l-790 0 0 793 c0
437 3 797 7 800 3 4 362 6 797 5 l791 -3 0 -797z m1639 796 c14 -6 16 -85 16
-800 l0 -794 -795 -2 c-437 -2 -800 -1 -805 0 -16 6 -14 1589 3 1595 18 8
1562 8 1581 1z m1640 0 c14 -6 16 -85 16 -800 l0 -794 -796 -2 c-438 -2 -798
-1 -800 1 -7 7 -4 1587 3 1594 8 9 1556 9 1577 1z m1656 -796 l0 -803 -797 2
-798 3 -3 787 c-1 434 0 794 3 801 3 9 173 12 800 12 l795 0 0 -802z m1644
786 c9 -23 7 -1567 -2 -1579 -4 -7 -283 -10 -800 -10 l-792 0 0 803 0 802 794
0 c715 0 794 -2 800 -16z m1641 -787 l0 -797 -798 -3 -797 -2 0 803 0 802 798
-2 797 -3 0 -798z m1640 1 c0 -439 -3 -798 -7 -798 -5 0 -363 0 -798 0 l-790
0 0 794 c0 626 3 795 13 799 6 3 365 5 797 4 l785 -2 0 -797z m-32865 -1640
c0 -434 0 -792 0 -798 0 -7 -272 -10 -795 -10 -523 0 -795 3 -795 10 0 6 0
363 0 795 0 432 0 787 0 790 0 3 358 4 795 3 l795 -3 0 -787z m1645 -8 l0
-795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -797
2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -795 0 c-523 0
-795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0 3 358 5 795 5 l795 0 0
-800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z
m1643 3 l-3 -798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12
l795 0 -2 -797z m1642 3 c0 -433 -3 -792 -6 -797 -3 -5 -347 -9 -794 -9 -709
0 -788 2 -794 16 -8 22 -8 1569 1 1577 3 4 364 6 800 5 l793 -3 0 -789z m1645
-6 l0 -795 -795 0 -795 0 -3 798 -2 797 797 -2 798 -3 0 -795z m1643 3 l-3
-798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 -2
-797z m1642 -3 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1648
3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1640 0 l-3
-798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 -2
-797z m1642 -3 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12
800 12 l795 0 0 -800z m1648 3 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0
800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3
9 173 12 800 12 l795 0 -2 -797z m1647 -3 l0 -795 -800 0 -800 0 -3 785 c-1
432 0 791 3 798 3 10 169 12 802 10 l798 -3 0 -795z m1643 3 l-3 -798 -795 0
-795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3
798 -2 797 800 0 800 0 0 -800z m1648 20 c1 -426 0 -785 -3 -797 l-5 -23 -795
0 -795 0 0 800 0 800 798 -2 797 -3 3 -775z m1640 -17 l-3 -798 -797 -3 -798
-2 0 800 0 800 800 0 800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 798
-2 797 800 0 800 0 -2 -797z m-32868 -1635 c0 -434 0 -792 0 -798 0 -7 -272
-10 -795 -10 -523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0 3
358 4 795 3 l795 -3 0 -787z m1648 -10 l2 -798 -800 0 -800 0 0 800 0 800 798
-2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m1640 0 l0 -800 -795 0 -795 0 -2 796 c-2 438 -1 798 1 800 2 2 361 4
798 4 l793 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797
-3 3 -797z m1640 0 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6
797 5 l791 -3 3 -797z m1643 798 c2 -2 3 -362 1 -800 l-2 -796 -798 2 -797 3
-3 785 c-1 432 0 791 3 798 3 9 172 12 798 12 437 0 796 -2 798 -4z m1644
-796 l0 -795 -797 -3 -798 -2 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791
-3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7
800 7 l793 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0
0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z
m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793
0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173
12 800 12 l795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 793 c0 437 3 797
7 800 3 4 362 6 797 5 l791 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 793
c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1648 -2 l2 -798 -805 0
-805 0 0 793 c0 437 3 797 7 800 3 4 365 6 802 5 l796 -3 3 -797z m1640 0 l2
-798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0
-800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1645 0 l0
-795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1643 -2 l2 -798
-800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800
0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m-32867 -1642 l-3
-793 -795 0 -795 0 -3 793 -2 792 800 0 800 0 -2 -792z m1647 -3 l0 -795 -800
0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0
795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -795 0 -795 0 0 779 c0 429 -3
786 -6 795 -6 15 69 16 795 16 l801 0 0 -795z m1650 0 l0 -795 -800 0 -800 0
0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795
800 0 800 0 0 -795z m1640 0 l0 -795 -797 2 -798 3 -3 793 -2 792 800 0 800 0
0 -795z m1650 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640
0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795
-795 0 -795 0 0 795 0 795 795 0 795 0 0 -795z m1650 0 l0 -795 -800 0 -800 0
0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795
800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0
-795z m1650 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0
l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1650 0 l0 -795 -805
0 -805 0 0 795 0 795 805 0 805 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0
795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795 800
0 800 0 0 -795z m1648 3 l-3 -793 -797 -3 -798 -2 0 795 0 795 800 0 800 0 -2
-792z m1642 -3 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640
0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m-32870 -1645 l0
-795 -795 0 -795 0 0 793 c0 435 0 794 0 797 0 3 358 4 795 3 l795 -3 0 -795z
m1648 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3
l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -795
2 -795 3 0 793 c0 435 0 794 0 797 0 3 358 5 795 5 l795 0 0 -800z m1648 3
l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0 -800
-797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 10 c0 -434 0 -793 0
-797 0 -5 -359 -8 -797 -8 l-798 0 -3 798 -2 797 800 0 800 0 0 -790z m1648
-7 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0
-800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -795 0
-795 0 0 800 0 800 795 0 795 0 0 -800z m1650 0 l0 -800 -800 0 -800 0 0 800
0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0
800 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0
-800z m1650 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0
l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1650 0 l0 -800
-802 2 -803 3 -3 798 -2 797 805 0 805 0 0 -800z m1640 0 l0 -800 -800 0 -800
0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -800 0 -800 0 0 800 0 800
800 0 800 0 0 -800z m1648 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800
0 -2 -797z m1642 -3 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z
m1640 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m-32870 -850
c0 -5 0 -364 0 -797 l0 -788 -795 0 -795 0 0 788 c0 433 0 792 0 797 0 7 272
10 795 10 523 0 795 -3 795 -10z m1648 -792 l2 -798 -800 0 -800 0 0 800 0
800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 800 0 800 800 0
800 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 0 788 c0 433 0 792 0 797 0 7
272 10 795 10 l795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798
-2 797 -3 3 -797z m1640 0 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3
4 362 6 797 5 l791 -3 3 -797z m1642 12 c0 -434 0 -793 0 -797 0 -5 -359 -8
-797 -8 l-798 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -790z
m1645 -10 l0 -795 -797 -3 -798 -2 0 793 c0 437 3 797 7 800 3 4 362 6 797 5
l791 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4
363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0
795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3
-797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800
7 l793 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3
9 173 12 800 12 l795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 793 c0 437
3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0
793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1648 -2 l2 -798 -805
0 -805 0 0 793 c0 437 3 797 7 800 3 4 365 6 802 5 l796 -3 3 -797z m1640 0
l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800
-800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1645
0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1643 -2 l2
-798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0
-800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m-32870 -850
c0 -5 0 -364 0 -797 l0 -788 -795 0 -795 0 0 788 c0 433 0 792 0 797 0 7 272
10 795 10 523 0 795 -3 795 -10z m1648 -792 l2 -798 -800 0 -800 0 0 800 0
800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797
7 800 3 4 363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 0 788 c0
434 2 793 5 798 4 5 323 9 796 9 l789 0 0 -800z m1648 -2 l2 -798 -800 0 -800
0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1640 0 l2 -798
-800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z
m1640 0 l2 -798 -800 0 -800 0 0 788 c0 602 3 791 12 800 9 9 195 12 798 10
l785 -3 3 -797z m1636 796 c14 -6 16 -85 16 -800 l0 -794 -800 0 -800 0 0 793
c0 437 3 797 7 800 8 9 1556 9 1577 1z m1654 -796 l2 -798 -800 0 -800 0 0
793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1640 0 l2 -798 -795
0 -795 0 0 800 0 800 793 -2 792 -3 3 -797z m1647 0 c0 -439 -3 -798 -7 -798
-5 0 -363 0 -798 0 l-790 0 0 800 0 800 798 -2 797 -3 0 -797z m1643 0 l2
-798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z
m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793
0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6
797 5 l791 -3 3 -797z m1640 0 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7
800 3 4 362 6 797 5 l791 -3 3 -797z m1650 0 l2 -798 -805 0 -805 0 0 793 c0
437 3 797 7 800 3 4 365 6 802 5 l796 -3 3 -797z m1640 0 l2 -798 -800 0 -800
0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1642 2 l0 -800
-800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1645
778 c8 -35 1 -1578 -7 -1578 -5 0 -363 0 -798 0 l-790 0 0 800 0 800 795 0
795 0 5 -22z m1643 -780 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3
-797z m1640 0 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797
5 l791 -3 3 -797z m-32868 -1648 l0 -795 -795 0 -795 0 0 793 c0 435 0 794 0
797 0 3 358 4 795 3 l795 -3 0 -795z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0
800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0
800 0 0 -800z m1640 0 l0 -800 -793 0 c-437 0 -796 2 -798 4 -2 2 -3 362 -1
800 l2 796 795 0 795 0 0 -800z m1645 0 l0 -795 -795 0 -795 0 -3 798 -2 797
797 -2 798 -3 0 -795z m1640 0 l0 -795 -795 0 -795 0 -3 785 c-1 432 0 791 3
798 3 10 168 12 797 10 l793 -3 0 -795z m1643 3 l-3 -798 -775 -3 c-426 -1
-785 0 -797 3 l-23 5 0 789 c0 433 3 791 7 794 3 4 363 7 800 7 l793 0 -2
-797z m1647 -3 l0 -795 -795 0 -795 0 -3 798 -2 797 797 -2 798 -3 0 -795z
m1643 3 l-3 -798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12
l795 0 -2 -797z m1640 0 l-3 -798 -792 -3 -793 -2 0 800 0 800 795 0 795 0 -2
-797z m1650 0 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z
m1640 0 l-3 -798 -795 0 -795 0 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12
l795 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3
9 173 12 800 12 l795 0 0 -800z m1648 3 l-3 -798 -795 0 -795 0 -3 798 -2 797
800 0 800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 785 c-1 432 0 791 3
798 3 9 173 12 800 12 l795 0 -2 -797z m1647 -3 l0 -795 -800 0 -800 0 -3 785
c-1 432 0 791 3 798 3 10 169 12 802 10 l798 -3 0 -795z m1643 3 l-3 -798
-795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2
-798 3 -3 798 -2 797 800 0 800 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0
800 0 800 798 -2 797 -3 0 -795z m1643 3 l-3 -798 -797 -3 -798 -2 0 800 0
800 800 0 800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0
800 0 -2 -797z m-32868 -853 c0 -5 0 -364 0 -797 l0 -788 -795 0 -795 0 0 788
c0 433 0 792 0 797 0 7 272 10 795 10 523 0 795 -3 795 -10z m1645 -790 l0
-795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800
0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 0
788 c0 433 0 792 0 797 0 7 272 10 795 10 l795 0 0 -800z m1645 0 l0 -795
-797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1643 -3 l2 -797 -797 2
-798 3 -3 785 c-1 432 0 791 3 798 3 10 168 12 797 10 l793 -3 3 -798z m1642
3 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0
0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 793 c0 437 3 797 7 800 3 4 362 6
797 5 l791 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7
800 3 4 363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0
800 795 0 795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2
797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4
363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 785 c-1 432 0
791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0
793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1642 2 l0 -800 -800
0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1648 -3
l2 -797 -802 2 -803 3 -3 785 c-1 432 0 791 3 798 3 10 169 12 802 10 l798 -3
3 -798z m1640 1 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z
m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793
0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z
m1643 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z m1642 2 l0
-800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z
m-32870 -1645 l0 -790 -795 0 -795 0 0 788 c0 433 0 789 0 792 0 3 358 4 795
3 l795 -3 0 -790z m1650 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0
-795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0
l0 -795 -795 2 -795 3 0 788 c0 433 0 789 0 792 0 3 358 5 795 5 l795 0 0
-795z m1650 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0
l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1641 791 c2 -2 3
-360 1 -795 l-2 -791 -798 2 -797 3 -3 780 c-1 429 0 786 3 793 3 9 172 12
798 12 437 0 796 -2 798 -4z m1644 -791 l0 -790 -797 -3 -798 -2 0 795 0 795
798 -2 797 -3 0 -790z m1645 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0
0 -795z m1640 0 l0 -795 -795 0 -795 0 0 795 0 795 795 0 795 0 0 -795z m1650
0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795
-800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -797 2 -798 3
-3 780 c-1 429 0 786 3 793 3 9 173 12 800 12 l795 0 0 -795z m1650 0 l0 -795
-800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0
0 795 0 795 800 0 800 0 0 -795z m1650 0 l0 -795 -802 2 -803 3 -3 793 -2 792
805 0 805 0 0 -795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0
-795z m1640 0 l0 -795 -800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1645 0
l0 -790 -797 -3 -798 -2 0 795 0 795 798 -2 797 -3 0 -790z m1645 0 l0 -795
-800 0 -800 0 0 795 0 795 800 0 800 0 0 -795z m1640 0 l0 -795 -800 0 -800 0
0 795 0 795 800 0 800 0 0 -795z m-32870 -1637 c0 -434 0 -792 0 -798 0 -7
-272 -10 -795 -10 -523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0
3 358 4 795 3 l795 -3 0 -787z m1645 -8 l0 -795 -797 -3 -798 -2 0 800 0 800
798 -2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0
0 -800z m1640 0 l0 -800 -795 0 c-523 0 -795 3 -795 10 0 6 0 363 0 795 0 432
0 787 0 790 0 3 358 5 795 5 l795 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2
0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -797 2 -798 3 -3 785 c-1
432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1641 796 c2 -2 3 -362 1
-800 l-2 -796 -798 2 -797 3 -3 785 c-1 432 0 791 3 798 3 9 172 12 798 12
437 0 796 -2 798 -4z m1644 -796 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2
797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z
m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1650 0 l0
-800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2
-798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3
785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1650 0 l0 -800
-800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798 3
-3 798 -2 797 800 0 800 0 0 -800z m1650 0 l0 -800 -802 2 -803 3 -3 785 c-1
432 0 791 3 798 3 9 174 12 805 12 l800 0 0 -800z m1640 0 l0 -800 -800 0
-800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -800 0 -800 0 0 800
0 800 800 0 800 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798
-2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m1640 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m-32870
-1632 c0 -434 0 -792 0 -798 0 -7 -272 -10 -795 -10 -523 0 -795 3 -795 10 0
6 0 363 0 795 0 432 0 787 0 790 0 3 358 4 795 3 l795 -3 0 -787z m1645 -8 l0
-795 -797 -3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800
0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800 7 l793 0 0 -800z m1640 0 l0
-800 -795 0 c-523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 787 0 790 0 3
358 5 795 5 l795 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798
-2 797 -3 0 -795z m1643 -2 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3
4 362 6 797 5 l791 -3 3 -797z m1643 798 c2 -2 3 -362 1 -800 l-2 -796 -798 2
-797 3 -3 785 c-1 432 0 791 3 798 3 9 172 12 798 12 437 0 796 -2 798 -4z
m1644 -796 l0 -795 -797 -3 -798 -2 0 793 c0 437 3 797 7 800 3 4 362 6 797 5
l791 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4
363 7 800 7 l793 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0
795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3
-797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7 800
7 l793 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 785 c-1 432 0 791 3 798 3
9 173 12 800 12 l795 0 0 -800z m1648 -2 l2 -798 -800 0 -800 0 0 793 c0 437
3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1640 0 l2 -798 -800 0 -800 0 0
793 c0 437 3 797 7 800 3 4 362 6 797 5 l791 -3 3 -797z m1650 -1 l2 -797
-802 2 -803 3 -3 785 c-1 432 0 791 3 798 3 10 169 12 802 10 l798 -3 3 -798z
m1640 1 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791
-3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 363 7
800 7 l793 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0 800 0 800 798 -2 797
-3 0 -795z m1643 -2 l2 -798 -800 0 -800 0 0 800 0 800 798 -2 797 -3 3 -797z
m1640 0 l2 -798 -800 0 -800 0 0 793 c0 437 3 797 7 800 3 4 362 6 797 5 l791
-3 3 -797z m-16430 -1649 l-3 -794 -797 -3 -798 -2 0 800 0 800 793 2 c435 2
795 0 800 -3 4 -3 6 -363 5 -800z m13150 0 l-3 -794 -797 -3 -798 -2 0 800 0
800 793 2 c435 2 795 0 800 -3 4 -3 6 -363 5 -800z m-29585 4 l-3 -798 -795 0
-795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1645 0 l-3 -798 -797 -3 -798 -2
0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2
797 800 0 800 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 -3 798 -2 797 798 0
797 0 0 -800z m1648 3 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2
-797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z
m1647 5 c0 -517 -3 -796 -10 -800 -12 -9 -1556 -11 -1579 -2 -14 6 -16 85 -16
800 l0 794 803 0 802 0 0 -792z m1643 -5 l-3 -798 -795 0 -795 0 -3 798 -2
797 800 0 800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0
800 0 -2 -797z m1640 0 l-3 -798 -792 -3 -793 -2 0 800 0 800 795 0 795 0 -2
-797z m3290 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z
m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1648 3
l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1640 0 l-3 -798
-795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1650 0 l-3 -798 -800 0
-800 0 -3 798 -2 797 805 0 805 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3
798 -2 797 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797 2 -798 3 -3 798 -2
797 800 0 800 0 0 -800z m3288 3 l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0
800 0 -2 -797z m1640 0 l-3 -798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2
-797z m-32868 -853 c0 -5 0 -364 0 -797 l0 -788 -795 0 -795 0 0 788 c0 433 0
792 0 797 0 7 272 10 795 10 523 0 795 -3 795 -10z m1645 -790 l0 -795 -797
-3 -798 -2 0 800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0
0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -795 2 -795 3 0 788 c0 433
0 792 0 797 0 7 272 10 795 10 l795 0 0 -800z m1645 0 l0 -795 -797 -3 -798
-2 0 800 0 800 798 -2 797 -3 0 -795z m1643 3 l-3 -798 -795 0 -795 0 -3 785
c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 -2 -797z m1642 7 c0 -434 0
-793 0 -797 0 -5 -359 -8 -797 -8 l-798 0 -3 785 c-1 432 0 791 3 798 3 9 173
12 800 12 l795 0 0 -790z m1645 -10 l0 -795 -797 -3 -798 -2 0 800 0 800 798
-2 797 -3 0 -795z m1645 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0 0
-800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z m1648 3
l-3 -798 -797 -3 -798 -2 0 800 0 800 800 0 800 0 -2 -797z m1642 -3 l0 -800
-797 2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798
3 -3 785 c-1 432 0 791 3 798 3 9 173 12 800 12 l795 0 0 -800z m1648 3 l-3
-798 -795 0 -795 0 -3 798 -2 797 800 0 800 0 -2 -797z m1642 -3 l0 -800 -797
2 -798 3 -3 798 -2 797 800 0 800 0 0 -800z m1648 3 l-3 -798 -800 0 -800 0
-3 785 c-1 432 0 791 3 798 3 9 174 12 805 12 l800 0 -2 -797z m1642 -3 l0
-800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1640 0 l0 -800 -797 2
-798 3 -3 798 -2 797 800 0 800 0 0 -800z m1645 0 l0 -795 -797 -3 -798 -2 0
800 0 800 798 -2 797 -3 0 -795z m1645 0 l0 -800 -800 0 -800 0 0 800 0 800
800 0 800 0 0 -800z m1640 0 l0 -800 -797 2 -798 3 -3 798 -2 797 800 0 800 0
0 -800z m-32870 -850 c0 -5 0 -363 0 -795 0 -432 0 -789 0 -795 0 -7 -272 -10
-795 -10 -523 0 -795 3 -795 10 0 6 0 363 0 795 0 432 0 790 0 795 0 7 272 10
795 10 523 0 795 -3 795 -10z m1645 -795 l0 -800 -795 0 -795 0 -3 803 -2 802
797 -2 798 -3 0 -800z m1645 0 l0 -805 -797 2 -798 3 -3 803 -2 802 800 0 800
0 0 -805z m1640 0 l0 -805 -795 0 c-523 0 -795 3 -795 10 0 6 0 363 0 795 0
432 0 790 0 795 0 7 272 10 795 10 l795 0 0 -805z m1645 0 l0 -800 -795 0
-795 0 -3 803 -2 802 797 -2 798 -3 0 -800z m1640 0 l0 -800 -795 0 -795 0 -3
790 c-1 435 0 796 3 803 3 10 168 12 797 10 l793 -3 0 -800z m1643 3 l-3 -803
-784 -3 c-571 -1 -787 1 -797 9 -12 10 -14 147 -14 799 0 432 3 790 7 793 3 4
363 7 800 7 l793 0 -2 -802z m1650 0 l2 -798 -22 -5 c-13 -3 -372 -4 -798 -3
l-775 3 -3 803 -2 802 797 -2 798 -3 3 -797z m1640 0 l-3 -803 -795 0 -795 0
-3 790 c-1 435 0 796 3 803 3 9 173 12 800 12 l795 0 -2 -802z m1640 0 l-3
-803 -792 -3 -793 -2 0 805 0 805 795 0 795 0 -2 -802z m1650 0 l-3 -803 -797
-3 -798 -2 0 805 0 805 800 0 800 0 -2 -802z m16432 -818 l0 -1620 -6550 0
c-6543 0 -6550 0 -6550 20 0 15 -7 20 -25 20 -18 0 -25 -5 -25 -20 0 -20 -7
-20 -800 -20 -793 0 -800 0 -800 20 0 13 -7 20 -20 20 -13 0 -20 -7 -20 -20 0
-20 -7 -20 -800 -20 l-800 0 0 800 c0 704 2 800 15 800 8 0 15 7 15 15 0 9 -7
18 -15 21 -13 5 -15 103 -15 798 0 436 3 796 7 799 3 4 3691 7 8195 7 l8188 0
0 -1620z m-32870 -820 l0 -795 -795 0 -795 0 0 793 c0 435 0 794 0 797 0 3
358 4 795 3 l795 -3 0 -795z m1648 -2 l2 -798 -800 0 -800 0 0 800 0 800 798
-2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m1640 0 l0 -800 -795 2 -795 3 0 793 c0 435 0 794 0 797 0 3 358 5 795
5 l795 0 0 -800z m1650 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0
-800z m1640 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z m1641
796 c2 -2 3 -362 1 -800 l-2 -796 -798 2 -797 3 -3 785 c-1 432 0 791 3 798 3
9 172 12 798 12 437 0 796 -2 798 -4z m1647 -798 l2 -798 -800 0 -800 0 0 800
0 800 798 -2 797 -3 3 -797z m1642 2 l0 -800 -800 0 -800 0 0 800 0 800 800 0
800 0 0 -800z m1640 0 l0 -800 -795 0 -795 0 0 800 0 800 795 0 795 0 0 -800z
m1650 0 l0 -800 -800 0 -800 0 0 800 0 800 800 0 800 0 0 -800z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,4 +1,7 @@
min_slic3r_version = 2.5.0-alpha0
1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format.
min_slic3r_version = 2.4.0-rc
1.4.6 Added SLA materials. Updated filament profiles.
1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code.
1.4.4 Added multiple Fiberlogy filament profiles. Updated Extrudr filament profiles.
1.4.3 Added new filament profiles and SLA materials.

View File

@ -5,7 +5,7 @@
name = Prusa Research
# 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.
config_version = 1.4.5
config_version = 1.5.0-alpha0
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -184,7 +184,7 @@ notes =
overhangs = 1
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
perimeters = 2
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
@ -193,7 +193,7 @@ print_settings_id =
raft_layers = 0
raft_first_layer_density = 90%
resolution = 0
seam_position = nearest
seam_position = aligned
single_extruder_multi_material_priming = 1
skirts = 1
skirt_distance = 2
@ -243,6 +243,14 @@ bottom_solid_min_thickness = 0.5
gcode_label_objects = 1
infill_anchor = 2.5
infill_anchor_max = 12
wall_add_middle_threshold = 75%
wall_split_middle_threshold = 50%
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 0.4
wall_distribution_count = 1
min_bead_width = 85%
min_feature_size = 0.1
[print:*MK3*]
fill_pattern = grid
@ -284,11 +292,19 @@ support_material_interface_spacing = 0.15
support_material_spacing = 1
support_material_xy_spacing = 150%
support_material_contact_distance = 0.1
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
thick_bridges = 0
bridge_flow_ratio = 1
bridge_speed = 20
wipe_tower_bridging = 6
wall_add_middle_threshold = 85%
wall_split_middle_threshold = 70%
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 0.25
wall_distribution_count = 1
min_bead_width = 85%
min_feature_size = 0.0625
[print:*0.25nozzleMK3*]
inherits = *0.25nozzle*
@ -330,13 +346,21 @@ support_material_extrusion_width = 0.55
support_material_contact_distance = 0.15
support_material_xy_spacing = 80%
support_material_interface_spacing = 0.3
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
infill_anchor_max = 15
top_solid_min_thickness = 0.9
bottom_solid_min_thickness = 0.6
thick_bridges = 1
bridge_flow_ratio = 0.95
bridge_speed = 25
wall_add_middle_threshold = 85%
wall_split_middle_threshold = 70%
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 0.6
wall_distribution_count = 1
min_bead_width = 85%
min_feature_size = 0.15
[print:*0.6nozzleMK3*]
inherits = *0.6nozzle*
@ -375,7 +399,7 @@ support_material_interface_speed = 100%
support_material_spacing = 2
support_material_xy_spacing = 80%
support_material_threshold = 50
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode
fill_pattern = gyroid
fill_density = 15%
infill_anchor_max = 20
@ -398,6 +422,14 @@ bottom_solid_min_thickness = 0.8
single_extruder_multi_material_priming = 0
thick_bridges = 1
overhangs = 0
wall_add_middle_threshold = 85%
wall_split_middle_threshold = 70%
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 0.8
wall_distribution_count = 1
min_bead_width = 85%
min_feature_size = 0.2
[print:*soluble_support*]
overhangs = 1
@ -1719,7 +1751,7 @@ filament_wipe = 0
inherits = *PLA*
filament_vendor = ColorFabb
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
extrusion_multiplier = 1.12
filament_cost = 80.65
filament_density = 3.9
filament_spool_weight = 236
@ -1730,7 +1762,7 @@ filament_max_volumetric_speed = 9
inherits = *PLA*
filament_vendor = ColorFabb
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
extrusion_multiplier = 1.15
filament_cost = 80.65
filament_density = 3.13
filament_spool_weight = 236
@ -1741,7 +1773,7 @@ filament_max_volumetric_speed = 8
inherits = *PLA*
filament_vendor = ColorFabb
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
extrusion_multiplier = 1.2
extrusion_multiplier = 1.15
filament_cost = 80.65
filament_density = 3.9
filament_spool_weight = 236
@ -7319,6 +7351,70 @@ material_vendor = Ameralabs
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:BASF Ultracur3D RG 35 @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 4
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 45 @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 2.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 45 M @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 2.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 80 @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 4.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 80 White @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 4.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFFFFF
[sla_material:BASF Ultracur3D ST 80 Black @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 4.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D EL 150 Black @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 2
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D FL 300 Black @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 3
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:PrimaCreator Tough Light Grey @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 1.8
@ -7418,6 +7514,38 @@ material_type = Tough
material_vendor = Peopoly
material_colour = #F8F8F8
[sla_material:Liqcreate Clear Impact @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 7
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #F8F8F8
[sla_material:Liqcreate Strong X @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 7
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #C0C0C0
[sla_material:Resinworks 3D Green @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 5
initial_exposure_time = 25
material_type = Tough
material_vendor = Resinworks 3D
material_colour = #00B900
[sla_material:3DJake Blue @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 1.8
initial_exposure_time = 25
material_type = Tough
material_vendor = 3DJake
material_colour = #007EFD
## 0.05 SL1S
## Prusa Polymers 0.05
@ -7641,6 +7769,70 @@ material_vendor = Ameralabs
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:BASF Ultracur3D RG 35 @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 45 @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 3
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 45 M @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 3
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 80 @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 5.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 80 White @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 5.9
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFFFFF
[sla_material:BASF Ultracur3D ST 80 Black @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 5.9
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D EL 150 Black @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 3.8
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D FL 300 Black @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4.8
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:PrimaCreator Tough Light Grey @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 2.4
@ -8044,6 +8236,38 @@ material_type = Tough
material_vendor = Photocentric
material_colour = #808080
[sla_material:Liqcreate Clear Impact @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 10
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #F8F8F8
[sla_material:Liqcreate Strong X @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 10
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #C0C0C0
[sla_material:Resinworks 3D Green @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 7
initial_exposure_time = 25
material_type = Tough
material_vendor = Resinworks 3D
material_colour = #00B900
[sla_material:3DJake Blue @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 2
initial_exposure_time = 25
material_type = Tough
material_vendor = 3DJake
material_colour = #007EFD
## 0.1 SL1S
## Prusa Polymers 0.1
@ -8267,6 +8491,70 @@ material_vendor = Ameralabs
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:BASF Ultracur3D RG 35 @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 10
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 45 @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 7.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 45 M @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 4.5
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D ST 80 @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 9
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFEEE6
[sla_material:BASF Ultracur3D ST 80 White @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 9
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #FFFFFF
[sla_material:BASF Ultracur3D ST 80 Black @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 9
initial_exposure_time = 25
material_type = Tough
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D EL 150 Black @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 5
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:BASF Ultracur3D FL 300 Black @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 6
initial_exposure_time = 25
material_type = Flexible
material_vendor = BASF
material_colour = #595959
[sla_material:PrimaCreator Tough Light Grey @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 3
@ -8348,6 +8636,38 @@ material_type = Tough
material_vendor = Peopoly
material_colour = #F8F8F8
[sla_material:Liqcreate Clear Impact @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 20
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #F8F8F8
[sla_material:Liqcreate Strong X @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 20
initial_exposure_time = 40
material_type = Tough
material_vendor = Liqcreate
material_colour = #C0C0C0
[sla_material:Resinworks 3D Green @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 13
initial_exposure_time = 25
material_type = Tough
material_vendor = Resinworks 3D
material_colour = #00B900
[sla_material:3DJake Blue @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 3
initial_exposure_time = 25
material_type = Tough
material_vendor = 3DJake
material_colour = #007EFD
[printer:*common*]
printer_technology = FFF
bed_shape = 0x0,250x0,250x210,0x210

View File

@ -4,3 +4,4 @@
add_subdirectory(its_neighbor_index)
# add_subdirectory(opencsg)
#add_subdirectory(aabb-evaluation)
add_subdirectory(wx_gl_test)

View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.10)
project(wxGL_Test)
add_executable(wxgltest WIN32 main.cpp )
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html)
find_package(OpenGL REQUIRED)
find_package(NanoSVG REQUIRED)
include(${wxWidgets_USE_FILE})
target_include_directories(wxgltest PRIVATE ${wxWidgets_INCLUDE_DIRS})
target_compile_definitions(wxgltest PRIVATE ${wxWidgets_DEFINITIONS})
target_compile_definitions(wxgltest PUBLIC -DwxDEBUG_LEVEL=0)
target_link_libraries(wxgltest ${wxWidgets_LIBRARIES}
OpenGL::GL
OpenGL::EGL
NanoSVG::nanosvgrast
# png16
# X11
X11 wayland-client wayland-egl
)

View File

@ -0,0 +1,221 @@
#include <iostream>
#include <utility>
#include <memory>
#include <vector>
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/tglbtn.h>
#include <wx/combobox.h>
#include <wx/msgdlg.h>
#include <wx/glcanvas.h>
#include <wx/notebook.h>
class Renderer {
protected:
wxGLCanvas *m_canvas;
std::unique_ptr<wxGLContext> m_context;
public:
Renderer(wxGLCanvas *c): m_canvas{c} {
m_context = std::make_unique<wxGLContext>(m_canvas);
}
wxGLContext * context() { return m_context.get(); }
const wxGLContext * context() const { return m_context.get(); }
void set_active()
{
m_canvas->SetCurrent(*m_context);
// Set the current clear color to sky blue and the current drawing color to
// white.
glClearColor(0.1, 0.39, 0.88, 1.0);
glColor3f(1.0, 1.0, 1.0);
// Tell the rendering engine not to draw backfaces. Without this code,
// all four faces of the tetrahedron would be drawn and it is possible
// that faces farther away could be drawn after nearer to the viewer.
// Since there is only one closed polyhedron in the whole scene,
// eliminating the drawing of backfaces gives us the realism we need.
// THIS DOES NOT WORK IN GENERAL.
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Set the camera lens so that we have a perspective viewing volume whose
// horizontal bounds at the near clipping plane are -2..2 and vertical
// bounds are -1.5..1.5. The near clipping plane is 1 unit from the camera
// and the far clipping plane is 40 units away.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-2, 2, -1.5, 1.5, 1, 40);
// Set up transforms so that the tetrahedron which is defined right at
// the origin will be rotated and moved into the view volume. First we
// rotate 70 degrees around y so we can see a lot of the left side.
// Then we rotate 50 degrees around x to "drop" the top of the pyramid
// down a bit. Then we move the object back 3 units "into the screen".
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -3);
glRotatef(50, 1, 0, 0);
glRotatef(70, 0, 1, 0);
}
void draw_scene(long w, long h)
{
glViewport(0, 0, GLsizei(w), GLsizei(h));
glClear(GL_COLOR_BUFFER_BIT);
// Draw a white grid "floor" for the tetrahedron to sit on.
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
for (GLfloat i = -2.5; i <= 2.5; i += 0.25) {
glVertex3f(i, 0, 2.5); glVertex3f(i, 0, -2.5);
glVertex3f(2.5, 0, i); glVertex3f(-2.5, 0, i);
}
glEnd();
// Draw the tetrahedron. It is a four sided figure, so when defining it
// with a triangle strip we have to repeat the last two vertices.
glBegin(GL_TRIANGLE_STRIP);
glColor3f(1, 1, 1); glVertex3f(0, 2, 0);
glColor3f(1, 0, 0); glVertex3f(-1, 0, 1);
glColor3f(0, 1, 0); glVertex3f(1, 0, 1);
glColor3f(0, 0, 1); glVertex3f(0, 0, -1.4);
glColor3f(1, 1, 1); glVertex3f(0, 2, 0);
glColor3f(1, 0, 0); glVertex3f(-1, 0, 1);
glEnd();
glFlush();
}
void swap_buffers() { m_canvas->SwapBuffers(); }
};
// The top level frame of the application.
class MyFrame: public wxFrame
{
wxGLCanvas *m_canvas;
std::unique_ptr<Renderer> m_renderer;
public:
MyFrame(const wxString & title,
const wxPoint & pos,
const wxSize & size);
wxGLCanvas * canvas() { return m_canvas; }
const wxGLCanvas * canvas() const { return m_canvas; }
};
class App : public wxApp {
MyFrame *m_frame = nullptr;
wxString m_fname;
public:
bool OnInit() override {
m_frame = new MyFrame("Wayland wxNotebook issue", wxDefaultPosition, wxSize(1024, 768));
m_frame->Show( true );
return true;
}
};
wxIMPLEMENT_APP(App);
MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size):
wxFrame(nullptr, wxID_ANY, title, pos, size)
{
wxMenu *menuFile = new wxMenu;
menuFile->Append(wxID_OPEN);
menuFile->Append(wxID_EXIT);
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append( menuFile, "&File" );
SetMenuBar( menuBar );
auto notebookpanel = new wxPanel(this);
auto notebook = new wxNotebook(notebookpanel, wxID_ANY);
auto maintab = new wxPanel(notebook);
m_canvas = new wxGLCanvas(maintab,
wxID_ANY,
nullptr,
wxDefaultPosition,
wxDefaultSize,
wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE);
m_renderer = std::make_unique<Renderer>(m_canvas);
wxPanel *control_panel = new wxPanel(maintab);
auto controlsizer = new wxBoxSizer(wxHORIZONTAL);
auto console_sizer = new wxBoxSizer(wxVERTICAL);
std::vector<wxString> combolist = {"One", "Two", "Three"};
auto combobox = new wxComboBox(control_panel, wxID_ANY, combolist[0],
wxDefaultPosition, wxDefaultSize,
int(combolist.size()), combolist.data());
auto sz = new wxBoxSizer(wxHORIZONTAL);
sz->Add(new wxStaticText(control_panel, wxID_ANY, "Choose number"), 0,
wxALL | wxALIGN_CENTER, 5);
sz->Add(combobox, 1, wxALL | wxEXPAND, 5);
console_sizer->Add(sz, 0, wxEXPAND);
auto btn1 = new wxToggleButton(control_panel, wxID_ANY, "Button1");
console_sizer->Add(btn1, 0, wxALL | wxEXPAND, 5);
auto btn2 = new wxToggleButton(control_panel, wxID_ANY, "Button2");
btn2->SetValue(true);
console_sizer->Add(btn2, 0, wxALL | wxEXPAND, 5);
controlsizer->Add(console_sizer, 1, wxEXPAND);
control_panel->SetSizer(controlsizer);
auto maintab_sizer = new wxBoxSizer(wxHORIZONTAL);
maintab_sizer->Add(m_canvas, 1, wxEXPAND);
maintab_sizer->Add(control_panel, 0);
maintab->SetSizer(maintab_sizer);
notebook->AddPage(maintab, "Main");
wxTextCtrl* textCtrl1 = new wxTextCtrl(notebook, wxID_ANY, L"Tab 2 Contents");
notebook->AddPage(textCtrl1, "Dummy");
auto notebooksizer = new wxBoxSizer(wxHORIZONTAL);
notebooksizer->Add(notebook, 1, wxEXPAND);
notebookpanel->SetSizer(notebooksizer);
auto topsizer = new wxBoxSizer(wxHORIZONTAL);
topsizer->Add(notebookpanel, 1, wxEXPAND);
SetSizer(topsizer);
SetMinSize(size);
Bind(wxEVT_MENU, [this](wxCommandEvent &) {
wxFileDialog dlg(this, "Select file", wxEmptyString,
wxEmptyString, "*.*", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
dlg.ShowModal();
}, wxID_OPEN);
Bind(wxEVT_MENU, [this](wxCommandEvent &) {
Close();
}, wxID_EXIT);
Bind(wxEVT_SHOW, [this](wxShowEvent &) {
m_renderer->set_active();
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &){
wxPaintDC dc(m_canvas);
const wxSize sz = m_canvas->GetClientSize();
m_renderer->draw_scene(sz.x, sz.y);
m_renderer->swap_buffers();
});
});
}

View File

@ -2290,6 +2290,12 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
{
#ifdef CLIPPERLIB_USE_XYZ
if (dir == dLeftToRight)
SetZ(e->Curr, *horzEdge, *e);
else
SetZ(e->Curr, *e, *horzEdge);
#endif
op1 = AddOutPt(horzEdge, e->Curr);
TEdge* eNextHorz = m_SortedEdges;
while (eNextHorz)
@ -2614,6 +2620,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
{
e->Curr.x() = TopX( *e, topY );
e->Curr.y() = topY;
#ifdef CLIPPERLIB_USE_XYZ
e->Curr.z() = topY == e->Top.y() ? e->Top.z() : (topY == e->Bot.y() ? e->Bot.z() : 0);
#endif
}
//When StrictlySimple and 'e' is being touched by another edge, then

View File

@ -1,5 +1,5 @@
#include "IndexedMesh.hpp"
#include "Concurrency.hpp"
#include "AABBMesh.hpp"
#include <Execution/ExecutionTBB.hpp>
#include <libslic3r/AABBTreeIndirect.hpp>
#include <libslic3r/TriangleMesh.hpp>
@ -12,9 +12,7 @@
namespace Slic3r {
namespace sla {
class IndexedMesh::AABBImpl {
class AABBMesh::AABBImpl {
private:
AABBTreeIndirect::Tree3f m_tree;
double m_triangle_ray_epsilon;
@ -68,7 +66,7 @@ public:
}
};
template<class M> void IndexedMesh::init(const M &mesh, bool calculate_epsilon)
template<class M> void AABBMesh::init(const M &mesh, bool calculate_epsilon)
{
BoundingBoxf3 bb = bounding_box(mesh);
m_ground_level += bb.min(Z);
@ -77,73 +75,86 @@ template<class M> void IndexedMesh::init(const M &mesh, bool calculate_epsilon)
m_aabb->init(*m_tm, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&tmesh)
AABBMesh::AABBMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon)
: m_tm(&tmesh)
, m_aabb(new AABBImpl())
, m_vfidx{tmesh}
, m_fnidx{its_face_neighbors(tmesh)}
{
init(tmesh, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&mesh.its)
AABBMesh::AABBMesh(const TriangleMesh &mesh, bool calculate_epsilon)
: m_tm(&mesh.its)
, m_aabb(new AABBImpl())
, m_vfidx{mesh.its}
, m_fnidx{its_face_neighbors(mesh.its)}
{
init(mesh, calculate_epsilon);
}
IndexedMesh::~IndexedMesh() {}
AABBMesh::~AABBMesh() {}
IndexedMesh::IndexedMesh(const IndexedMesh &other):
m_tm(other.m_tm), m_ground_level(other.m_ground_level),
m_aabb( new AABBImpl(*other.m_aabb) ) {}
AABBMesh::AABBMesh(const AABBMesh &other)
: m_tm(other.m_tm)
, m_ground_level(other.m_ground_level)
, m_aabb(new AABBImpl(*other.m_aabb))
, m_vfidx{other.m_vfidx}
, m_fnidx{other.m_fnidx}
{}
IndexedMesh &IndexedMesh::operator=(const IndexedMesh &other)
AABBMesh &AABBMesh::operator=(const AABBMesh &other)
{
m_tm = other.m_tm;
m_ground_level = other.m_ground_level;
m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this;
m_aabb.reset(new AABBImpl(*other.m_aabb));
m_vfidx = other.m_vfidx;
m_fnidx = other.m_fnidx;
return *this;
}
IndexedMesh &IndexedMesh::operator=(IndexedMesh &&other) = default;
AABBMesh &AABBMesh::operator=(AABBMesh &&other) = default;
IndexedMesh::IndexedMesh(IndexedMesh &&other) = default;
AABBMesh::AABBMesh(AABBMesh &&other) = default;
const std::vector<Vec3f>& IndexedMesh::vertices() const
const std::vector<Vec3f>& AABBMesh::vertices() const
{
return m_tm->vertices;
}
const std::vector<Vec3i>& IndexedMesh::indices() const
const std::vector<Vec3i>& AABBMesh::indices() const
{
return m_tm->indices;
}
const Vec3f& IndexedMesh::vertices(size_t idx) const
const Vec3f& AABBMesh::vertices(size_t idx) const
{
return m_tm->vertices[idx];
}
const Vec3i& IndexedMesh::indices(size_t idx) const
const Vec3i& AABBMesh::indices(size_t idx) const
{
return m_tm->indices[idx];
}
Vec3d IndexedMesh::normal_by_face_id(int face_id) const {
Vec3d AABBMesh::normal_by_face_id(int face_id) const {
return its_unnormalized_normal(*m_tm, face_id).cast<double>().normalized();
}
IndexedMesh::hit_result
IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
AABBMesh::hit_result
AABBMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
{
assert(is_approx(dir.norm(), 1.));
igl::Hit hit{-1, -1, 0.f, 0.f, 0.f};
@ -171,10 +182,10 @@ IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
return ret;
}
std::vector<IndexedMesh::hit_result>
IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
std::vector<AABBMesh::hit_result>
AABBMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
{
std::vector<IndexedMesh::hit_result> outs;
std::vector<AABBMesh::hit_result> outs;
std::vector<igl::Hit> hits;
m_aabb->intersect_ray(*m_tm, s, dir, hits);
@ -192,7 +203,7 @@ IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
// Convert the igl::Hit into hit_result
outs.reserve(hits.size());
for (const igl::Hit& hit : hits) {
outs.emplace_back(IndexedMesh::hit_result(*this));
outs.emplace_back(AABBMesh::hit_result(*this));
outs.back().m_t = double(hit.t);
outs.back().m_dir = dir;
outs.back().m_source = s;
@ -207,8 +218,8 @@ IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const
#ifdef SLIC3R_HOLE_RAYCASTER
IndexedMesh::hit_result IndexedMesh::filter_hits(
const std::vector<IndexedMesh::hit_result>& object_hits) const
AABBMesh::hit_result IndexedMesh::filter_hits(
const std::vector<AABBMesh::hit_result>& object_hits) const
{
assert(! m_holes.empty());
hit_result out(*this);
@ -304,7 +315,7 @@ IndexedMesh::hit_result IndexedMesh::filter_hits(
#endif
double IndexedMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
double AABBMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
double sqdst = 0;
Eigen::Matrix<double, 1, 3> pp = p;
Eigen::Matrix<double, 1, 3> cc;
@ -313,143 +324,4 @@ double IndexedMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
return sqdst;
}
static bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
double eps = 0.05)
{
using Line3D = Eigen::ParametrizedLine<double, 3>;
auto line = Line3D::Through(e1, e2);
double d = line.distance(p);
return std::abs(d) < eps;
}
PointSet normals(const PointSet& points,
const IndexedMesh& mesh,
double eps,
std::function<void()> thr, // throw on cancel
const std::vector<unsigned>& pt_indices)
{
if (points.rows() == 0 || mesh.vertices().empty() || mesh.indices().empty())
return {};
std::vector<unsigned> range = pt_indices;
if (range.empty()) {
range.resize(size_t(points.rows()), 0);
std::iota(range.begin(), range.end(), 0);
}
PointSet ret(range.size(), 3);
// for (size_t ridx = 0; ridx < range.size(); ++ridx)
ccr::for_each(size_t(0), range.size(),
[&ret, &mesh, &points, thr, eps, &range](size_t ridx) {
thr();
unsigned el = range[ridx];
auto eidx = Eigen::Index(el);
int faceid = 0;
Vec3d p;
mesh.squared_distance(points.row(eidx), faceid, p);
auto trindex = mesh.indices(faceid);
const Vec3d &p1 = mesh.vertices(trindex(0)).cast<double>();
const Vec3d &p2 = mesh.vertices(trindex(1)).cast<double>();
const Vec3d &p3 = mesh.vertices(trindex(2)).cast<double>();
// We should check if the point lies on an edge of the hosting
// triangle. If it does then all the other triangles using the
// same two points have to be searched and the final normal should
// be some kind of aggregation of the participating triangle
// normals. We should also consider the cases where the support
// point lies right on a vertex of its triangle. The procedure is
// the same, get the neighbor triangles and calculate an average
// normal.
// mark the vertex indices of the edge. ia and ib marks and edge
// ic will mark a single vertex.
int ia = -1, ib = -1, ic = -1;
if (std::abs((p - p1).norm()) < eps) {
ic = trindex(0);
} else if (std::abs((p - p2).norm()) < eps) {
ic = trindex(1);
} else if (std::abs((p - p3).norm()) < eps) {
ic = trindex(2);
} else if (point_on_edge(p, p1, p2, eps)) {
ia = trindex(0);
ib = trindex(1);
} else if (point_on_edge(p, p2, p3, eps)) {
ia = trindex(1);
ib = trindex(2);
} else if (point_on_edge(p, p1, p3, eps)) {
ia = trindex(0);
ib = trindex(2);
}
// vector for the neigboring triangles including the detected one.
std::vector<size_t> neigh;
if (ic >= 0) { // The point is right on a vertex of the triangle
for (size_t n = 0; n < mesh.indices().size(); ++n) {
thr();
Vec3i ni = mesh.indices(n);
if ((ni(X) == ic || ni(Y) == ic || ni(Z) == ic))
neigh.emplace_back(n);
}
} else if (ia >= 0 && ib >= 0) { // the point is on and edge
// now get all the neigboring triangles
for (size_t n = 0; n < mesh.indices().size(); ++n) {
thr();
Vec3i ni = mesh.indices(n);
if ((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) &&
(ni(X) == ib || ni(Y) == ib || ni(Z) == ib))
neigh.emplace_back(n);
}
}
// Calculate the normals for the neighboring triangles
std::vector<Vec3d> neighnorms;
neighnorms.reserve(neigh.size());
for (size_t &tri_id : neigh)
neighnorms.emplace_back(mesh.normal_by_face_id(tri_id));
// Throw out duplicates. They would cause trouble with summing. We
// will use std::unique which works on sorted ranges. We will sort
// by the coefficient-wise sum of the normals. It should force the
// same elements to be consecutive.
std::sort(neighnorms.begin(), neighnorms.end(),
[](const Vec3d &v1, const Vec3d &v2) {
return v1.sum() < v2.sum();
});
auto lend = std::unique(neighnorms.begin(), neighnorms.end(),
[](const Vec3d &n1, const Vec3d &n2) {
// Compare normals for equivalence.
// This is controvers stuff.
auto deq = [](double a, double b) {
return std::abs(a - b) < 1e-3;
};
return deq(n1(X), n2(X)) &&
deq(n1(Y), n2(Y)) &&
deq(n1(Z), n2(Z));
});
if (!neighnorms.empty()) { // there were neighbors to count with
// sum up the normals and then normalize the result again.
// This unification seems to be enough.
Vec3d sumnorm(0, 0, 0);
sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm);
sumnorm.normalize();
ret.row(long(ridx)) = sumnorm;
} else { // point lies safely within its triangle
Eigen::Vector3d U = p2 - p1;
Eigen::Vector3d V = p3 - p1;
ret.row(long(ridx)) = U.cross(V).normalized();
}
});
return ret;
}
}} // namespace Slic3r::sla
} // namespace Slic3r

View File

@ -1,10 +1,11 @@
#ifndef SLA_INDEXEDMESH_H
#define SLA_INDEXEDMESH_H
#ifndef PRUSASLICER_AABBMESH_H
#define PRUSASLICER_AABBMESH_H
#include <memory>
#include <vector>
#include <libslic3r/Point.hpp>
#include <libslic3r/TriangleMesh.hpp>
// There is an implementation of a hole-aware raycaster that was eventually
// not used in production version. It is now hidden under following define
@ -21,25 +22,22 @@ namespace Slic3r {
class TriangleMesh;
namespace sla {
using PointSet = Eigen::MatrixXd;
/// An index-triangle structure for libIGL functions. Also serves as an
/// alternative (raw) input format for the SLASupportTree.
// Implemented in libslic3r/SLA/Common.cpp
class IndexedMesh {
// An index-triangle structure coupled with an AABB index to support ray
// casting and other higher level operations.
class AABBMesh {
class AABBImpl;
const indexed_triangle_set* m_tm;
double m_ground_level = 0, m_gnd_offset = 0;
double m_ground_level = 0/*, m_gnd_offset = 0*/;
std::unique_ptr<AABBImpl> m_aabb;
VertexFaceIndex m_vfidx; // vertex-face index
std::vector<Vec3i> m_fnidx; // face-neighbor index
#ifdef SLIC3R_HOLE_RAYCASTER
// This holds a copy of holes in the mesh. Initialized externally
// by load_mesh setter.
std::vector<DrainHole> m_holes;
std::vector<sla::DrainHole> m_holes;
#endif
template<class M> void init(const M &mesh, bool calculate_epsilon);
@ -48,20 +46,20 @@ public:
// calculate_epsilon ... calculate epsilon for triangle-ray intersection from an average triangle edge length.
// If set to false, a default epsilon is used, which works for "reasonable" meshes.
explicit IndexedMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false);
explicit IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon = false);
explicit AABBMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false);
explicit AABBMesh(const TriangleMesh &mesh, bool calculate_epsilon = false);
IndexedMesh(const IndexedMesh& other);
IndexedMesh& operator=(const IndexedMesh&);
AABBMesh(const AABBMesh& other);
AABBMesh& operator=(const AABBMesh&);
IndexedMesh(IndexedMesh &&other);
IndexedMesh& operator=(IndexedMesh &&other);
AABBMesh(AABBMesh &&other);
AABBMesh& operator=(AABBMesh &&other);
~IndexedMesh();
~AABBMesh();
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
inline void ground_level_offset(double o) { m_gnd_offset = o; }
inline double ground_level_offset() const { return m_gnd_offset; }
inline double ground_level() const { return m_ground_level /*+ m_gnd_offset*/; }
// inline void ground_level_offset(double o) { m_gnd_offset = o; }
// inline double ground_level_offset() const { return m_gnd_offset; }
const std::vector<Vec3f>& vertices() const;
const std::vector<Vec3i>& indices() const;
@ -73,15 +71,15 @@ public:
// m_t holds a distance from m_source to the intersection.
double m_t = infty();
int m_face_id = -1;
const IndexedMesh *m_mesh = nullptr;
const AABBMesh *m_mesh = nullptr;
Vec3d m_dir;
Vec3d m_source;
Vec3d m_normal;
friend class IndexedMesh;
friend class AABBMesh;
// A valid object of this class can only be obtained from
// IndexedMesh::query_ray_hit method.
explicit inline hit_result(const IndexedMesh& em): m_mesh(&em) {}
explicit inline hit_result(const AABBMesh& em): m_mesh(&em) {}
public:
// This denotes no hit on the mesh.
static inline constexpr double infty() { return std::numeric_limits<double>::infinity(); }
@ -109,7 +107,7 @@ public:
#ifdef SLIC3R_HOLE_RAYCASTER
// Inform the object about location of holes
// creates internal copy of the vector
void load_holes(const std::vector<DrainHole>& holes) {
void load_holes(const std::vector<sla::DrainHole>& holes) {
m_holes = holes;
}
@ -118,7 +116,7 @@ public:
// This function is currently not used anywhere, it was written when the
// holes were subtracted on slices, that is, before we started using CGAL
// to actually cut the holes into the mesh.
hit_result filter_hits(const std::vector<IndexedMesh::hit_result>& obj_hits) const;
hit_result filter_hits(const std::vector<AABBMesh::hit_result>& obj_hits) const;
#endif
// Casting a ray on the mesh, returns the distance where the hit occures.
@ -138,16 +136,12 @@ public:
Vec3d normal_by_face_id(int face_id) const;
const indexed_triangle_set * get_triangle_mesh() const { return m_tm; }
const VertexFaceIndex &vertex_face_index() const { return m_vfidx; }
const std::vector<Vec3i> &face_neighbor_index() const { return m_fnidx; }
};
// Calculate the normals for the selected points (from 'points' set) on the
// mesh. This will call squared distance for each point.
PointSet normals(const PointSet& points,
const IndexedMesh& convert_mesh,
double eps = 0.05, // min distance from edges
std::function<void()> throw_on_cancel = [](){},
const std::vector<unsigned>& selected_points = {});
}} // namespace Slic3r::sla
} // namespace Slic3r::sla
#endif // INDEXEDMESH_H

View File

@ -1,11 +1,12 @@
#ifndef ASTAR_HPP
#define ASTAR_HPP
#include <cmath> // std::isinf() is here
#include <unordered_map>
#include "libslic3r/Point.hpp"
#include "libslic3r/MutablePriorityQueue.hpp"
#include <unordered_map>
namespace Slic3r { namespace astar {
// Input interface for the Astar algorithm. Specialize this struct for a
@ -34,6 +35,8 @@ template<class T> struct TracerTraits_
// Get the estimated distance heuristic from node 'n' to the destination.
// This is referred to as the h value in AStar context.
// If node 'n' is the goal, this function should return a negative value.
// Note that this heuristic should be admissible (never bigger than the real
// cost) in order for Astar to work.
static float goal_heuristic(const T &tracer, const Node &n)
{
return tracer.goal_heuristic(n);
@ -50,131 +53,136 @@ template<class T> struct TracerTraits_
template<class T>
using TracerNodeT = typename TracerTraits_<remove_cvref_t<T>>::Node;
namespace detail {
// Helper functions dispatching calls through the TracerTraits_ interface
constexpr auto Unassigned = std::numeric_limits<size_t>::max();
template<class T> using TracerTraits = TracerTraits_<remove_cvref_t<T>>;
template<class T, class Fn>
void foreach_reachable(const T &tracer, const TracerNodeT<T> &from, Fn &&fn)
template<class Tracer>
struct QNode // Queue node. Keeps track of scores g, and h
{
TracerTraits<T>::foreach_reachable(tracer, from, fn);
}
TracerNodeT<Tracer> node; // The actual node itself
size_t queue_id; // Position in the open queue or Unassigned if closed
size_t parent; // unique id of the parent or Unassigned
template<class T>
float trace_distance(const T &tracer, const TracerNodeT<T> &a, const TracerNodeT<T> &b)
{
return TracerTraits<T>::distance(tracer, a, b);
}
float g, h;
float f() const { return g + h; }
template<class T>
float goal_heuristic(const T &tracer, const TracerNodeT<T> &n)
{
return TracerTraits<T>::goal_heuristic(tracer, n);
}
template<class T>
size_t unique_id(const T &tracer, const TracerNodeT<T> &n)
{
return TracerTraits<T>::unique_id(tracer, n);
}
} // namespace astar_detail
QNode(TracerNodeT<Tracer> n = {},
size_t p = Unassigned,
float gval = std::numeric_limits<float>::infinity(),
float hval = 0.f)
: node{std::move(n)}
, parent{p}
, queue_id{InvalidQueueID}
, g{gval}
, h{hval}
{}
};
// Run the AStar algorithm on a tracer implementation.
// The 'tracer' argument encapsulates the domain (grid, point cloud, etc...)
// The 'source' argument is the starting node.
// The 'out' argument is the output iterator into which the output nodes are
// written.
// Note that no destination node is given. The tracer's goal_heuristic() method
// should return a negative value if a node is a destination node.
template<class Tracer, class It>
bool search_route(const Tracer &tracer, const TracerNodeT<Tracer> &source, It out)
// written. For performance reasons, the order is reverse, from the destination
// to the source -- (destination included, source is not).
// The 'cached_nodes' argument is an optional associative container to hold a
// QNode entry for each visited node. Any compatible container can be used
// (like std::map or maps with different allocators, even a sufficiently large
// std::vector).
//
// Note that no destination node is given in the signature. The tracer's
// goal_heuristic() method should return a negative value if a node is a
// destination node.
template<class Tracer,
class It,
class NodeMap = std::unordered_map<size_t, QNode<Tracer>>>
bool search_route(const Tracer &tracer,
const TracerNodeT<Tracer> &source,
It out,
NodeMap &&cached_nodes = {})
{
using namespace detail;
using Node = TracerNodeT<Tracer>;
enum class QueueType { Open, Closed, None };
using QNode = QNode<Tracer>;
using TracerTraits = TracerTraits_<remove_cvref_t<Tracer>>;
struct QNode // Queue node. Keeps track of scores g, and h
{
Node node; // The actual node itself
QueueType qtype = QueueType::None; // Which queue holds this node
float g = 0.f, h = 0.f;
float f() const { return g + h; }
};
// TODO: apply a linear memory allocator
using QMap = std::unordered_map<size_t, QNode>;
// The traversed nodes are stored here encapsulated in QNodes
QMap cached_nodes;
struct LessPred { // Comparison functor needed by MutablePriorityQueue
QMap &m;
struct LessPred { // Comparison functor needed by the priority queue
NodeMap &m;
bool operator ()(size_t node_a, size_t node_b) {
auto ait = m.find(node_a);
auto bit = m.find(node_b);
assert (ait != m.end() && bit != m.end());
return ait->second.f() < bit->second.f();
return m[node_a].f() < m[node_b].f();
}
};
auto qopen =
make_mutable_priority_queue<size_t, false>([](size_t, size_t){},
auto qopen = make_mutable_priority_queue<size_t, true>(
[&cached_nodes](size_t el, size_t qidx) {
cached_nodes[el].queue_id = qidx;
},
LessPred{cached_nodes});
auto qclosed =
make_mutable_priority_queue<size_t, false>([](size_t, size_t){},
LessPred{cached_nodes});
QNode initial{source, /*parent = */ Unassigned, /*g = */0.f};
size_t source_id = TracerTraits::unique_id(tracer, source);
cached_nodes[source_id] = initial;
qopen.push(source_id);
QNode initial{source, QueueType::Open};
cached_nodes.insert({unique_id(tracer, source), initial});
qopen.push(unique_id(tracer, source));
size_t goal_id = TracerTraits::goal_heuristic(tracer, source) < 0.f ?
source_id :
Unassigned;
bool goal_reached = false;
while (!goal_reached && !qopen.empty()) {
while (goal_id == Unassigned && !qopen.empty()) {
size_t q_id = qopen.top();
qopen.pop();
QNode q = cached_nodes.at(q_id);
QNode &q = cached_nodes[q_id];
foreach_reachable(tracer, q.node, [&](const Node &nd) {
if (goal_reached) return goal_reached;
// This should absolutely be initialized in the cache already
assert(!std::isinf(q.g));
TracerTraits::foreach_reachable(tracer, q.node, [&](const Node &succ_nd) {
if (goal_id != Unassigned)
return true;
float h = TracerTraits::goal_heuristic(tracer, succ_nd);
float dst = TracerTraits::distance(tracer, q.node, succ_nd);
size_t succ_id = TracerTraits::unique_id(tracer, succ_nd);
QNode qsucc_nd{succ_nd, q_id, q.g + dst, h};
float h = goal_heuristic(tracer, nd);
if (h < 0.f) {
goal_reached = true;
goal_id = succ_id;
cached_nodes[succ_id] = qsucc_nd;
} else {
float dst = trace_distance(tracer, q.node, nd);
QNode qnd{nd, QueueType::None, q.g + dst, h};
size_t qnd_id = unique_id(tracer, nd);
// If succ_id is not in cache, it gets created with g = infinity
QNode &prev_nd = cached_nodes[succ_id];
auto it = cached_nodes.find(qnd_id);
if (qsucc_nd.g < prev_nd.g) {
// new route is better, apply it:
if (it == cached_nodes.end() ||
(it->second.qtype != QueueType::None && qnd.f() < it->second.f())) {
qnd.qtype = QueueType::Open;
cached_nodes.insert_or_assign(qnd_id, qnd);
qopen.push(qnd_id);
// Save the old queue id, it would be lost after the next line
size_t queue_id = prev_nd.queue_id;
// The cache needs to be updated either way
prev_nd = qsucc_nd;
if (queue_id == InvalidQueueID)
// was in closed or unqueued, rescheduling
qopen.push(succ_id);
else // was in open, updating
qopen.update(queue_id);
}
}
return goal_reached;
return goal_id != Unassigned;
});
q.qtype = QueueType::Closed;
cached_nodes.insert_or_assign(q_id, q);
qclosed.push(q_id);
// write the output
*out = q.node;
++out;
}
return goal_reached;
// Write the output, do not reverse. Clients can do so if they need to.
if (goal_id != Unassigned) {
const QNode *q = &cached_nodes[goal_id];
while (q->parent != Unassigned) {
assert(!std::isinf(q->g)); // Uninitialized nodes are NOT allowed
*out = q->node;
++out;
q = &cached_nodes[q->parent];
}
}
return goal_id != Unassigned;
}
}} // namespace Slic3r::astar

View File

@ -231,8 +231,13 @@ static std::string appconfig_md5_hash_line(const std::string_view data)
return "# MD5 checksum " + md5_digest_str + "\n";
};
struct ConfigFileInfo {
bool correct_checksum {false};
bool contains_null {false};
};
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
static ConfigFileInfo check_config_file_and_verify_checksum(boost::nowide::ifstream &ifs)
{
auto read_whole_config_file = [&ifs]() -> std::string {
std::stringstream ss;
@ -241,7 +246,8 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
};
ifs.seekg(0, boost::nowide::ifstream::beg);
std::string whole_config = read_whole_config_file();
const std::string whole_config = read_whole_config_file();
const bool contains_null = whole_config.find_first_of('\0') != std::string::npos;
// The checksum should be on the last line in the config file.
if (size_t last_comment_pos = whole_config.find_last_of('#'); last_comment_pos != std::string::npos) {
@ -250,9 +256,9 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
// If the checksum is incorrect, then the file was either not saved correctly or modified.
if (std::string_view(whole_config.c_str() + last_comment_pos, whole_config.size() - last_comment_pos) == appconfig_md5_hash_line({ whole_config.data(), last_comment_pos }))
return true;
return {true, contains_null};
}
return false;
return {false, contains_null};
}
#endif
@ -270,14 +276,25 @@ std::string AppConfig::load(const std::string &path)
ifs.open(path);
#ifdef WIN32
// Verify the checksum of the config file without taking just for debugging purpose.
if (!verify_config_file_checksum(ifs))
BOOST_LOG_TRIVIAL(info) << "The configuration file " << path <<
" has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(ifs);
if (!config_file_info.correct_checksum)
BOOST_LOG_TRIVIAL(info)
<< "The configuration file " << path
<< " has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
if (!config_file_info.correct_checksum && config_file_info.contains_null) {
BOOST_LOG_TRIVIAL(info) << "The configuration file " + path + " is corrupted, because it is contains null characters.";
throw Slic3r::CriticalException("The configuration file contains null characters.");
}
ifs.seekg(0, boost::nowide::ifstream::beg);
#endif
try {
pt::read_ini(ifs, tree);
} catch (pt::ptree_error &ex) {
throw Slic3r::CriticalException(ex.what());
}
} catch (Slic3r::CriticalException &ex) {
#ifdef WIN32
// The configuration file is corrupted, try replacing it with the backup configuration.
ifs.close();
@ -285,28 +302,28 @@ std::string AppConfig::load(const std::string &path)
if (boost::filesystem::exists(backup_path)) {
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
boost::nowide::ifstream backup_ifs(backup_path);
if (!verify_config_file_checksum(backup_ifs)) {
BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", path, backup_path);
if (const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(backup_ifs); !config_file_info.correct_checksum || config_file_info.contains_null) {
BOOST_LOG_TRIVIAL(error) << format(R"(Both "%1%" and "%2%" are corrupted. It isn't possible to restore configuration from the backup.)", path, backup_path);
backup_ifs.close();
boost::filesystem::remove(backup_path);
} else if (std::string error_message; copy_file(backup_path, path, error_message, false) != SUCCESS) {
BOOST_LOG_TRIVIAL(error) << format("Configuration file \"%1%\" is corrupted. Failed to restore from backup \"%2%\": %3%", path, backup_path, error_message);
BOOST_LOG_TRIVIAL(error) << format(R"(Configuration file "%1%" is corrupted. Failed to restore from backup "%2%": %3%)", path, backup_path, error_message);
backup_ifs.close();
boost::filesystem::remove(backup_path);
} else {
BOOST_LOG_TRIVIAL(info) << format("Configuration file \"%1%\" was corrupted. It has been succesfully restored from the backup \"%2%\".", path, backup_path);
BOOST_LOG_TRIVIAL(info) << format(R"(Configuration file "%1%" was corrupted. It has been successfully restored from the backup "%2%".)", path, backup_path);
// Try parse configuration file after restore from backup.
try {
ifs.open(path);
pt::read_ini(ifs, tree);
recovered = true;
} catch (pt::ptree_error& ex) {
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\" after it has been restored from backup: %2%", path, ex.what());
BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%" after it has been restored from backup: %2%)", path, ex.what());
}
}
} else
#endif // WIN32
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", path, ex.what());
BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%": %2%)", path, ex.what());
if (!recovered) {
// Report the initial error of parsing PrusaSlicer.ini.
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.

View File

@ -0,0 +1,79 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include <cassert>
#include "BeadingStrategy.hpp"
#include "Point.hpp"
namespace Slic3r::Arachne
{
BeadingStrategy::BeadingStrategy(coord_t optimal_width, double wall_split_middle_threshold, double wall_add_middle_threshold, coord_t default_transition_length, float transitioning_angle)
: optimal_width(optimal_width)
, wall_split_middle_threshold(wall_split_middle_threshold)
, wall_add_middle_threshold(wall_add_middle_threshold)
, default_transition_length(default_transition_length)
, transitioning_angle(transitioning_angle)
{
name = "Unknown";
}
BeadingStrategy::BeadingStrategy(const BeadingStrategy &other)
: optimal_width(other.optimal_width)
, wall_split_middle_threshold(other.wall_split_middle_threshold)
, wall_add_middle_threshold(other.wall_add_middle_threshold)
, default_transition_length(other.default_transition_length)
, transitioning_angle(other.transitioning_angle)
, name(other.name)
{}
coord_t BeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
{
if (lower_bead_count == 0)
return scaled<coord_t>(0.01);
return default_transition_length;
}
float BeadingStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const
{
coord_t lower_optimum = getOptimalThickness(lower_bead_count);
coord_t transition_point = getTransitionThickness(lower_bead_count);
coord_t upper_optimum = getOptimalThickness(lower_bead_count + 1);
return 1.0 - float(transition_point - lower_optimum) / float(upper_optimum - lower_optimum);
}
std::vector<coord_t> BeadingStrategy::getNonlinearThicknesses(coord_t lower_bead_count) const
{
return {};
}
std::string BeadingStrategy::toString() const
{
return name;
}
double BeadingStrategy::getSplitMiddleThreshold() const
{
return wall_split_middle_threshold;
}
double BeadingStrategy::getTransitioningAngle() const
{
return transitioning_angle;
}
coord_t BeadingStrategy::getOptimalThickness(coord_t bead_count) const
{
return optimal_width * bead_count;
}
coord_t BeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
{
const coord_t lower_ideal_width = getOptimalThickness(lower_bead_count);
const coord_t higher_ideal_width = getOptimalThickness(lower_bead_count + 1);
const double threshold = lower_bead_count % 2 == 1 ? wall_split_middle_threshold : wall_add_middle_threshold;
return lower_ideal_width + threshold * (higher_ideal_width - lower_ideal_width);
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,117 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef BEADING_STRATEGY_H
#define BEADING_STRATEGY_H
#include <memory>
#include "../../libslic3r.h"
namespace Slic3r::Arachne
{
template<typename T> constexpr T pi_div(const T div) { return static_cast<T>(M_PI) / div; }
/*!
* Mostly virtual base class template.
*
* Strategy for covering a given (constant) horizontal model thickness with a number of beads.
*
* The beads may have different widths.
*
* TODO: extend with printing order?
*/
class BeadingStrategy
{
public:
/*!
* The beading for a given horizontal model thickness.
*/
struct Beading
{
coord_t total_thickness;
std::vector<coord_t> bead_widths; //! The line width of each bead from the outer inset inward
std::vector<coord_t> toolpath_locations; //! The distance of the toolpath location of each bead from the outline
coord_t left_over; //! The distance not covered by any bead; gap area.
};
BeadingStrategy(coord_t optimal_width, double wall_split_middle_threshold, double wall_add_middle_threshold, coord_t default_transition_length, float transitioning_angle = pi_div(3));
BeadingStrategy(const BeadingStrategy &other);
virtual ~BeadingStrategy() = default;
/*!
* Retrieve the bead widths with which to cover a given thickness.
*
* Requirement: Given a constant \p bead_count the output of each bead width must change gradually along with the \p thickness.
*
* \note The \p bead_count might be different from the \ref BeadingStrategy::optimal_bead_count
*/
virtual Beading compute(coord_t thickness, coord_t bead_count) const = 0;
/*!
* The ideal thickness for a given \param bead_count
*/
virtual coord_t getOptimalThickness(coord_t bead_count) const;
/*!
* The model thickness at which \ref BeadingStrategy::optimal_bead_count transitions from \p lower_bead_count to \p lower_bead_count + 1
*/
virtual coord_t getTransitionThickness(coord_t lower_bead_count) const;
/*!
* The number of beads should we ideally usefor a given model thickness
*/
virtual coord_t getOptimalBeadCount(coord_t thickness) const = 0;
/*!
* The length of the transitioning region along the marked / significant regions of the skeleton.
*
* Transitions are used to smooth out the jumps in integer bead count; the jumps turn into ramps with some incline defined by their length.
*/
virtual coord_t getTransitioningLength(coord_t lower_bead_count) const;
/*!
* The fraction of the transition length to put between the lower end of the transition and the point where the unsmoothed bead count jumps.
*
* Transitions are used to smooth out the jumps in integer bead count; the jumps turn into ramps which could be positioned relative to the jump location.
*/
virtual float getTransitionAnchorPos(coord_t lower_bead_count) const;
/*!
* Get the locations in a bead count region where \ref BeadingStrategy::compute exhibits a bend in the widths.
* Ordered from lower thickness to higher.
*
* This is used to insert extra support bones into the skeleton, so that the resulting beads in long trapezoids don't linearly change between the two ends.
*/
virtual std::vector<coord_t> getNonlinearThicknesses(coord_t lower_bead_count) const;
virtual std::string toString() const;
double getSplitMiddleThreshold() const;
double getTransitioningAngle() const;
protected:
std::string name;
coord_t optimal_width; //! Optimal bead width, nominal width off the walls in 'ideal' circumstances.
double wall_split_middle_threshold; //! Threshold when a middle wall should be split into two, as a ratio of the optimal wall width.
double wall_add_middle_threshold; //! Threshold when a new middle wall should be added between an even number of walls, as a ratio of the optimal wall width.
coord_t default_transition_length; //! The length of the region to smoothly transfer between bead counts
/*!
* The maximum angle between outline segments smaller than which we are going to add transitions
* Equals 180 - the "limit bisector angle" from the paper
*/
double transitioning_angle;
};
using BeadingStrategyPtr = std::unique_ptr<BeadingStrategy>;
} // namespace Slic3r::Arachne
#endif // BEADING_STRATEGY_H

View File

@ -0,0 +1,52 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "BeadingStrategyFactory.hpp"
#include "LimitedBeadingStrategy.hpp"
#include "WideningBeadingStrategy.hpp"
#include "DistributedBeadingStrategy.hpp"
#include "RedistributeBeadingStrategy.hpp"
#include "OuterWallInsetBeadingStrategy.hpp"
#include <limits>
#include <boost/log/trivial.hpp>
namespace Slic3r::Arachne
{
BeadingStrategyPtr BeadingStrategyFactory::makeStrategy(
const coord_t preferred_bead_width_outer,
const coord_t preferred_bead_width_inner,
const coord_t preferred_transition_length,
const float transitioning_angle,
const bool print_thin_walls,
const coord_t min_bead_width,
const coord_t min_feature_size,
const double wall_split_middle_threshold,
const double wall_add_middle_threshold,
const coord_t max_bead_count,
const coord_t outer_wall_offset,
const int inward_distributed_center_wall_count,
const double minimum_variable_line_ratio
)
{
BeadingStrategyPtr ret = std::make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, inward_distributed_center_wall_count);
BOOST_LOG_TRIVIAL(debug) << "Applying the Redistribute meta-strategy with outer-wall width = " << preferred_bead_width_outer << ", inner-wall width = " << preferred_bead_width_inner << ".";
ret = std::make_unique<RedistributeBeadingStrategy>(preferred_bead_width_outer, minimum_variable_line_ratio, std::move(ret));
if (print_thin_walls) {
BOOST_LOG_TRIVIAL(debug) << "Applying the Widening Beading meta-strategy with minimum input width " << min_feature_size << " and minimum output width " << min_bead_width << ".";
ret = std::make_unique<WideningBeadingStrategy>(std::move(ret), min_feature_size, min_bead_width);
}
if (outer_wall_offset > 0) {
BOOST_LOG_TRIVIAL(debug) << "Applying the OuterWallOffset meta-strategy with offset = " << outer_wall_offset << ".";
ret = std::make_unique<OuterWallInsetBeadingStrategy>(outer_wall_offset, std::move(ret));
}
//Apply the LimitedBeadingStrategy last, since that adds a 0-width marker wall which other beading strategies shouldn't touch.
BOOST_LOG_TRIVIAL(debug) << "Applying the Limited Beading meta-strategy with maximum bead count = " << max_bead_count << ".";
ret = std::make_unique<LimitedBeadingStrategy>(max_bead_count, std::move(ret));
return ret;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,35 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef BEADING_STRATEGY_FACTORY_H
#define BEADING_STRATEGY_FACTORY_H
#include "BeadingStrategy.hpp"
#include "../../Point.hpp"
namespace Slic3r::Arachne
{
class BeadingStrategyFactory
{
public:
static BeadingStrategyPtr makeStrategy
(
coord_t preferred_bead_width_outer = scaled<coord_t>(0.0005),
coord_t preferred_bead_width_inner = scaled<coord_t>(0.0005),
coord_t preferred_transition_length = scaled<coord_t>(0.0004),
float transitioning_angle = M_PI / 4.0,
bool print_thin_walls = false,
coord_t min_bead_width = 0,
coord_t min_feature_size = 0,
double wall_split_middle_threshold = 0.5,
double wall_add_middle_threshold = 0.5,
coord_t max_bead_count = 0,
coord_t outer_wall_offset = 0,
int inward_distributed_center_wall_count = 2,
double minimum_variable_line_width = 0.5
);
};
} // namespace Slic3r::Arachne
#endif // BEADING_STRATEGY_FACTORY_H

View File

@ -0,0 +1,82 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include <numeric>
#include "DistributedBeadingStrategy.hpp"
namespace Slic3r::Arachne
{
DistributedBeadingStrategy::DistributedBeadingStrategy(const coord_t optimal_width,
const coord_t default_transition_length,
const double transitioning_angle,
const double wall_split_middle_threshold,
const double wall_add_middle_threshold,
const int distribution_radius)
: BeadingStrategy(optimal_width, wall_split_middle_threshold, wall_add_middle_threshold, default_transition_length, transitioning_angle)
{
if(distribution_radius >= 2)
one_over_distribution_radius_squared = 1.0f / (distribution_radius - 1) * 1.0f / (distribution_radius - 1);
else
one_over_distribution_radius_squared = 1.0f / 1 * 1.0f / 1;
name = "DistributedBeadingStrategy";
}
DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
{
Beading ret;
ret.total_thickness = thickness;
if (bead_count > 2) {
const coord_t to_be_divided = thickness - bead_count * optimal_width;
const float middle = static_cast<float>(bead_count - 1) / 2;
const auto getWeight = [middle, this](coord_t bead_idx) {
const float dev_from_middle = bead_idx - middle;
return std::max(0.0f, 1.0f - one_over_distribution_radius_squared * dev_from_middle * dev_from_middle);
};
std::vector<float> weights;
weights.resize(bead_count);
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++)
weights[bead_idx] = getWeight(bead_idx);
const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f);
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) {
const float weight_fraction = weights[bead_idx] / total_weight;
const coord_t splitup_left_over_weight = to_be_divided * weight_fraction;
const coord_t width = optimal_width + splitup_left_over_weight;
if (bead_idx == 0)
ret.toolpath_locations.emplace_back(width / 2);
else
ret.toolpath_locations.emplace_back(ret.toolpath_locations.back() + (ret.bead_widths.back() + width) / 2);
ret.bead_widths.emplace_back(width);
}
ret.left_over = 0;
} else if (bead_count == 2) {
const coord_t outer_width = thickness / 2;
ret.bead_widths.emplace_back(outer_width);
ret.bead_widths.emplace_back(outer_width);
ret.toolpath_locations.emplace_back(outer_width / 2);
ret.toolpath_locations.emplace_back(thickness - outer_width / 2);
ret.left_over = 0;
} else if (bead_count == 1) {
const coord_t outer_width = thickness;
ret.bead_widths.emplace_back(outer_width);
ret.toolpath_locations.emplace_back(outer_width / 2);
ret.left_over = 0;
} else {
ret.left_over = thickness;
}
return ret;
}
coord_t DistributedBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
{
const coord_t naive_count = thickness / optimal_width; // How many lines we can fit in for sure.
const coord_t remainder = thickness - naive_count * optimal_width; // Space left after fitting that many lines.
const coord_t minimum_line_width = optimal_width * (naive_count % 2 == 1 ? wall_split_middle_threshold : wall_add_middle_threshold);
return naive_count + (remainder >= minimum_line_width); // If there's enough space, fit an extra one.
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,40 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef DISTRIBUTED_BEADING_STRATEGY_H
#define DISTRIBUTED_BEADING_STRATEGY_H
#include "BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
/*!
* This beading strategy chooses a wall count that would make the line width
* deviate the least from the optimal line width, and then distributes the lines
* evenly among the thickness available.
*/
class DistributedBeadingStrategy : public BeadingStrategy
{
protected:
float one_over_distribution_radius_squared; // (1 / distribution_radius)^2
public:
/*!
* \param distribution_radius the radius (in number of beads) over which to distribute the discrepancy between the feature size and the optimal thickness
*/
DistributedBeadingStrategy(coord_t optimal_width,
coord_t default_transition_length,
double transitioning_angle,
double wall_split_middle_threshold,
double wall_add_middle_threshold,
int distribution_radius);
~DistributedBeadingStrategy() override = default;
Beading compute(coord_t thickness, coord_t bead_count) const override;
coord_t getOptimalBeadCount(coord_t thickness) const override;
};
} // namespace Slic3r::Arachne
#endif // DISTRIBUTED_BEADING_STRATEGY_H

View File

@ -0,0 +1,126 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include <cassert>
#include <boost/log/trivial.hpp>
#include "LimitedBeadingStrategy.hpp"
#include "Point.hpp"
namespace Slic3r::Arachne
{
std::string LimitedBeadingStrategy::toString() const
{
return std::string("LimitedBeadingStrategy+") + parent->toString();
}
coord_t LimitedBeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
{
return parent->getTransitioningLength(lower_bead_count);
}
float LimitedBeadingStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const
{
return parent->getTransitionAnchorPos(lower_bead_count);
}
LimitedBeadingStrategy::LimitedBeadingStrategy(const coord_t max_bead_count, BeadingStrategyPtr parent)
: BeadingStrategy(*parent)
, max_bead_count(max_bead_count)
, parent(std::move(parent))
{
if (max_bead_count % 2 == 1)
{
BOOST_LOG_TRIVIAL(warning) << "LimitedBeadingStrategy with odd bead count is odd indeed!";
}
}
LimitedBeadingStrategy::Beading LimitedBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
{
if (bead_count <= max_bead_count)
{
Beading ret = parent->compute(thickness, bead_count);
bead_count = ret.toolpath_locations.size();
if (bead_count % 2 == 0 && bead_count == max_bead_count)
{
const coord_t innermost_toolpath_location = ret.toolpath_locations[max_bead_count / 2 - 1];
const coord_t innermost_toolpath_width = ret.bead_widths[max_bead_count / 2 - 1];
ret.toolpath_locations.insert(ret.toolpath_locations.begin() + max_bead_count / 2, innermost_toolpath_location + innermost_toolpath_width / 2);
ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, 0);
}
return ret;
}
assert(bead_count == max_bead_count + 1);
if(bead_count != max_bead_count + 1)
{
BOOST_LOG_TRIVIAL(warning) << "Too many beads! " << bead_count << " != " << max_bead_count + 1;
}
coord_t optimal_thickness = parent->getOptimalThickness(max_bead_count);
Beading ret = parent->compute(optimal_thickness, max_bead_count);
bead_count = ret.toolpath_locations.size();
ret.left_over += thickness - ret.total_thickness;
ret.total_thickness = thickness;
// Enforce symmetry
if (bead_count % 2 == 1) {
ret.toolpath_locations[bead_count / 2] = thickness / 2;
ret.bead_widths[bead_count / 2] = thickness - optimal_thickness;
}
for (coord_t bead_idx = 0; bead_idx < (bead_count + 1) / 2; bead_idx++)
ret.toolpath_locations[bead_count - 1 - bead_idx] = thickness - ret.toolpath_locations[bead_idx];
//Create a "fake" inner wall with 0 width to indicate the edge of the walled area.
//This wall can then be used by other structures to e.g. fill the infill area adjacent to the variable-width walls.
coord_t innermost_toolpath_location = ret.toolpath_locations[max_bead_count / 2 - 1];
coord_t innermost_toolpath_width = ret.bead_widths[max_bead_count / 2 - 1];
ret.toolpath_locations.insert(ret.toolpath_locations.begin() + max_bead_count / 2, innermost_toolpath_location + innermost_toolpath_width / 2);
ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, 0);
//Symmetry on both sides. Symmetry is guaranteed since this code is stopped early if the bead_count <= max_bead_count, and never reaches this point then.
const size_t opposite_bead = bead_count - (max_bead_count / 2 - 1);
innermost_toolpath_location = ret.toolpath_locations[opposite_bead];
innermost_toolpath_width = ret.bead_widths[opposite_bead];
ret.toolpath_locations.insert(ret.toolpath_locations.begin() + opposite_bead, innermost_toolpath_location - innermost_toolpath_width / 2);
ret.bead_widths.insert(ret.bead_widths.begin() + opposite_bead, 0);
return ret;
}
coord_t LimitedBeadingStrategy::getOptimalThickness(coord_t bead_count) const
{
if (bead_count <= max_bead_count)
return parent->getOptimalThickness(bead_count);
assert(false);
return scaled<coord_t>(1000.); // 1 meter (Cura was returning 10 meter)
}
coord_t LimitedBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
{
if (lower_bead_count < max_bead_count)
return parent->getTransitionThickness(lower_bead_count);
if (lower_bead_count == max_bead_count)
return parent->getOptimalThickness(lower_bead_count + 1) - scaled<coord_t>(0.01);
assert(false);
return scaled<coord_t>(900.); // 0.9 meter;
}
coord_t LimitedBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
{
coord_t parent_bead_count = parent->getOptimalBeadCount(thickness);
if (parent_bead_count <= max_bead_count) {
return parent->getOptimalBeadCount(thickness);
} else if (parent_bead_count == max_bead_count + 1) {
if (thickness < parent->getOptimalThickness(max_bead_count + 1) - scaled<coord_t>(0.01))
return max_bead_count;
else
return max_bead_count + 1;
}
else return max_bead_count + 1;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,49 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef LIMITED_BEADING_STRATEGY_H
#define LIMITED_BEADING_STRATEGY_H
#include "BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
/*!
* This is a meta-strategy that can be applied on top of any other beading
* strategy, which limits the thickness of the walls to the thickness that the
* lines can reasonably print.
*
* The width of the wall is limited to the maximum number of contours times the
* maximum width of each of these contours.
*
* If the width of the wall gets limited, this strategy outputs one additional
* bead with 0 width. This bead is used to denote the limits of the walled area.
* Other structures can then use this border to align their structures to, such
* as to create correctly overlapping infill or skin, or to align the infill
* pattern to any extra infill walls.
*/
class LimitedBeadingStrategy : public BeadingStrategy
{
public:
LimitedBeadingStrategy(coord_t max_bead_count, BeadingStrategyPtr parent);
~LimitedBeadingStrategy() override = default;
Beading compute(coord_t thickness, coord_t bead_count) const override;
coord_t getOptimalThickness(coord_t bead_count) const override;
coord_t getTransitionThickness(coord_t lower_bead_count) const override;
coord_t getOptimalBeadCount(coord_t thickness) const override;
std::string toString() const override;
coord_t getTransitioningLength(coord_t lower_bead_count) const override;
float getTransitionAnchorPos(coord_t lower_bead_count) const override;
protected:
const coord_t max_bead_count;
const BeadingStrategyPtr parent;
};
} // namespace Slic3r::Arachne
#endif // LIMITED_DISTRIBUTED_BEADING_STRATEGY_H

View File

@ -0,0 +1,59 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "OuterWallInsetBeadingStrategy.hpp"
#include <algorithm>
namespace Slic3r::Arachne
{
OuterWallInsetBeadingStrategy::OuterWallInsetBeadingStrategy(coord_t outer_wall_offset, BeadingStrategyPtr parent)
: BeadingStrategy(*parent), parent(std::move(parent)), outer_wall_offset(outer_wall_offset)
{
name = "OuterWallOfsetBeadingStrategy";
}
coord_t OuterWallInsetBeadingStrategy::getOptimalThickness(coord_t bead_count) const
{
return parent->getOptimalThickness(bead_count);
}
coord_t OuterWallInsetBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
{
return parent->getTransitionThickness(lower_bead_count);
}
coord_t OuterWallInsetBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
{
return parent->getOptimalBeadCount(thickness);
}
coord_t OuterWallInsetBeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
{
return parent->getTransitioningLength(lower_bead_count);
}
std::string OuterWallInsetBeadingStrategy::toString() const
{
return std::string("OuterWallOfsetBeadingStrategy+") + parent->toString();
}
BeadingStrategy::Beading OuterWallInsetBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
{
Beading ret = parent->compute(thickness, bead_count);
// Actual count and thickness as represented by extant walls. Don't count any potential zero-width 'signaling' walls.
bead_count = std::count_if(ret.bead_widths.begin(), ret.bead_widths.end(), [](const coord_t width) { return width > 0; });
// No need to apply any inset if there is just a single wall.
if (bead_count < 2)
{
return ret;
}
// Actually move the outer wall inside. Ensure that the outer wall never goes beyond the middle line.
ret.toolpath_locations[0] = std::min(ret.toolpath_locations[0] + outer_wall_offset, thickness / 2);
return ret;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,35 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef OUTER_WALL_INSET_BEADING_STRATEGY_H
#define OUTER_WALL_INSET_BEADING_STRATEGY_H
#include "BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
/*
* This is a meta strategy that allows for the outer wall to be inset towards the inside of the model.
*/
class OuterWallInsetBeadingStrategy : public BeadingStrategy
{
public:
OuterWallInsetBeadingStrategy(coord_t outer_wall_offset, BeadingStrategyPtr parent);
~OuterWallInsetBeadingStrategy() override = default;
Beading compute(coord_t thickness, coord_t bead_count) const override;
coord_t getOptimalThickness(coord_t bead_count) const override;
coord_t getTransitionThickness(coord_t lower_bead_count) const override;
coord_t getOptimalBeadCount(coord_t thickness) const override;
coord_t getTransitioningLength(coord_t lower_bead_count) const override;
std::string toString() const override;
private:
BeadingStrategyPtr parent;
coord_t outer_wall_offset;
};
} // namespace Slic3r::Arachne
#endif // OUTER_WALL_INSET_BEADING_STRATEGY_H

View File

@ -0,0 +1,97 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "RedistributeBeadingStrategy.hpp"
#include <algorithm>
#include <numeric>
namespace Slic3r::Arachne
{
RedistributeBeadingStrategy::RedistributeBeadingStrategy(const coord_t optimal_width_outer,
const double minimum_variable_line_ratio,
BeadingStrategyPtr parent)
: BeadingStrategy(*parent)
, parent(std::move(parent))
, optimal_width_outer(optimal_width_outer)
, minimum_variable_line_ratio(minimum_variable_line_ratio)
{
name = "RedistributeBeadingStrategy";
}
coord_t RedistributeBeadingStrategy::getOptimalThickness(coord_t bead_count) const
{
const coord_t inner_bead_count = std::max(static_cast<coord_t>(0), bead_count - 2);
const coord_t outer_bead_count = bead_count - inner_bead_count;
return parent->getOptimalThickness(inner_bead_count) + optimal_width_outer * outer_bead_count;
}
coord_t RedistributeBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
{
switch (lower_bead_count) {
case 0: return minimum_variable_line_ratio * optimal_width_outer;
case 1: return (1.0 + parent->getSplitMiddleThreshold()) * optimal_width_outer;
default: return parent->getTransitionThickness(lower_bead_count - 2) + 2 * optimal_width_outer;
}
}
coord_t RedistributeBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
{
if (thickness < minimum_variable_line_ratio * optimal_width_outer)
return 0;
if (thickness <= 2 * optimal_width_outer)
return thickness > (1.0 + parent->getSplitMiddleThreshold()) * optimal_width_outer ? 2 : 1;
return parent->getOptimalBeadCount(thickness - 2 * optimal_width_outer) + 2;
}
coord_t RedistributeBeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
{
return parent->getTransitioningLength(lower_bead_count);
}
float RedistributeBeadingStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const
{
return parent->getTransitionAnchorPos(lower_bead_count);
}
std::string RedistributeBeadingStrategy::toString() const
{
return std::string("RedistributeBeadingStrategy+") + parent->toString();
}
BeadingStrategy::Beading RedistributeBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
{
Beading ret;
// Take care of all situations in which no lines are actually produced:
if (bead_count == 0 || thickness < minimum_variable_line_ratio * optimal_width_outer) {
ret.left_over = thickness;
ret.total_thickness = thickness;
return ret;
}
// Compute the beadings of the inner walls, if any:
const coord_t inner_bead_count = bead_count - 2;
const coord_t inner_thickness = thickness - 2 * optimal_width_outer;
if (inner_bead_count > 0 && inner_thickness > 0) {
ret = parent->compute(inner_thickness, inner_bead_count);
for (auto &toolpath_location : ret.toolpath_locations) toolpath_location += optimal_width_outer;
}
// Insert the outer wall(s) around the previously computed inner wall(s), which may be empty:
const coord_t actual_outer_thickness = bead_count > 2 ? std::min(thickness / 2, optimal_width_outer) : thickness / bead_count;
ret.bead_widths.insert(ret.bead_widths.begin(), actual_outer_thickness);
ret.toolpath_locations.insert(ret.toolpath_locations.begin(), actual_outer_thickness / 2);
if (bead_count > 1) {
ret.bead_widths.push_back(actual_outer_thickness);
ret.toolpath_locations.push_back(thickness - actual_outer_thickness / 2);
}
// Ensure correct total and left over thickness.
ret.total_thickness = thickness;
ret.left_over = thickness - std::accumulate(ret.bead_widths.cbegin(), ret.bead_widths.cend(), static_cast<coord_t>(0));
return ret;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,56 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef REDISTRIBUTE_DISTRIBUTED_BEADING_STRATEGY_H
#define REDISTRIBUTE_DISTRIBUTED_BEADING_STRATEGY_H
#include "BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
/*!
* A meta-beading-strategy that takes outer and inner wall widths into account.
*
* The outer wall will try to keep a constant width by only applying the beading strategy on the inner walls. This
* ensures that this outer wall doesn't react to changes happening to inner walls. It will limit print artifacts on
* the surface of the print. Although this strategy technically deviates from the original philosophy of the paper.
* It will generally results in better prints because of a smoother motion and less variation in extrusion width in
* the outer walls.
*
* If the thickness of the model is less then two times the optimal outer wall width and once the minimum inner wall
* width it will keep the minimum inner wall at a minimum constant and vary the outer wall widths symmetrical. Until
* The thickness of the model is that of at least twice the optimal outer wall width it will then use two
* symmetrical outer walls only. Until it transitions into a single outer wall. These last scenario's are always
* symmetrical in nature, disregarding the user specified strategy.
*/
class RedistributeBeadingStrategy : public BeadingStrategy
{
public:
/*!
* /param optimal_width_outer Outer wall width, guaranteed to be the actual (save rounding errors) at a
* bead count if the parent strategies' optimum bead width is a weighted
* average of the outer and inner walls at that bead count.
* /param minimum_variable_line_ratio Minimum factor that the variable line might deviate from the optimal width.
*/
RedistributeBeadingStrategy(coord_t optimal_width_outer, double minimum_variable_line_ratio, BeadingStrategyPtr parent);
~RedistributeBeadingStrategy() override = default;
Beading compute(coord_t thickness, coord_t bead_count) const override;
coord_t getOptimalThickness(coord_t bead_count) const override;
coord_t getTransitionThickness(coord_t lower_bead_count) const override;
coord_t getOptimalBeadCount(coord_t thickness) const override;
coord_t getTransitioningLength(coord_t lower_bead_count) const override;
float getTransitionAnchorPos(coord_t lower_bead_count) const override;
std::string toString() const override;
protected:
BeadingStrategyPtr parent;
coord_t optimal_width_outer;
double minimum_variable_line_ratio;
};
} // namespace Slic3r::Arachne
#endif // INWARD_DISTRIBUTED_BEADING_STRATEGY_H

View File

@ -0,0 +1,82 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "WideningBeadingStrategy.hpp"
namespace Slic3r::Arachne
{
WideningBeadingStrategy::WideningBeadingStrategy(BeadingStrategyPtr parent, const coord_t min_input_width, const coord_t min_output_width)
: BeadingStrategy(*parent)
, parent(std::move(parent))
, min_input_width(min_input_width)
, min_output_width(min_output_width)
{
}
std::string WideningBeadingStrategy::toString() const
{
return std::string("Widening+") + parent->toString();
}
WideningBeadingStrategy::Beading WideningBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
{
if (thickness < optimal_width) {
Beading ret;
ret.total_thickness = thickness;
if (thickness >= min_input_width)
{
ret.bead_widths.emplace_back(std::max(thickness, min_output_width));
ret.toolpath_locations.emplace_back(thickness / 2);
} else {
ret.left_over = thickness;
}
return ret;
} else {
return parent->compute(thickness, bead_count);
}
}
coord_t WideningBeadingStrategy::getOptimalThickness(coord_t bead_count) const
{
return parent->getOptimalThickness(bead_count);
}
coord_t WideningBeadingStrategy::getTransitionThickness(coord_t lower_bead_count) const
{
if (lower_bead_count == 0)
return min_input_width;
else
return parent->getTransitionThickness(lower_bead_count);
}
coord_t WideningBeadingStrategy::getOptimalBeadCount(coord_t thickness) const
{
if (thickness < min_input_width)
return 0;
coord_t ret = parent->getOptimalBeadCount(thickness);
if (thickness >= min_input_width && ret < 1)
return 1;
return ret;
}
coord_t WideningBeadingStrategy::getTransitioningLength(coord_t lower_bead_count) const
{
return parent->getTransitioningLength(lower_bead_count);
}
float WideningBeadingStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const
{
return parent->getTransitionAnchorPos(lower_bead_count);
}
std::vector<coord_t> WideningBeadingStrategy::getNonlinearThicknesses(coord_t lower_bead_count) const
{
std::vector<coord_t> ret;
ret.emplace_back(min_output_width);
std::vector<coord_t> pret = parent->getNonlinearThicknesses(lower_bead_count);
ret.insert(ret.end(), pret.begin(), pret.end());
return ret;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,46 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef WIDENING_BEADING_STRATEGY_H
#define WIDENING_BEADING_STRATEGY_H
#include "BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
/*!
* This is a meta-strategy that can be applied on any other beading strategy. If
* the part is thinner than a single line, this strategy adjusts the part so
* that it becomes the minimum thickness of one line.
*
* This way, tiny pieces that are smaller than a single line will still be
* printed.
*/
class WideningBeadingStrategy : public BeadingStrategy
{
public:
/*!
* Takes responsibility for deleting \param parent
*/
WideningBeadingStrategy(BeadingStrategyPtr parent, coord_t min_input_width, coord_t min_output_width);
~WideningBeadingStrategy() override = default;
Beading compute(coord_t thickness, coord_t bead_count) const override;
coord_t getOptimalThickness(coord_t bead_count) const override;
coord_t getTransitionThickness(coord_t lower_bead_count) const override;
coord_t getOptimalBeadCount(coord_t thickness) const override;
coord_t getTransitioningLength(coord_t lower_bead_count) const override;
float getTransitionAnchorPos(coord_t lower_bead_count) const override;
std::vector<coord_t> getNonlinearThicknesses(coord_t lower_bead_count) const override;
std::string toString() const override;
protected:
BeadingStrategyPtr parent;
const coord_t min_input_width;
const coord_t min_output_width;
};
} // namespace Slic3r::Arachne
#endif // WIDENING_BEADING_STRATEGY_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,595 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef SKELETAL_TRAPEZOIDATION_H
#define SKELETAL_TRAPEZOIDATION_H
#include <boost/polygon/voronoi.hpp>
#include <memory> // smart pointers
#include <unordered_map>
#include <utility> // pair
#include "utils/HalfEdgeGraph.hpp"
#include "utils/PolygonsSegmentIndex.hpp"
#include "utils/ExtrusionJunction.hpp"
#include "utils/ExtrusionLine.hpp"
#include "SkeletalTrapezoidationEdge.hpp"
#include "SkeletalTrapezoidationJoint.hpp"
#include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp"
#include "SkeletalTrapezoidationGraph.hpp"
namespace Slic3r::Arachne
{
/*!
* Main class of the dynamic beading strategies.
*
* The input polygon region is decomposed into trapezoids and represented as a half-edge data-structure.
*
* We determine which edges are 'central' accordinding to the transitioning_angle of the beading strategy,
* and determine the bead count for these central regions and apply them outward when generating toolpaths. [oversimplified]
*
* The method can be visually explained as generating the 3D union of cones surface on the outline polygons,
* and changing the heights along central regions of that surface so that they are flat.
* For more info, please consult the paper "A framework for adaptive width control of dense contour-parallel toolpaths in fused
deposition modeling" by Kuipers et al.
* This visual explanation aid explains the use of "upward", "lower" etc,
* i.e. the radial distance and/or the bead count are used as heights of this visualization, there is no coordinate called 'Z'.
*
* TODO: split this class into two:
* 1. Class for generating the decomposition and aux functions for performing updates
* 2. Class for editing the structure for our purposes.
*/
class SkeletalTrapezoidation
{
using pos_t = double;
using vd_t = boost::polygon::voronoi_diagram<pos_t>;
using graph_t = SkeletalTrapezoidationGraph;
using edge_t = STHalfEdge;
using node_t = STHalfEdgeNode;
using Beading = BeadingStrategy::Beading;
using BeadingPropagation = SkeletalTrapezoidationJoint::BeadingPropagation;
using TransitionMiddle = SkeletalTrapezoidationEdge::TransitionMiddle;
using TransitionEnd = SkeletalTrapezoidationEdge::TransitionEnd;
template<typename T>
using ptr_vector_t = std::vector<std::shared_ptr<T>>;
double transitioning_angle; //!< How pointy a region should be before we apply the method. Equals 180* - limit_bisector_angle
coord_t discretization_step_size; //!< approximate size of segments when parabolic VD edges get discretized (and vertex-vertex edges)
coord_t transition_filter_dist; //!< Filter transition mids (i.e. anchors) closer together than this
coord_t allowed_filter_deviation; //!< The allowed line width deviation induced by filtering
coord_t beading_propagation_transition_dist; //!< When there are different beadings propagated from below and from above, use this transitioning distance
static constexpr coord_t central_filter_dist = scaled<coord_t>(0.02); //!< Filter areas marked as 'central' smaller than this
static constexpr coord_t snap_dist = scaled<coord_t>(0.02); //!< Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge.
/*!
* The strategy to use to fill a certain shape with lines.
*
* Various BeadingStrategies are available that differ in which lines get to
* print at their optimal width, where the play is being compensated, and
* how the joints are handled where we transition to different numbers of
* lines.
*/
const BeadingStrategy& beading_strategy;
public:
using Segment = PolygonsSegmentIndex;
/*!
* Construct a new trapezoidation problem to solve.
* \param polys The shapes to fill with walls.
* \param beading_strategy The strategy to use to fill these shapes.
* \param transitioning_angle Where we transition to a different number of
* walls, how steep should this transition be? A lower angle means that the
* transition will be longer.
* \param discretization_step_size Since g-code can't represent smooth
* transitions in line width, the line width must change with discretized
* steps. This indicates how long the line segments between those steps will
* be.
* \param transition_filter_dist The minimum length of transitions.
* Transitions shorter than this will be considered for dissolution.
* \param beading_propagation_transition_dist When there are different
* beadings propagated from below and from above, use this transitioning
* distance.
*/
SkeletalTrapezoidation(const Polygons& polys,
const BeadingStrategy& beading_strategy,
double transitioning_angle
, coord_t discretization_step_size
, coord_t transition_filter_dist
, coord_t allowed_filter_deviation
, coord_t beading_propagation_transition_dist);
/*!
* A skeletal graph through the polygons that we need to fill with beads.
*
* The skeletal graph represents the medial axes through each part of the
* polygons, and the lines from these medial axes towards each vertex of the
* polygons. The graph can be used to see what the width is of a polygon in
* each place and where the width transitions.
*/
graph_t graph;
/*!
* Generate the paths that the printer must extrude, to print the outlines
* in the input polygons.
* \param filter_outermost_central_edges Some edges are "central" but still
* touch the outside of the polygon. If enabled, don't treat these as
* "central" but as if it's a obtuse corner. As a result, sharp corners will
* no longer end in a single line but will just loop.
*/
void generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges = false);
protected:
/*!
* Auxiliary for referencing one transition along an edge which may contain multiple transitions
*/
struct TransitionMidRef
{
edge_t* edge;
std::list<TransitionMiddle>::iterator transition_it;
TransitionMidRef(edge_t* edge, std::list<TransitionMiddle>::iterator transition_it)
: edge(edge)
, transition_it(transition_it)
{}
};
/*!
* Compute the skeletal trapezoidation decomposition of the input shape.
*
* Compute the Voronoi Diagram (VD) and transfer all inside edges into our half-edge (HE) datastructure.
*
* The algorithm is currently a bit overcomplicated, because the discretization of parabolic edges is performed at the same time as all edges are being transfered,
* which means that there is no one-to-one mapping from VD edges to HE edges.
* Instead we map from a VD edge to the last HE edge.
* This could be cimplified by recording the edges which should be discretized and discretizing the mafterwards.
*
* Another complication arises because the VD uses floating logic, which can result in zero-length segments after rounding to integers.
* We therefore collapse edges and their whole cells afterwards.
*/
void constructFromPolygons(const Polygons& polys);
/*!
* mapping each voronoi VD edge to the corresponding halfedge HE edge
* In case the result segment is discretized, we map the VD edge to the *last* HE edge
*/
std::unordered_map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge;
std::unordered_map<vd_t::vertex_type*, node_t*> vd_node_to_he_node;
node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
/*!
* (Eventual) returned 'polylines per index' result (from generateToolpaths):
*/
std::vector<VariableWidthLines> *p_generated_toolpaths;
/*!
* Transfer an edge from the VD to the HE and perform discretization of parabolic edges (and vertex-vertex edges)
* \p prev_edge serves as input and output. May be null as input.
*/
void transferEdge(Point from, Point to, vd_t::edge_type& vd_edge, edge_t*& prev_edge, Point& start_source_point, Point& end_source_point, const std::vector<Segment>& segments);
/*!
* Discretize a Voronoi edge that represents the medial axis of a vertex-
* line region or vertex-vertex region into small segments that can be
* considered to have a straight medial axis and a linear line width
* transition.
*
* The medial axis between a point and a line is a parabola. The rest of the
* algorithm doesn't want to have to deal with parabola, so this discretises
* the parabola into straight line segments. This is necessary if there is a
* sharp inner corner (acts as a point) that comes close to a straight edge.
*
* The medial axis between a point and a point is a straight line segment.
* However the distance from the medial axis to either of those points draws
* a parabola as you go along the medial axis. That means that the resulting
* line width along the medial axis would not be linearly increasing or
* linearly decreasing, but needs to take the shape of a parabola. Instead,
* we'll break this edge up into tiny line segments that can approximate the
* parabola with tiny linear increases or decreases in line width.
* \param segment The variable-width Voronoi edge to discretize.
* \param points All vertices of the original Polygons to fill with beads.
* \param segments All line segments of the original Polygons to fill with
* beads.
* \return A number of coordinates along the edge where the edge is broken
* up into discrete pieces.
*/
std::vector<Point> discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments);
/*!
* Compute the range of line segments that surround a cell of the skeletal
* graph that belongs to a point on the medial axis.
*
* This should only be used on cells that belong to a corner in the skeletal
* graph, e.g. triangular cells, not trapezoid cells.
*
* The resulting line segments is just the first and the last segment. They
* are linked to the neighboring segments, so you can iterate over the
* segments until you reach the last segment.
* \param cell The cell to compute the range of line segments for.
* \param[out] start_source_point The start point of the source segment of
* this cell.
* \param[out] end_source_point The end point of the source segment of this
* cell.
* \param[out] starting_vd_edge The edge of the Voronoi diagram where the
* loop around the cell starts.
* \param[out] ending_vd_edge The edge of the Voronoi diagram where the loop
* around the cell ends.
* \param points All vertices of the input Polygons.
* \param segments All edges of the input Polygons.
* /return Whether the cell is inside of the polygon. If it's outside of the
* polygon we should skip processing it altogether.
*/
bool computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
/*!
* Compute the range of line segments that surround a cell of the skeletal
* graph that belongs to a line segment of the medial axis.
*
* This should only be used on cells that belong to a central line segment
* of the skeletal graph, e.g. trapezoid cells, not triangular cells.
*
* The resulting line segments is just the first and the last segment. They
* are linked to the neighboring segments, so you can iterate over the
* segments until you reach the last segment.
* \param cell The cell to compute the range of line segments for.
* \param[out] start_source_point The start point of the source segment of
* this cell.
* \param[out] end_source_point The end point of the source segment of this
* cell.
* \param[out] starting_vd_edge The edge of the Voronoi diagram where the
* loop around the cell starts.
* \param[out] ending_vd_edge The edge of the Voronoi diagram where the loop
* around the cell ends.
* \param points All vertices of the input Polygons.
* \param segments All edges of the input Polygons.
* /return Whether the cell is inside of the polygon. If it's outside of the
* polygon we should skip processing it altogether.
*/
void computeSegmentCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
/*!
* For VD cells associated with an input polygon vertex, we need to separate the node at the end and start of the cell into two
* That way we can reach both the quad_start and the quad_end from the [incident_edge] of the two new nodes
* Otherwise if node.incident_edge = quad_start you couldnt reach quad_end.twin by normal iteration (i.e. it = it.twin.next)
*/
void separatePointyQuadEndNodes();
// ^ init | v transitioning
void updateIsCentral(); // Update the "is_central" flag for each edge based on the transitioning_angle
/*!
* Filter out small central areas.
*
* Only used to get rid of small edges which get marked as central because
* of rounding errors because the region is so small.
*/
void filterCentral(coord_t max_length);
/*!
* Filter central areas connected to starting_edge recursively.
* \return Whether we should unmark this section marked as central, on the
* way back out of the recursion.
*/
bool filterCentral(edge_t* starting_edge, coord_t traveled_dist, coord_t max_length);
/*!
* Unmark the outermost edges directly connected to the outline, as not
* being central.
*
* Only used to emulate some related literature.
*
* The paper shows that this function is bad for the stability of the framework.
*/
void filterOuterCentral();
/*!
* Set bead count in central regions based on the optimal_bead_count of the
* beading strategy.
*/
void updateBeadCount();
/*!
* Add central regions and set bead counts where there is an end of the
* central area and when traveling upward we get to another region with the
* same bead count.
*/
void filterNoncentralRegions();
/*!
* Add central regions and set bead counts for a particular edge and all of
* its adjacent edges.
*
* Recursive subroutine for \ref filterNoncentralRegions().
* \return Whether to set the bead count on the way back
*/
bool filterNoncentralRegions(edge_t* to_edge, coord_t bead_count, coord_t traveled_dist, coord_t max_dist);
/*!
* Generate middle points of all transitions on edges.
*
* The transition middle points are saved in the graph itself. They are also
* returned via the output parameter.
* \param[out] edge_transitions A list of transitions that were generated.
*/
void generateTransitionMids(ptr_vector_t<std::list<TransitionMiddle>>& edge_transitions);
/*!
* Removes some transition middle points.
*
* Transitions can be removed if there are multiple intersecting transitions
* that are too close together. If transitions have opposite effects, both
* are removed.
*/
void filterTransitionMids();
/*!
* Merge transitions that are too close together.
* \param edge_to_start Edge pointing to the node from which to start
* traveling in all directions except along \p edge_to_start .
* \param origin_transition The transition for which we are checking nearby
* transitions.
* \param traveled_dist The distance traveled before we came to
* \p edge_to_start.to .
* \param going_up Whether we are traveling in the upward direction as seen
* from the \p origin_transition. If this doesn't align with the direction
* according to the R diff on a consecutive edge we know there was a local
* optimum.
* \return Whether the origin transition should be dissolved.
*/
std::list<TransitionMidRef> dissolveNearbyTransitions(edge_t* edge_to_start, TransitionMiddle& origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up);
/*!
* Spread a certain bead count over a region in the graph.
* \param edge_to_start One edge of the region to spread the bead count in.
* \param from_bead_count All edges with this bead count will be changed.
* \param to_bead_count The new bead count for those edges.
*/
void dissolveBeadCountRegion(edge_t* edge_to_start, coord_t from_bead_count, coord_t to_bead_count);
/*!
* Change the bead count if the given edge is at the end of a central
* region.
*
* This is necessary to provide a transitioning bead count to the edges of a
* central region to transition more smoothly from a high bead count in the
* central region to a lower bead count at the edge.
* \param edge_to_start One edge from a zone that needs to be filtered.
* \param traveled_dist The distance along the edges we've traveled so far.
* \param max_distance Don't filter beyond this range.
* \param replacing_bead_count The new bead count for this region.
* \return ``true`` if the bead count of this edge was changed.
*/
bool filterEndOfCentralTransition(edge_t* edge_to_start, coord_t traveled_dist, coord_t max_dist, coord_t replacing_bead_count);
/*!
* Generate the endpoints of all transitions for all edges in the graph.
* \param[out] edge_transition_ends The resulting transition endpoints.
*/
void generateAllTransitionEnds(ptr_vector_t<std::list<TransitionEnd>>& edge_transition_ends);
/*!
* Also set the rest values at nodes in between the transition ends
*/
void applyTransitions(ptr_vector_t<std::list<TransitionEnd>>& edge_transition_ends);
/*!
* Create extra edges along all edges, where it needs to transition from one
* bead count to another.
*
* For example, if an edge of the graph goes from a bead count of 6 to a
* bead count of 1, it needs to generate 5 places where the beads around
* this line transition to a lower bead count. These are the "ribs". They
* reach from the edge to the border of the polygon. Where the beads hit
* those ribs the beads know to make a transition.
*/
void generateTransitioningRibs();
/*!
* Generate the endpoints of a specific transition midpoint.
* \param edge The edge to create transitions on.
* \param mid_R The radius of the transition middle point.
* \param transition_lower_bead_count The bead count at the lower end of the
* transition.
* \param[out] edge_transition_ends A list of endpoints to add the new
* endpoints to.
*/
void generateTransitionEnds(edge_t& edge, coord_t mid_R, coord_t transition_lower_bead_count, ptr_vector_t<std::list<TransitionEnd>>& edge_transition_ends);
/*!
* Compute a single endpoint of a transition.
* \param edge The edge to generate the endpoint for.
* \param start_pos The position where the transition starts.
* \param end_pos The position where the transition ends on the other side.
* \param transition_half_length The distance to the transition middle
* point.
* \param start_rest The gap between the start of the transition and the
* starting endpoint, as ratio of the inner bead width at the high end of
* the transition.
* \param end_rest The gap between the end of the transition and the ending
* endpoint, as ratio of the inner bead width at the high end of the
* transition.
* \param transition_lower_bead_count The bead count at the lower end of the
* transition.
* \param[out] edge_transition_ends The list to put the resulting endpoints
* in.
* \return Whether the given edge is going downward (i.e. towards a thinner
* region of the polygon).
*/
bool generateTransitionEnd(edge_t& edge, coord_t start_pos, coord_t end_pos, coord_t transition_half_length, double start_rest, double end_rest, coord_t transition_lower_bead_count, ptr_vector_t<std::list<TransitionEnd>>& edge_transition_ends);
/*!
* Determines whether an edge is going downwards or upwards in the graph.
*
* An edge is said to go "downwards" if it's going towards a narrower part
* of the polygon. The notion of "downwards" comes from the conical
* representation of the graph, where the polygon is filled with a cone of
* maximum radius.
*
* This function works by recursively checking adjacent edges until the edge
* is reached.
* \param outgoing The edge to check.
* \param traveled_dist The distance traversed so far.
* \param transition_half_length The radius of the transition width.
* \param lower_bead_count The bead count at the lower end of the edge.
* \return ``true`` if this edge is going down, or ``false`` if it's going
* up.
*/
bool isGoingDown(edge_t* outgoing, coord_t traveled_dist, coord_t transition_half_length, coord_t lower_bead_count) const;
/*!
* Determines whether this edge marks the end of the central region.
* \param edge The edge to check.
* \return ``true`` if this edge goes from a central region to a non-central
* region, or ``false`` in every other case (central to central, non-central
* to non-central, non-central to central, or end-of-the-line).
*/
bool isEndOfCentral(const edge_t& edge) const;
/*!
* Create extra ribs in the graph where the graph contains a parabolic arc
* or a straight between two inner corners.
*
* There might be transitions there as the beads go through a narrow
* bottleneck in the polygon.
*/
void generateExtraRibs();
// ^ transitioning ^
// v toolpath generation v
/*!
* \param[out] segments the generated segments
*/
void generateSegments();
/*!
* From a quad (a group of linked edges in one cell of the Voronoi), find
* the edge pointing to the node that is furthest away from the border of the polygon.
* \param quad_start_edge The first edge of the quad.
* \return The edge of the quad that is furthest away from the border.
*/
edge_t* getQuadMaxRedgeTo(edge_t* quad_start_edge);
/*!
* Propagate beading information from nodes that are closer to the edge
* (low radius R) to nodes that are farther from the edge (high R).
*
* only propagate from nodes with beading info upward to nodes without beading info
*
* Edges are sorted by their radius, so that we can do a depth-first walk
* without employing a recursive algorithm.
*
* In upward propagated beadings we store the distance traveled, so that we can merge these beadings with the downward propagated beadings in \ref propagateBeadingsDownward(.)
*
* \param upward_quad_mids all upward halfedges of the inner skeletal edges (not directly connected to the outline) sorted on their highest [distance_to_boundary]. Higher dist first.
*/
void propagateBeadingsUpward(std::vector<edge_t*>& upward_quad_mids, ptr_vector_t<BeadingPropagation>& node_beadings);
/*!
* propagate beading info from higher R nodes to lower R nodes
*
* merge with upward propagated beadings if they are encountered
*
* don't transfer to nodes which lie on the outline polygon
*
* edges are sorted so that we can do a depth-first walk without employing a recursive algorithm
*
* \param upward_quad_mids all upward halfedges of the inner skeletal edges (not directly connected to the outline) sorted on their highest [distance_to_boundary]. Higher dist first.
*/
void propagateBeadingsDownward(std::vector<edge_t*>& upward_quad_mids, ptr_vector_t<BeadingPropagation>& node_beadings);
/*!
* Subroutine of \ref propagateBeadingsDownward(std::vector<edge_t*>&, ptr_vector_t<BeadingPropagation>&)
*/
void propagateBeadingsDownward(edge_t* edge_to_peak, ptr_vector_t<BeadingPropagation>& node_beadings);
/*!
* Find a beading in between two other beadings.
*
* This creates a new beading. With this we can find the coordinates of the
* endpoints of the actual line segments to draw.
*
* The parameters \p left and \p right are not actually always left or right
* but just arbitrary directions to visually indicate the difference.
* \param left One of the beadings to interpolate between.
* \param ratio_left_to_whole The position within the two beadings to sample
* an interpolation. Should be a ratio between 0 and 1.
* \param right One of the beadings to interpolate between.
* \param switching_radius The bead radius at which we switch from the left
* beading to the merged beading, if the beadings have a different number of
* beads.
* \return The beading at the interpolated location.
*/
Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) const;
/*!
* Subroutine of \ref interpolate(const Beading&, Ratio, const Beading&, coord_t)
*
* This creates a new Beading between two beadings, assuming that both have
* the same number of beads.
* \param left One of the beadings to interpolate between.
* \param ratio_left_to_whole The position within the two beadings to sample
* an interpolation. Should be a ratio between 0 and 1.
* \param right One of the beadings to interpolate between.
* \return The beading at the interpolated location.
*/
Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) const;
/*!
* Get the beading at a certain node of the skeletal graph, or create one if
* it doesn't have one yet.
*
* This is a lazy get.
* \param node The node to get the beading from.
* \param node_beadings A list of all beadings for nodes.
* \return The beading of that node.
*/
std::shared_ptr<BeadingPropagation> getOrCreateBeading(node_t* node, ptr_vector_t<BeadingPropagation>& node_beadings);
/*!
* In case we cannot find the beading of a node, get a beading from the
* nearest node.
* \param node The node to attempt to get a beading from. The actual node
* that the returned beading is from may be a different, nearby node.
* \param max_dist The maximum distance to search for.
* \return A beading for the node, or ``nullptr`` if there is no node nearby
* with a beading.
*/
std::shared_ptr<BeadingPropagation> getNearestBeading(node_t* node, coord_t max_dist);
/*!
* generate junctions for each bone
* \param edge_to_junctions junctions ordered high R to low R
*/
void generateJunctions(ptr_vector_t<BeadingPropagation>& node_beadings, ptr_vector_t<LineJunctions>& edge_junctions);
/*!
* Add a new toolpath segment, defined between two extrusion-juntions.
*
* \param from The junction from which to add a segment.
* \param to The junction to which to add a segment.
* \param is_odd Whether this segment is an odd gap filler along the middle of the skeleton.
* \param force_new_path Whether to prevent adding this path to an existing path which ends in \p from
* \param from_is_3way Whether the \p from junction is a splitting junction where two normal wall lines and a gap filler line come together.
* \param to_is_3way Whether the \p to junction is a splitting junction where two normal wall lines and a gap filler line come together.
*/
void addToolpathSegment(const ExtrusionJunction& from, const ExtrusionJunction& to, bool is_odd, bool force_new_path, bool from_is_3way, bool to_is_3way);
/*!
* connect junctions in each quad
*/
void connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions);
/*!
* Genrate small segments for local maxima where the beading would only result in a single bead
*/
void generateLocalMaximaSingleBeads();
};
} // namespace Slic3r::Arachne
#endif // VORONOI_QUADRILATERALIZATION_H

View File

@ -0,0 +1,122 @@
//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef SKELETAL_TRAPEZOIDATION_EDGE_H
#define SKELETAL_TRAPEZOIDATION_EDGE_H
#include <memory> // smart pointers
#include <list>
#include <vector>
#include "utils/ExtrusionJunction.hpp"
namespace Slic3r::Arachne
{
class SkeletalTrapezoidationEdge
{
private:
enum class Central { UNKNOWN = -1, NO, YES };
public:
/*!
* Representing the location along an edge where the anchor position of a transition should be placed.
*/
struct TransitionMiddle
{
coord_t pos; // Position along edge as measure from edge.from.p
int lower_bead_count;
coord_t feature_radius; // The feature radius at which this transition is placed
TransitionMiddle(coord_t pos, int lower_bead_count, coord_t feature_radius)
: pos(pos), lower_bead_count(lower_bead_count)
, feature_radius(feature_radius)
{}
};
/*!
* Represents the location along an edge where the lower or upper end of a transition should be placed.
*/
struct TransitionEnd
{
coord_t pos; // Position along edge as measure from edge.from.p, where the edge is always the half edge oriented from lower to higher R
int lower_bead_count;
bool is_lower_end; // Whether this is the ed of the transition with lower bead count
TransitionEnd(coord_t pos, int lower_bead_count, bool is_lower_end)
: pos(pos), lower_bead_count(lower_bead_count), is_lower_end(is_lower_end)
{}
};
enum class EdgeType
{
NORMAL = 0, // from voronoi diagram
EXTRA_VD = 1, // introduced to voronoi diagram in order to make the gMAT
TRANSITION_END = 2 // introduced to voronoi diagram in order to make the gMAT
};
EdgeType type;
SkeletalTrapezoidationEdge() : SkeletalTrapezoidationEdge(EdgeType::NORMAL) {}
SkeletalTrapezoidationEdge(const EdgeType &type) : type(type), is_central(Central::UNKNOWN) {}
bool isCentral() const
{
assert(is_central != Central::UNKNOWN);
return is_central == Central::YES;
}
void setIsCentral(bool b)
{
is_central = b ? Central::YES : Central::NO;
}
bool centralIsSet() const
{
return is_central != Central::UNKNOWN;
}
bool hasTransitions(bool ignore_empty = false) const
{
return transitions.use_count() > 0 && (ignore_empty || ! transitions.lock()->empty());
}
void setTransitions(std::shared_ptr<std::list<TransitionMiddle>> storage)
{
transitions = storage;
}
std::shared_ptr<std::list<TransitionMiddle>> getTransitions()
{
return transitions.lock();
}
bool hasTransitionEnds(bool ignore_empty = false) const
{
return transition_ends.use_count() > 0 && (ignore_empty || ! transition_ends.lock()->empty());
}
void setTransitionEnds(std::shared_ptr<std::list<TransitionEnd>> storage)
{
transition_ends = storage;
}
std::shared_ptr<std::list<TransitionEnd>> getTransitionEnds()
{
return transition_ends.lock();
}
bool hasExtrusionJunctions(bool ignore_empty = false) const
{
return extrusion_junctions.use_count() > 0 && (ignore_empty || ! extrusion_junctions.lock()->empty());
}
void setExtrusionJunctions(std::shared_ptr<LineJunctions> storage)
{
extrusion_junctions = storage;
}
std::shared_ptr<LineJunctions> getExtrusionJunctions()
{
return extrusion_junctions.lock();
}
private:
Central is_central; //! whether the edge is significant; whether the source segments have a sharp angle; -1 is unknown
std::weak_ptr<std::list<TransitionMiddle>> transitions;
std::weak_ptr<std::list<TransitionEnd>> transition_ends;
std::weak_ptr<LineJunctions> extrusion_junctions;
};
} // namespace Slic3r::Arachne
#endif // SKELETAL_TRAPEZOIDATION_EDGE_H

View File

@ -0,0 +1,467 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkeletalTrapezoidationGraph.hpp"
#include <unordered_map>
#include <boost/log/trivial.hpp>
#include "utils/linearAlg2D.hpp"
#include "../Line.hpp"
namespace Slic3r::Arachne
{
STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) : HalfEdge(data) {}
bool STHalfEdge::canGoUp(bool strict) const
{
if (to->data.distance_to_boundary > from->data.distance_to_boundary)
{
return true;
}
if (to->data.distance_to_boundary < from->data.distance_to_boundary || strict)
{
return false;
}
// Edge is between equidistqant verts; recurse!
for (edge_t* outgoing = next; outgoing != twin; outgoing = outgoing->twin->next)
{
if (outgoing->canGoUp())
{
return true;
}
assert(outgoing->twin); if (!outgoing->twin) return false;
assert(outgoing->twin->next); if (!outgoing->twin->next) return true; // This point is on the boundary?! Should never occur
}
return false;
}
bool STHalfEdge::isUpward() const
{
if (to->data.distance_to_boundary > from->data.distance_to_boundary)
{
return true;
}
if (to->data.distance_to_boundary < from->data.distance_to_boundary)
{
return false;
}
// Equidistant edge case:
std::optional<coord_t> forward_up_dist = this->distToGoUp();
std::optional<coord_t> backward_up_dist = twin->distToGoUp();
if (forward_up_dist && backward_up_dist)
{
return forward_up_dist < backward_up_dist;
}
if (forward_up_dist)
{
return true;
}
if (backward_up_dist)
{
return false;
}
return to->p < from->p; // Arbitrary ordering, which returns the opposite for the twin edge
}
std::optional<coord_t> STHalfEdge::distToGoUp() const
{
if (to->data.distance_to_boundary > from->data.distance_to_boundary)
{
return 0;
}
if (to->data.distance_to_boundary < from->data.distance_to_boundary)
{
return std::optional<coord_t>();
}
// Edge is between equidistqant verts; recurse!
std::optional<coord_t> ret;
for (edge_t* outgoing = next; outgoing != twin; outgoing = outgoing->twin->next)
{
std::optional<coord_t> dist_to_up = outgoing->distToGoUp();
if (dist_to_up)
{
if (ret)
{
ret = std::min(*ret, *dist_to_up);
}
else
{
ret = dist_to_up;
}
}
assert(outgoing->twin); if (!outgoing->twin) return std::optional<coord_t>();
assert(outgoing->twin->next); if (!outgoing->twin->next) return 0; // This point is on the boundary?! Should never occur
}
if (ret)
{
ret = *ret + (to->p - from->p).cast<int64_t>().norm();
}
return ret;
}
STHalfEdge* STHalfEdge::getNextUnconnected()
{
edge_t* result = static_cast<STHalfEdge*>(this);
while (result->next)
{
result = result->next;
if (result == this)
{
return nullptr;
}
}
return result->twin;
}
STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) : HalfEdgeNode(data, p) {}
bool STHalfEdgeNode::isMultiIntersection()
{
int odd_path_count = 0;
edge_t* outgoing = this->incident_edge;
do
{
if ( ! outgoing)
{ // This is a node on the outside
return false;
}
if (outgoing->data.isCentral())
{
odd_path_count++;
}
} while (outgoing = outgoing->twin->next, outgoing != this->incident_edge);
return odd_path_count > 2;
}
bool STHalfEdgeNode::isCentral() const
{
edge_t* edge = incident_edge;
do
{
if (edge->data.isCentral())
{
return true;
}
assert(edge->twin); if (!edge->twin) return false;
} while (edge = edge->twin->next, edge != incident_edge);
return false;
}
bool STHalfEdgeNode::isLocalMaximum(bool strict) const
{
if (data.distance_to_boundary == 0)
{
return false;
}
edge_t* edge = incident_edge;
do
{
if (edge->canGoUp(strict))
{
return false;
}
assert(edge->twin); if (!edge->twin) return false;
if (!edge->twin->next)
{ // This point is on the boundary
return false;
}
} while (edge = edge->twin->next, edge != incident_edge);
return true;
}
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
{
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator;
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator;
for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it)
{
edge_locator.emplace(&*edge_it, edge_it);
}
for (auto node_it = nodes.begin(); node_it != nodes.end(); ++node_it)
{
node_locator.emplace(&*node_it, node_it);
}
auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, std::list<edge_t>::iterator& current_edge_it, bool& edge_it_is_updated)
{
if (current_edge_it != edges.end()
&& to_be_removed == &*current_edge_it)
{
current_edge_it = edges.erase(current_edge_it);
edge_it_is_updated = true;
}
else
{
edges.erase(edge_locator[to_be_removed]);
}
};
auto should_collapse = [snap_dist](node_t* a, node_t* b)
{
return shorter_then(a->p - b->p, snap_dist);
};
for (auto edge_it = edges.begin(); edge_it != edges.end();)
{
if (edge_it->prev)
{
edge_it++;
continue;
}
edge_t* quad_start = &*edge_it;
edge_t* quad_end = quad_start; while (quad_end->next) quad_end = quad_end->next;
edge_t* quad_mid = (quad_start->next == quad_end)? nullptr : quad_start->next;
bool edge_it_is_updated = false;
if (quad_mid && should_collapse(quad_mid->from, quad_mid->to))
{
assert(quad_mid->twin);
if(!quad_mid->twin)
{
BOOST_LOG_TRIVIAL(warning) << "Encountered quad edge without a twin.";
continue; //Prevent accessing unallocated memory.
}
int count = 0;
for (edge_t* edge_from_3 = quad_end; edge_from_3 && edge_from_3 != quad_mid->twin; edge_from_3 = edge_from_3->twin->next)
{
edge_from_3->from = quad_mid->from;
edge_from_3->twin->to = quad_mid->from;
if (count > 50)
{
std::cerr << edge_from_3->from->p << " - " << edge_from_3->to->p << '\n';
}
if (++count > 1000)
{
break;
}
}
// o-o > collapse top
// | |
// | |
// | |
// o o
if (quad_mid->from->incident_edge == quad_mid)
{
if (quad_mid->twin->next)
{
quad_mid->from->incident_edge = quad_mid->twin->next;
}
else
{
quad_mid->from->incident_edge = quad_mid->prev->twin;
}
}
nodes.erase(node_locator[quad_mid->to]);
quad_mid->prev->next = quad_mid->next;
quad_mid->next->prev = quad_mid->prev;
quad_mid->twin->next->prev = quad_mid->twin->prev;
quad_mid->twin->prev->next = quad_mid->twin->next;
safelyRemoveEdge(quad_mid->twin, edge_it, edge_it_is_updated);
safelyRemoveEdge(quad_mid, edge_it, edge_it_is_updated);
}
// o-o
// | | > collapse sides
// o o
if ( should_collapse(quad_start->from, quad_end->to) && should_collapse(quad_start->to, quad_end->from))
{ // Collapse start and end edges and remove whole cell
quad_start->twin->to = quad_end->to;
quad_end->to->incident_edge = quad_end->twin;
if (quad_end->from->incident_edge == quad_end)
{
if (quad_end->twin->next)
{
quad_end->from->incident_edge = quad_end->twin->next;
}
else
{
quad_end->from->incident_edge = quad_end->prev->twin;
}
}
nodes.erase(node_locator[quad_start->from]);
quad_start->twin->twin = quad_end->twin;
quad_end->twin->twin = quad_start->twin;
safelyRemoveEdge(quad_start, edge_it, edge_it_is_updated);
safelyRemoveEdge(quad_end, edge_it, edge_it_is_updated);
}
// If only one side had collapsable length then the cell on the other side of that edge has to collapse
// if we would collapse that one edge then that would change the quad_start and/or quad_end of neighboring cells
// this is to do with the constraint that !prev == !twin.next
if (!edge_it_is_updated)
{
edge_it++;
}
}
}
void SkeletalTrapezoidationGraph::makeRib(edge_t*& prev_edge, Point start_source_point, Point end_source_point, bool is_next_to_start_or_end)
{
Point p;
Line(start_source_point, end_source_point).distance_to_infinite_squared(prev_edge->to->p, &p);
coord_t dist = (prev_edge->to->p - p).cast<int64_t>().norm();
prev_edge->to->data.distance_to_boundary = dist;
assert(dist >= 0);
nodes.emplace_front(SkeletalTrapezoidationJoint(), p);
node_t* node = &nodes.front();
node->data.distance_to_boundary = 0;
edges.emplace_front(SkeletalTrapezoidationEdge(SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD));
edge_t* forth_edge = &edges.front();
edges.emplace_front(SkeletalTrapezoidationEdge(SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD));
edge_t* back_edge = &edges.front();
prev_edge->next = forth_edge;
forth_edge->prev = prev_edge;
forth_edge->from = prev_edge->to;
forth_edge->to = node;
forth_edge->twin = back_edge;
back_edge->twin = forth_edge;
back_edge->from = node;
back_edge->to = prev_edge->to;
node->incident_edge = back_edge;
prev_edge = back_edge;
}
std::pair<SkeletalTrapezoidationGraph::edge_t*, SkeletalTrapezoidationGraph::edge_t*> SkeletalTrapezoidationGraph::insertRib(edge_t& edge, node_t* mid_node)
{
edge_t* edge_before = edge.prev;
edge_t* edge_after = edge.next;
node_t* node_before = edge.from;
node_t* node_after = edge.to;
Point p = mid_node->p;
const Line source_segment = getSource(edge);
Point px;
source_segment.distance_to_squared(p, &px);
coord_t dist = (p - px).cast<int64_t>().norm();
assert(dist > 0);
mid_node->data.distance_to_boundary = dist;
mid_node->data.transition_ratio = 0; // Both transition end should have rest = 0, because at the ends a whole number of beads fits without rest
nodes.emplace_back(SkeletalTrapezoidationJoint(), px);
node_t* source_node = &nodes.back();
source_node->data.distance_to_boundary = 0;
edge_t* first = &edge;
edges.emplace_back(SkeletalTrapezoidationEdge());
edge_t* second = &edges.back();
edges.emplace_back(SkeletalTrapezoidationEdge(SkeletalTrapezoidationEdge::EdgeType::TRANSITION_END));
edge_t* outward_edge = &edges.back();
edges.emplace_back(SkeletalTrapezoidationEdge(SkeletalTrapezoidationEdge::EdgeType::TRANSITION_END));
edge_t* inward_edge = &edges.back();
if (edge_before)
{
edge_before->next = first;
}
first->next = outward_edge;
outward_edge->next = nullptr;
inward_edge->next = second;
second->next = edge_after;
if (edge_after)
{
edge_after->prev = second;
}
second->prev = inward_edge;
inward_edge->prev = nullptr;
outward_edge->prev = first;
first->prev = edge_before;
first->to = mid_node;
outward_edge->to = source_node;
inward_edge->to = mid_node;
second->to = node_after;
first->from = node_before;
outward_edge->from = mid_node;
inward_edge->from = source_node;
second->from = mid_node;
node_before->incident_edge = first;
mid_node->incident_edge = outward_edge;
source_node->incident_edge = inward_edge;
if (edge_after)
{
node_after->incident_edge = edge_after;
}
first->data.setIsCentral(true);
outward_edge->data.setIsCentral(false); // TODO verify this is always the case.
inward_edge->data.setIsCentral(false);
second->data.setIsCentral(true);
outward_edge->twin = inward_edge;
inward_edge->twin = outward_edge;
first->twin = nullptr; // we don't know these yet!
second->twin = nullptr;
assert(second->prev->from->data.distance_to_boundary == 0);
return std::make_pair(first, second);
}
SkeletalTrapezoidationGraph::edge_t* SkeletalTrapezoidationGraph::insertNode(edge_t* edge, Point mid, coord_t mide_node_bead_count)
{
edge_t* last_edge_replacing_input = edge;
nodes.emplace_back(SkeletalTrapezoidationJoint(), mid);
node_t* mid_node = &nodes.back();
edge_t* twin = last_edge_replacing_input->twin;
last_edge_replacing_input->twin = nullptr;
twin->twin = nullptr;
std::pair<edge_t*, edge_t*> left_pair = insertRib(*last_edge_replacing_input, mid_node);
std::pair<edge_t*, edge_t*> right_pair = insertRib(*twin, mid_node);
edge_t* first_edge_replacing_input = left_pair.first;
last_edge_replacing_input = left_pair.second;
edge_t* first_edge_replacing_twin = right_pair.first;
edge_t* last_edge_replacing_twin = right_pair.second;
first_edge_replacing_input->twin = last_edge_replacing_twin;
last_edge_replacing_twin->twin = first_edge_replacing_input;
last_edge_replacing_input->twin = first_edge_replacing_twin;
first_edge_replacing_twin->twin = last_edge_replacing_input;
mid_node->data.bead_count = mide_node_bead_count;
return last_edge_replacing_input;
}
Line SkeletalTrapezoidationGraph::getSource(const edge_t &edge) const
{
const edge_t *from_edge = &edge;
while (from_edge->prev)
from_edge = from_edge->prev;
const edge_t *to_edge = &edge;
while (to_edge->next)
to_edge = to_edge->next;
return Line(from_edge->from->p, to_edge->to->p);
}
}

View File

@ -0,0 +1,105 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef SKELETAL_TRAPEZOIDATION_GRAPH_H
#define SKELETAL_TRAPEZOIDATION_GRAPH_H
#include <optional>
#include "utils/HalfEdgeGraph.hpp"
#include "SkeletalTrapezoidationEdge.hpp"
#include "SkeletalTrapezoidationJoint.hpp"
namespace Slic3r::Arachne
{
class STHalfEdgeNode;
class STHalfEdge : public HalfEdge<SkeletalTrapezoidationJoint, SkeletalTrapezoidationEdge, STHalfEdgeNode, STHalfEdge>
{
using edge_t = STHalfEdge;
using node_t = STHalfEdgeNode;
public:
STHalfEdge(SkeletalTrapezoidationEdge data);
/*!
* Check (recursively) whether there is any upward edge from the distance_to_boundary of the from of the \param edge
*
* \param strict Whether equidistant edges can count as a local maximum
*/
bool canGoUp(bool strict = false) const;
/*!
* Check whether the edge goes from a lower to a higher distance_to_boundary.
* Effectively deals with equidistant edges by looking beyond this edge.
*/
bool isUpward() const;
/*!
* Calculate the traversed distance until we meet an upward edge.
* Useful for calling on edges between equidistant points.
*
* If we can go up then the distance includes the length of the \param edge
*/
std::optional<coord_t> distToGoUp() const;
STHalfEdge* getNextUnconnected();
};
class STHalfEdgeNode : public HalfEdgeNode<SkeletalTrapezoidationJoint, SkeletalTrapezoidationEdge, STHalfEdgeNode, STHalfEdge>
{
using edge_t = STHalfEdge;
using node_t = STHalfEdgeNode;
public:
STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p);
bool isMultiIntersection();
bool isCentral() const;
/*!
* Check whether this node has a locally maximal distance_to_boundary
*
* \param strict Whether equidistant edges can count as a local maximum
*/
bool isLocalMaximum(bool strict = false) const;
};
class SkeletalTrapezoidationGraph: public HalfEdgeGraph<SkeletalTrapezoidationJoint, SkeletalTrapezoidationEdge, STHalfEdgeNode, STHalfEdge>
{
using edge_t = STHalfEdge;
using node_t = STHalfEdgeNode;
public:
/*!
* If an edge is too small, collapse it and its twin and fix the surrounding edges to ensure a consistent graph.
*
* Don't collapse support edges, unless we can collapse the whole quad.
*
* o-,
* | "-o
* | | > Don't collapse this edge only.
* o o
*/
void collapseSmallEdges(coord_t snap_dist = 5);
void makeRib(edge_t*& prev_edge, Point start_source_point, Point end_source_point, bool is_next_to_start_or_end);
/*!
* Insert a node into the graph and connect it to the input polygon using ribs
*
* \return the last edge which replaced [edge], which points to the same [to] node
*/
edge_t* insertNode(edge_t* edge, Point mid, coord_t mide_node_bead_count);
/*!
* Return the first and last edge of the edges replacing \p edge pointing to the same node
*/
std::pair<edge_t*, edge_t*> insertRib(edge_t& edge, node_t* mid_node);
protected:
Line getSource(const edge_t& edge) const;
};
}
#endif

View File

@ -0,0 +1,60 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef SKELETAL_TRAPEZOIDATION_JOINT_H
#define SKELETAL_TRAPEZOIDATION_JOINT_H
#include <memory> // smart pointers
#include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp"
namespace Slic3r::Arachne
{
class SkeletalTrapezoidationJoint
{
using Beading = BeadingStrategy::Beading;
public:
struct BeadingPropagation
{
Beading beading;
coord_t dist_to_bottom_source;
coord_t dist_from_top_source;
bool is_upward_propagated_only;
BeadingPropagation(const Beading& beading)
: beading(beading)
, dist_to_bottom_source(0)
, dist_from_top_source(0)
, is_upward_propagated_only(false)
{}
};
coord_t distance_to_boundary;
coord_t bead_count;
float transition_ratio; //! The distance near the skeleton to leave free because this joint is in the middle of a transition, as a fraction of the inner bead width of the bead at the higher transition.
SkeletalTrapezoidationJoint()
: distance_to_boundary(-1)
, bead_count(-1)
, transition_ratio(0)
{}
bool hasBeading() const
{
return beading.use_count() > 0;
}
void setBeading(std::shared_ptr<BeadingPropagation> storage)
{
beading = storage;
}
std::shared_ptr<BeadingPropagation> getBeading()
{
return beading.lock();
}
private:
std::weak_ptr<BeadingPropagation> beading;
};
} // namespace Slic3r::Arachne
#endif // SKELETAL_TRAPEZOIDATION_JOINT_H

View File

@ -0,0 +1,853 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include <algorithm> //For std::partition_copy and std::min_element.
#include <unordered_set>
#include "WallToolPaths.hpp"
#include "SkeletalTrapezoidation.hpp"
#include "../ClipperUtils.hpp"
#include "utils/linearAlg2D.hpp"
#include "EdgeGrid.hpp"
#include "utils/SparseLineGrid.hpp"
#include "Geometry.hpp"
#include "utils/PolylineStitcher.hpp"
#include "SVG.hpp"
#include "Utils.hpp"
#include <boost/log/trivial.hpp>
//#define ARACHNE_STITCH_PATCH_DEBUG
namespace Slic3r::Arachne
{
WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
const size_t inset_count, const coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config)
: outline(outline)
, bead_width_0(bead_width_0)
, bead_width_x(bead_width_x)
, inset_count(inset_count)
, wall_0_inset(wall_0_inset)
, print_thin_walls(Slic3r::Arachne::fill_outline_gaps)
, min_feature_size(scaled<coord_t>(print_object_config.min_feature_size.value))
, min_bead_width(scaled<coord_t>(print_object_config.min_bead_width.value))
, small_area_length(static_cast<double>(bead_width_0) / 2.)
, toolpaths_generated(false)
, print_object_config(print_object_config)
{
if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent) {
assert(!print_config.nozzle_diameter.empty());
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
this->min_bead_width = scaled<coord_t>(min_bead_width_opt.value * 0.01 * min_nozzle_diameter);
}
if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent) {
assert(!print_config.nozzle_diameter.empty());
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
this->wall_transition_filter_deviation = scaled<coord_t>(wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter);
}
}
void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
{
if (thiss.size() < 3) {
thiss.points.clear();
return;
}
if (thiss.size() == 3)
return;
Polygon new_path;
Point previous = thiss.points.back();
Point previous_previous = thiss.points.at(thiss.points.size() - 2);
Point current = thiss.points.at(0);
/* When removing a vertex, we check the height of the triangle of the area
being removed from the original polygon by the simplification. However,
when consecutively removing multiple vertices the height of the previously
removed vertices w.r.t. the shortcut path changes.
In order to not recompute the new height value of previously removed
vertices we compute the height of a representative triangle, which covers
the same amount of area as the area being cut off. We use the Shoelace
formula to accumulate the area under the removed segments. This works by
computing the area in a 'fan' where each of the blades of the fan go from
the origin to one of the segments. While removing vertices the area in
this fan accumulates. By subtracting the area of the blade connected to
the short-cutting segment we obtain the total area of the cutoff region.
From this area we compute the height of the representative triangle using
the standard formula for a triangle area: A = .5*b*h
*/
int64_t accumulated_area_removed = int64_t(previous.x()) * int64_t(current.y()) - int64_t(previous.y()) * int64_t(current.x()); // Twice the Shoelace formula for area of polygon per line segment.
for (size_t point_idx = 0; point_idx < thiss.points.size(); point_idx++) {
current = thiss.points.at(point_idx % thiss.points.size());
//Check if the accumulated area doesn't exceed the maximum.
Point next;
if (point_idx + 1 < thiss.points.size()) {
next = thiss.points.at(point_idx + 1);
} else if (point_idx + 1 == thiss.points.size() && new_path.size() > 1) { // don't spill over if the [next] vertex will then be equal to [previous]
next = new_path[0]; //Spill over to new polygon for checking removed area.
} else {
next = thiss.points.at((point_idx + 1) % thiss.points.size());
}
const int64_t removed_area_next = int64_t(current.x()) * int64_t(next.y()) - int64_t(current.y()) * int64_t(next.x()); // Twice the Shoelace formula for area of polygon per line segment.
const int64_t negative_area_closing = int64_t(next.x()) * int64_t(previous.y()) - int64_t(next.y()) * int64_t(previous.x()); // area between the origin and the short-cutting segment
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<int64_t>(25.)) {
// We're allowed to always delete segments of less than 5 micron.
continue;
}
const int64_t area_removed_so_far = accumulated_area_removed + negative_area_closing; // close the shortcut area polygon
const int64_t base_length_2 = (next - previous).cast<int64_t>().squaredNorm();
if (base_length_2 == 0) //Two line segments form a line back and forth with no area.
continue; //Remove the vertex.
//We want to check if the height of the triangle formed by previous, current and next vertices is less than allowed_error_distance_squared.
//1/2 L = A [actual area is half of the computed shoelace value] // Shoelace formula is .5*(...) , but we simplify the computation and take out the .5
//A = 1/2 * b * h [triangle area formula]
//L = b * h [apply above two and take out the 1/2]
//h = L / b [divide by b]
//h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor]
const int64_t height_2 = double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2);
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current, previous, next) <= scaled<double>(0.005))) // make sure that height_2 is not small because of cancellation of positive and negative areas
continue;
if (length2 < smallest_line_segment_squared
&& height_2 <= allowed_error_distance_squared) // removing the vertex doesn't introduce too much error.)
{
const int64_t next_length2 = (current - next).cast<int64_t>().squaredNorm();
if (next_length2 > 4 * smallest_line_segment_squared) {
// Special case; The next line is long. If we were to remove this, it could happen that we get quite noticeable artifacts.
// We should instead move this point to a location where both edges are kept and then remove the previous point that we wanted to keep.
// By taking the intersection of these two lines, we get a point that preserves the direction (so it makes the corner a bit more pointy).
// We just need to be sure that the intersection point does not introduce an artifact itself.
Point intersection_point;
bool has_intersection = Line(previous_previous, previous).intersection_infinite(Line(current, next), &intersection_point);
if (!has_intersection
|| Line::distance_to_infinite_squared(intersection_point, previous, current) > double(allowed_error_distance_squared)
|| (intersection_point - previous).cast<int64_t>().squaredNorm() > smallest_line_segment_squared // The intersection point is way too far from the 'previous'
|| (intersection_point - next).cast<int64_t>().squaredNorm() > smallest_line_segment_squared) // and 'next' points, so it shouldn't replace 'current'
{
// We can't find a better spot for it, but the size of the line is more than 5 micron.
// So the only thing we can do here is leave it in...
}
else {
// New point seems like a valid one.
current = intersection_point;
// If there was a previous point added, remove it.
if(!new_path.empty()) {
new_path.points.pop_back();
previous = previous_previous;
}
}
} else {
continue; //Remove the vertex.
}
}
//Don't remove the vertex.
accumulated_area_removed = removed_area_next; // so that in the next iteration it's the area between the origin, [previous] and [current]
previous_previous = previous;
previous = current; //Note that "previous" is only updated if we don't remove the vertex.
new_path.points.push_back(current);
}
thiss = new_path;
}
/*!
* Removes vertices of the polygons to make sure that they are not too high
* resolution.
*
* This removes points which are connected to line segments that are shorter
* than the `smallest_line_segment`, unless that would introduce a deviation
* in the contour of more than `allowed_error_distance`.
*
* Criteria:
* 1. Never remove a vertex if either of the connceted segments is larger than \p smallest_line_segment
* 2. Never remove a vertex if the distance between that vertex and the final resulting polygon would be higher than \p allowed_error_distance
* 3. The direction of segments longer than \p smallest_line_segment always
* remains unaltered (but their end points may change if it is connected to
* a small segment)
*
* Simplify uses a heuristic and doesn't neccesarily remove all removable
* vertices under the above criteria, but simplify may never violate these
* criteria. Unless the segments or the distance is smaller than the
* rounding error of 5 micron.
*
* Vertices which introduce an error of less than 5 microns are removed
* anyway, even if the segments are longer than the smallest line segment.
* This makes sure that (practically) colinear line segments are joined into
* a single line segment.
* \param smallest_line_segment Maximal length of removed line segments.
* \param allowed_error_distance If removing a vertex introduces a deviation
* from the original path that is more than this distance, the vertex may
* not be removed.
*/
void simplify(Polygons &thiss, const int64_t smallest_line_segment = scaled<coord_t>(0.01), const int64_t allowed_error_distance = scaled<coord_t>(0.005))
{
const int64_t allowed_error_distance_squared = int64_t(allowed_error_distance) * int64_t(allowed_error_distance);
const int64_t smallest_line_segment_squared = int64_t(smallest_line_segment) * int64_t(smallest_line_segment);
for (size_t p = 0; p < thiss.size(); p++)
{
simplify(thiss[p], smallest_line_segment_squared, allowed_error_distance_squared);
if (thiss[p].size() < 3)
{
thiss.erase(thiss.begin() + p);
p--;
}
}
}
typedef SparseLineGrid<PolygonsPointIndex, PolygonsPointIndexSegmentLocator> LocToLineGrid;
std::unique_ptr<LocToLineGrid> createLocToLineGrid(const Polygons &polygons, int square_size)
{
unsigned int n_points = 0;
for (const auto &poly : polygons)
n_points += poly.size();
auto ret = std::make_unique<LocToLineGrid>(square_size, n_points);
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
for (unsigned int point_idx = 0; point_idx < polygons[poly_idx].size(); point_idx++)
ret->insert(PolygonsPointIndex(&polygons, poly_idx, point_idx));
return ret;
}
/* Note: Also tries to solve for near-self intersections, when epsilon >= 1
*/
void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
{
if (epsilon < 1) {
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
return;
}
const int64_t half_epsilon = (epsilon + 1) / 2;
// Points too close to line segments should be moved a little away from those line segments, but less than epsilon,
// so at least half-epsilon distance between points can still be guaranteed.
constexpr coord_t grid_size = scaled<coord_t>(2.);
auto query_grid = createLocToLineGrid(thiss, grid_size);
const auto move_dist = std::max<int64_t>(2L, half_epsilon - 2);
const int64_t half_epsilon_sqrd = half_epsilon * half_epsilon;
const size_t n = thiss.size();
for (size_t poly_idx = 0; poly_idx < n; poly_idx++) {
const size_t pathlen = thiss[poly_idx].size();
for (size_t point_idx = 0; point_idx < pathlen; ++point_idx) {
Point &pt = thiss[poly_idx][point_idx];
for (const auto &line : query_grid->getNearby(pt, epsilon)) {
const size_t line_next_idx = (line.point_idx + 1) % thiss[line.poly_idx].size();
if (poly_idx == line.poly_idx && (point_idx == line.point_idx || point_idx == line_next_idx))
continue;
const Line segment(thiss[line.poly_idx][line.point_idx], thiss[line.poly_idx][line_next_idx]);
Point segment_closest_point;
segment.distance_to_squared(pt, &segment_closest_point);
if (half_epsilon_sqrd >= (pt - segment_closest_point).cast<int64_t>().squaredNorm()) {
const Point &other = thiss[poly_idx][(point_idx + 1) % pathlen];
const Vec2i64 vec = (LinearAlg2D::pointIsLeftOfLine(other, segment.a, segment.b) > 0 ? segment.b - segment.a : segment.a - segment.b).cast<int64_t>();
assert(Slic3r::sqr(double(vec.x())) < double(std::numeric_limits<int64_t>::max()));
assert(Slic3r::sqr(double(vec.y())) < double(std::numeric_limits<int64_t>::max()));
const int64_t len = vec.norm();
pt.x() += (-vec.y() * move_dist) / len;
pt.y() += (vec.x() * move_dist) / len;
}
}
}
}
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
}
/*!
* Removes overlapping consecutive line segments which don't delimit a positive area.
*/
void removeDegenerateVerts(Polygons &thiss)
{
for (size_t poly_idx = 0; poly_idx < thiss.size(); poly_idx++) {
Polygon &poly = thiss[poly_idx];
Polygon result;
auto isDegenerate = [](const Point &last, const Point &now, const Point &next) {
Vec2i64 last_line = (now - last).cast<int64_t>();
Vec2i64 next_line = (next - now).cast<int64_t>();
return last_line.dot(next_line) == -1 * last_line.norm() * next_line.norm();
};
bool isChanged = false;
for (size_t idx = 0; idx < poly.size(); idx++) {
const Point &last = (result.size() == 0) ? poly.back() : result.back();
if (idx + 1 == poly.size() && result.size() == 0)
break;
const Point &next = (idx + 1 == poly.size()) ? result[0] : poly[idx + 1];
if (isDegenerate(last, poly[idx], next)) { // lines are in the opposite direction
// don't add vert to the result
isChanged = true;
while (result.size() > 1 && isDegenerate(result[result.size() - 2], result.back(), next))
result.points.pop_back();
} else {
result.points.emplace_back(poly[idx]);
}
}
if (isChanged) {
if (result.size() > 2) {
poly = result;
} else {
thiss.erase(thiss.begin() + poly_idx);
poly_idx--; // effectively the next iteration has the same poly_idx (referring to a new poly which is not yet processed)
}
}
}
}
void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool remove_holes)
{
auto to_path = [](const Polygon &poly) -> ClipperLib::Path {
ClipperLib::Path out;
for (const Point &pt : poly.points)
out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
return out;
};
auto new_end = thiss.end();
if(remove_holes)
{
for(auto it = thiss.begin(); it < new_end; it++)
{
// All polygons smaller than target are removed by replacing them with a polygon from the back of the vector
if(fabs(ClipperLib::Area(to_path(*it))) < min_area_size)
{
new_end--;
*it = std::move(*new_end);
it--; // wind back the iterator such that the polygon just swaped in is checked next
}
}
}
else
{
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
std::vector<Polygon> small_holes;
for(auto it = thiss.begin(); it < new_end; it++) {
double area = ClipperLib::Area(to_path(*it));
if (fabs(area) < min_area_size)
{
if(area >= 0)
{
new_end--;
if(it < new_end) {
std::swap(*new_end, *it);
it--;
}
else
{ // Don't self-swap the last Path
break;
}
}
else
{
small_holes.push_back(*it);
}
}
}
// Removes small holes that have their first point inside one of the removed outlines
// Iterating in reverse ensures that unprocessed small holes won't be moved
const auto removed_outlines_start = new_end;
for(auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
{
for(auto outline_it = removed_outlines_start; outline_it < thiss.end() ; outline_it++)
{
if(Polygon(*outline_it).contains(*hole_it->begin())) {
new_end--;
*hole_it = std::move(*new_end);
break;
}
}
}
}
thiss.resize(new_end-thiss.begin());
}
void removeColinearEdges(Polygon &poly, const double max_deviation_angle)
{
// TODO: Can be made more efficient (for example, use pointer-types for process-/skip-indices, so we can swap them without copy).
size_t num_removed_in_iteration = 0;
do {
num_removed_in_iteration = 0;
std::vector<bool> process_indices(poly.points.size(), true);
bool go = true;
while (go) {
go = false;
const auto &rpath = poly;
const size_t pathlen = rpath.size();
if (pathlen <= 3)
return;
std::vector<bool> skip_indices(poly.points.size(), false);
Polygon new_path;
for (size_t point_idx = 0; point_idx < pathlen; ++point_idx) {
// Don't iterate directly over process-indices, but do it this way, because there are points _in_ process-indices that should nonetheless
// be skipped:
if (!process_indices[point_idx]) {
new_path.points.push_back(rpath[point_idx]);
continue;
}
// Should skip the last point for this iteration if the old first was removed (which can be seen from the fact that the new first was skipped):
if (point_idx == (pathlen - 1) && skip_indices[0]) {
skip_indices[new_path.size()] = true;
go = true;
new_path.points.push_back(rpath[point_idx]);
break;
}
const Point &prev = rpath[(point_idx - 1 + pathlen) % pathlen];
const Point &pt = rpath[point_idx];
const Point &next = rpath[(point_idx + 1) % pathlen];
float angle = LinearAlg2D::getAngleLeft(prev, pt, next); // [0 : 2 * pi]
if (angle >= float(M_PI)) { angle -= float(M_PI); } // map [pi : 2 * pi] to [0 : pi]
// Check if the angle is within limits for the point to 'make sense', given the maximum deviation.
// If the angle indicates near-parallel segments ignore the point 'pt'
if (angle > max_deviation_angle && angle < M_PI - max_deviation_angle) {
new_path.points.push_back(pt);
} else if (point_idx != (pathlen - 1)) {
// Skip the next point, since the current one was removed:
skip_indices[new_path.size()] = true;
go = true;
new_path.points.push_back(next);
++point_idx;
}
}
poly = new_path;
num_removed_in_iteration += pathlen - poly.points.size();
process_indices.clear();
process_indices.insert(process_indices.end(), skip_indices.begin(), skip_indices.end());
}
} while (num_removed_in_iteration > 0);
}
void removeColinearEdges(Polygons &thiss, const double max_deviation_angle = 0.0005)
{
for (int p = 0; p < int(thiss.size()); p++) {
removeColinearEdges(thiss[p], max_deviation_angle);
if (thiss[p].size() < 3) {
thiss.erase(thiss.begin() + p);
p--;
}
}
}
const std::vector<VariableWidthLines> &WallToolPaths::generate()
{
if (this->inset_count < 1)
return toolpaths;
const coord_t smallest_segment = Slic3r::Arachne::meshfix_maximum_resolution;
const coord_t allowed_distance = Slic3r::Arachne::meshfix_maximum_deviation;
const coord_t epsilon_offset = (allowed_distance / 2) - 1;
const double transitioning_angle = Geometry::deg2rad(this->print_object_config.wall_transition_angle.value);
constexpr coord_t discretization_step_size = scaled<coord_t>(0.8);
// Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed:
// TODO: Open question: Does this indeed fix all (or all-but-one-in-a-million) cases for manifold but otherwise possibly complex polygons?
Polygons prepared_outline = offset(offset(offset(outline, -epsilon_offset), epsilon_offset * 2), -epsilon_offset);
simplify(prepared_outline, smallest_segment, allowed_distance);
fixSelfIntersections(epsilon_offset, prepared_outline);
removeDegenerateVerts(prepared_outline);
removeColinearEdges(prepared_outline, 0.005);
// Removing collinear edges may introduce self intersections, so we need to fix them again
fixSelfIntersections(epsilon_offset, prepared_outline);
removeDegenerateVerts(prepared_outline);
removeSmallAreas(prepared_outline, small_area_length * small_area_length, false);
// The functions above could produce intersecting polygons that could cause a crash inside Arachne.
// Applying Clipper union should be enough to get rid of this issue.
// Clipper union also fixed an issue in Arachne that in post-processing Voronoi diagram, some edges
// didn't have twin edges (this probably isn't an issue in Boost Voronoi generator).
prepared_outline = union_(prepared_outline);
if (area(prepared_outline) <= 0) {
assert(toolpaths.empty());
return toolpaths;
}
const coord_t wall_transition_length = scaled<coord_t>(this->print_object_config.wall_transition_length.value);
const double wall_split_middle_threshold = this->print_object_config.wall_split_middle_threshold.value / 100.; // For an uneven nr. of lines: When to split the middle wall into two.
const double wall_add_middle_threshold = this->print_object_config.wall_add_middle_threshold.value / 100.; // For an even nr. of lines: When to add a new middle in between the innermost two walls.
const int wall_distribution_count = this->print_object_config.wall_distribution_count.value;
const size_t max_bead_count = (inset_count < std::numeric_limits<coord_t>::max() / 2) ? 2 * inset_count : std::numeric_limits<coord_t>::max();
const auto beading_strat = BeadingStrategyFactory::makeStrategy
(
bead_width_0,
bead_width_x,
wall_transition_length,
transitioning_angle,
print_thin_walls,
min_bead_width,
min_feature_size,
wall_split_middle_threshold,
wall_add_middle_threshold,
max_bead_count,
wall_0_inset,
wall_distribution_count
);
const coord_t transition_filter_dist = scaled<coord_t>(100.f);
const coord_t allowed_filter_deviation = wall_transition_filter_deviation;
SkeletalTrapezoidation wall_maker
(
prepared_outline,
*beading_strat,
beading_strat->getTransitioningAngle(),
discretization_step_size,
transition_filter_dist,
allowed_filter_deviation,
wall_transition_length
);
wall_maker.generateToolpaths(toolpaths);
stitchToolPaths(toolpaths, this->bead_width_x);
removeSmallLines(toolpaths);
separateOutInnerContour();
simplifyToolPaths(toolpaths);
removeEmptyToolPaths(toolpaths);
assert(std::is_sorted(toolpaths.cbegin(), toolpaths.cend(),
[](const VariableWidthLines& l, const VariableWidthLines& r)
{
return l.front().inset_idx < r.front().inset_idx;
}) && "WallToolPaths should be sorted from the outer 0th to inner_walls");
toolpaths_generated = true;
return toolpaths;
}
void WallToolPaths::stitchToolPaths(std::vector<VariableWidthLines> &toolpaths, const coord_t bead_width_x)
{
const coord_t stitch_distance = bead_width_x - 1; //In 0-width contours, junctions can cause up to 1-line-width gaps. Don't stitch more than 1 line width.
for (unsigned int wall_idx = 0; wall_idx < toolpaths.size(); wall_idx++) {
VariableWidthLines& wall_lines = toolpaths[wall_idx];
VariableWidthLines stitched_polylines;
VariableWidthLines closed_polygons;
PolylineStitcher<VariableWidthLines, ExtrusionLine, ExtrusionJunction>::stitch(wall_lines, stitched_polylines, closed_polygons, stitch_distance);
#ifdef ARACHNE_STITCH_PATCH_DEBUG
for (const ExtrusionLine& line : stitched_polylines) {
if ( ! line.is_odd && line.polylineLength() > 3 * stitch_distance && line.size() > 3) {
BOOST_LOG_TRIVIAL(error) << "Some even contour lines could not be closed into polygons!";
assert(false && "Some even contour lines could not be closed into polygons!");
BoundingBox aabb;
for (auto line2 : wall_lines)
for (auto j : line2)
aabb.merge(j.p);
{
static int iRun = 0;
SVG svg(debug_out_path("contours_before.svg-%d.png", iRun), aabb);
std::array<const char *, 8> colors = {"gray", "black", "blue", "green", "lime", "purple", "red", "yellow"};
size_t color_idx = 0;
for (auto& inset : toolpaths)
for (auto& line2 : inset) {
// svg.writePolyline(line2.toPolygon(), col);
Polygon poly = line2.toPolygon();
Point last = poly.front();
for (size_t idx = 1 ; idx < poly.size(); idx++) {
Point here = poly[idx];
svg.draw(Line(last, here), colors[color_idx]);
// svg.draw_text((last + here) / 2, std::to_string(line2.junctions[idx].region_id).c_str(), "black");
last = here;
}
svg.draw(poly[0], colors[color_idx]);
// svg.nextLayer();
// svg.writePoints(poly, true, 0.1);
// svg.nextLayer();
color_idx = (color_idx + 1) % colors.size();
}
}
{
static int iRun = 0;
SVG svg(debug_out_path("contours-%d.svg", iRun), aabb);
for (auto& inset : toolpaths)
for (auto& line2 : inset)
svg.draw_outline(line2.toPolygon(), "gray");
for (auto& line2 : stitched_polylines) {
const char *col = line2.is_odd ? "gray" : "red";
if ( ! line2.is_odd)
std::cerr << "Non-closed even wall of size: " << line2.size() << " at " << line2.front().p << "\n";
if ( ! line2.is_odd)
svg.draw(line2.front().p);
Polygon poly = line2.toPolygon();
Point last = poly.front();
for (size_t idx = 1 ; idx < poly.size(); idx++)
{
Point here = poly[idx];
svg.draw(Line(last, here), col);
last = here;
}
}
for (auto line2 : closed_polygons)
svg.draw(line2.toPolygon());
}
}
}
#endif // ARACHNE_STITCH_PATCH_DEBUG
wall_lines = stitched_polylines; // replace input toolpaths with stitched polylines
for (ExtrusionLine& wall_polygon : closed_polygons)
{
if (wall_polygon.junctions.empty())
{
continue;
}
wall_polygon.is_closed = true;
wall_lines.emplace_back(std::move(wall_polygon)); // add stitched polygons to result
}
#ifdef DEBUG
for (ExtrusionLine& line : wall_lines)
{
assert(line.inset_idx == wall_idx);
}
#endif // DEBUG
}
}
template<typename T> bool shorterThan(const T &shape, const coord_t check_length)
{
const auto *p0 = &shape.back();
int64_t length = 0;
for (const auto &p1 : shape) {
length += (*p0 - p1).template cast<int64_t>().norm();
if (length >= check_length)
return false;
p0 = &p1;
}
return true;
}
void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths)
{
for (VariableWidthLines &inset : toolpaths) {
for (size_t line_idx = 0; line_idx < inset.size(); line_idx++) {
ExtrusionLine &line = inset[line_idx];
coord_t min_width = std::numeric_limits<coord_t>::max();
for (const ExtrusionJunction &j : line)
min_width = std::min(min_width, j.w);
if (line.is_odd && !line.is_closed && shorterThan(line, min_width / 2)) { // remove line
line = std::move(inset.back());
inset.erase(--inset.end());
line_idx--; // reconsider the current position
}
}
}
}
void WallToolPaths::simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths)
{
for (size_t toolpaths_idx = 0; toolpaths_idx < toolpaths.size(); ++toolpaths_idx)
{
const int64_t maximum_resolution = Slic3r::Arachne::meshfix_maximum_resolution;
const int64_t maximum_deviation = Slic3r::Arachne::meshfix_maximum_deviation;
const int64_t maximum_extrusion_area_deviation = Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation; // unit: μm²
for (auto& line : toolpaths[toolpaths_idx])
{
line.simplify(maximum_resolution * maximum_resolution, maximum_deviation * maximum_deviation, maximum_extrusion_area_deviation);
}
}
}
const std::vector<VariableWidthLines> &WallToolPaths::getToolPaths()
{
if (!toolpaths_generated)
return generate();
return toolpaths;
}
void WallToolPaths::separateOutInnerContour()
{
//We'll remove all 0-width paths from the original toolpaths and store them separately as polygons.
std::vector<VariableWidthLines> actual_toolpaths;
actual_toolpaths.reserve(toolpaths.size()); //A bit too much, but the correct order of magnitude.
std::vector<VariableWidthLines> contour_paths;
contour_paths.reserve(toolpaths.size() / inset_count);
inner_contour.clear();
for (const VariableWidthLines &inset : toolpaths) {
if (inset.empty())
continue;
bool is_contour = false;
for (const ExtrusionLine &line : inset) {
for (const ExtrusionJunction &j : line) {
if (j.w == 0)
is_contour = true;
else
is_contour = false;
break;
}
}
if (is_contour) {
#ifdef DEBUG
for (const ExtrusionLine &line : inset)
for (const ExtrusionJunction &j : line)
assert(j.w == 0);
#endif // DEBUG
for (const ExtrusionLine &line : inset) {
if (line.is_odd)
continue; // odd lines don't contribute to the contour
else if (line.is_closed) // sometimes an very small even polygonal wall is not stitched into a polygon
inner_contour.emplace_back(line.toPolygon());
}
} else {
actual_toolpaths.emplace_back(inset);
}
}
if (!actual_toolpaths.empty())
toolpaths = std::move(actual_toolpaths); // Filtered out the 0-width paths.
else
toolpaths.clear();
//The output walls from the skeletal trapezoidation have no known winding order, especially if they are joined together from polylines.
//They can be in any direction, clockwise or counter-clockwise, regardless of whether the shapes are positive or negative.
//To get a correct shape, we need to make the outside contour positive and any holes inside negative.
//This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon.
//The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation.
inner_contour = union_(inner_contour, ClipperLib::PolyFillType::pftEvenOdd);
}
const Polygons& WallToolPaths::getInnerContour()
{
if (!toolpaths_generated && inset_count > 0)
{
generate();
}
else if(inset_count == 0)
{
return outline;
}
return inner_contour;
}
bool WallToolPaths::removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths)
{
toolpaths.erase(std::remove_if(toolpaths.begin(), toolpaths.end(), [](const VariableWidthLines& lines)
{
return lines.empty();
}), toolpaths.end());
return toolpaths.empty();
}
/*!
* Get the order constraints of the insets when printing walls per region / hole.
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
*
* Odd walls should always go after their enclosing wall polygons.
*
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner)
{
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> order_requirements;
// We build a grid where we map toolpath vertex locations to toolpaths,
// so that we can easily find which two toolpaths are next to each other,
// which is the requirement for there to be an order constraint.
//
// We use a PointGrid rather than a LineGrid to save on computation time.
// In very rare cases two insets might lie next to each other without having neighboring vertices, e.g.
// \ .
// | / .
// | / .
// || .
// | \ .
// | \ .
// / .
// However, because of how Arachne works this will likely never be the case for two consecutive insets.
// On the other hand one could imagine that two consecutive insets of a very large circle
// could be simplify()ed such that the remaining vertices of the two insets don't align.
// In those cases the order requirement is not captured,
// which means that the PathOrderOptimizer *might* result in a violation of the user set path order.
// This problem is expected to be not so severe and happen very sparsely.
coord_t max_line_w = 0u;
for (const ExtrusionLine *line : input) // compute max_line_w
for (const ExtrusionJunction &junction : *line)
max_line_w = std::max(max_line_w, junction.w);
if (max_line_w == 0u)
return order_requirements;
struct LineLoc
{
ExtrusionJunction j;
const ExtrusionLine *line;
};
struct Locator
{
Point operator()(const LineLoc &elem) { return elem.j.p; }
};
// How much farther two verts may be apart due to corners.
// This distance must be smaller than 2, because otherwise
// we could create an order requirement between e.g.
// wall 2 of one region and wall 3 of another region,
// while another wall 3 of the first region would lie in between those two walls.
// However, higher values are better against the limitations of using a PointGrid rather than a LineGrid.
constexpr float diagonal_extension = 1.9f;
const auto searching_radius = coord_t(max_line_w * diagonal_extension);
using GridT = SparsePointGrid<LineLoc, Locator>;
GridT grid(searching_radius);
for (const ExtrusionLine *line : input)
for (const ExtrusionJunction &junction : *line) grid.insert(LineLoc{junction, line});
for (const std::pair<const SquareGrid::GridPoint, LineLoc> &pair : grid) {
const LineLoc &lineloc_here = pair.second;
const ExtrusionLine *here = lineloc_here.line;
Point loc_here = pair.second.j.p;
std::vector<LineLoc> nearby_verts = grid.getNearby(loc_here, searching_radius);
for (const LineLoc &lineloc_nearby : nearby_verts) {
const ExtrusionLine *nearby = lineloc_nearby.line;
if (nearby == here)
continue;
if (nearby->inset_idx == here->inset_idx)
continue;
if (nearby->inset_idx > here->inset_idx + 1)
continue; // not directly adjacent
if (here->inset_idx > nearby->inset_idx + 1)
continue; // not directly adjacent
if (!shorter_then(loc_here - lineloc_nearby.j.p, (lineloc_here.j.w + lineloc_nearby.j.w) / 2 * diagonal_extension))
continue; // points are too far away from each other
if (here->is_odd || nearby->is_odd) {
if (here->is_odd && !nearby->is_odd && nearby->inset_idx < here->inset_idx)
order_requirements.emplace(std::make_pair(nearby, here));
if (nearby->is_odd && !here->is_odd && here->inset_idx < nearby->inset_idx)
order_requirements.emplace(std::make_pair(here, nearby));
} else if ((nearby->inset_idx < here->inset_idx) == outer_to_inner) {
order_requirements.emplace(std::make_pair(nearby, here));
} else {
assert((nearby->inset_idx > here->inset_idx) == outer_to_inner);
order_requirements.emplace(std::make_pair(here, nearby));
}
}
}
return order_requirements;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,126 @@
// Copyright (c) 2020 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef CURAENGINE_WALLTOOLPATHS_H
#define CURAENGINE_WALLTOOLPATHS_H
#include <memory>
#include <unordered_set>
#include "BeadingStrategy/BeadingStrategyFactory.hpp"
#include "utils/ExtrusionLine.hpp"
#include "../Polygon.hpp"
#include "../PrintConfig.hpp"
namespace Slic3r::Arachne
{
constexpr bool fill_outline_gaps = true;
constexpr coord_t meshfix_maximum_resolution = scaled<coord_t>(0.5);
constexpr coord_t meshfix_maximum_deviation = scaled<coord_t>(0.025);
constexpr coord_t meshfix_maximum_extrusion_area_deviation = scaled<coord_t>(2.);
class WallToolPaths
{
public:
/*!
* A class that creates the toolpaths given an outline, nominal bead width and maximum amount of walls
* \param outline An outline of the area in which the ToolPaths are to be generated
* \param bead_width_0 The bead width of the first wall used in the generation of the toolpaths
* \param bead_width_x The bead width of the inner walls used in the generation of the toolpaths
* \param inset_count The maximum number of parallel extrusion lines that make up the wall
* \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls.
*/
WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
/*!
* Generates the Toolpaths
* \return A reference to the newly create ToolPaths
*/
const std::vector<VariableWidthLines> &generate();
/*!
* Gets the toolpaths, if this called before \p generate() it will first generate the Toolpaths
* \return a reference to the toolpaths
*/
const std::vector<VariableWidthLines> &getToolPaths();
/*!
* Compute the inner contour of the walls. This contour indicates where the walled area ends and its infill begins.
* The inside can then be filled, e.g. with skin/infill for the walls of a part, or with a pattern in the case of
* infill with extra infill walls.
*/
void separateOutInnerContour();
/*!
* Gets the inner contour of the area which is inside of the generated tool
* paths.
*
* If the walls haven't been generated yet, this will lazily call the
* \p generate() function to generate the walls with variable width.
* The resulting polygon will snugly match the inside of the variable-width
* walls where the walls get limited by the LimitedBeadingStrategy to a
* maximum wall count.
* If there are no walls, the outline will be returned.
* \return The inner contour of the generated walls.
*/
const Polygons& getInnerContour();
/*!
* Removes empty paths from the toolpaths
* \param toolpaths the VariableWidthPaths generated with \p generate()
* \return true if there are still paths left. If all toolpaths were removed it returns false
*/
static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths);
/*!
* Get the order constraints of the insets when printing walls per region / hole.
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
*
* Odd walls should always go after their enclosing wall polygons.
*
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/
static std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
protected:
/*!
* Stitch the polylines together and form closed polygons.
*
* Works on both toolpaths and inner contours simultaneously.
*/
static void stitchToolPaths(std::vector<VariableWidthLines> &toolpaths, coord_t bead_width_x);
/*!
* Remove polylines shorter than half the smallest line width along that polyline.
*/
static void removeSmallLines(std::vector<VariableWidthLines> &toolpaths);
/*!
* Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided
* settings.
* \param settings The settings as provided by the user
* \return
*/
static void simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths);
private:
const Polygons& outline; //<! A reference to the outline polygon that is the designated area
coord_t bead_width_0; //<! The nominal or first extrusion line width with which libArachne generates its walls
coord_t bead_width_x; //<! The subsequently extrusion line width with which libArachne generates its walls if WallToolPaths was called with the nominal_bead_width Constructor this is the same as bead_width_0
size_t inset_count; //<! The maximum number of walls to generate
coord_t wall_0_inset; //<! How far to inset the outer wall. Should only be applied when printing the actual walls, not extra infill/skin/support walls.
bool print_thin_walls; //<! Whether to enable the widening beading meta-strategy for thin features
coord_t min_feature_size; //<! The minimum size of the features that can be widened by the widening beading meta-strategy. Features thinner than that will not be printed
coord_t min_bead_width; //<! The minimum bead size to use when widening thin model features with the widening beading meta-strategy
double small_area_length; //<! The length of the small features which are to be filtered out, this is squared into a surface
bool toolpaths_generated; //<! Are the toolpaths generated
std::vector<VariableWidthLines> toolpaths; //<! The generated toolpaths
Polygons inner_contour; //<! The inner contour of the generated toolpaths
coord_t wall_transition_filter_deviation; //!< The allowed line width deviation induced by filtering
const PrintObjectConfig &print_object_config;
};
} // namespace Slic3r::Arachne
#endif // CURAENGINE_WALLTOOLPATHS_H

View File

@ -0,0 +1,18 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "ExtrusionJunction.hpp"
namespace Slic3r::Arachne
{
bool ExtrusionJunction::operator ==(const ExtrusionJunction& other) const
{
return p == other.p
&& w == other.w
&& perimeter_index == other.perimeter_index;
}
ExtrusionJunction::ExtrusionJunction(const Point p, const coord_t w, const coord_t perimeter_index) : p(p), w(w), perimeter_index(perimeter_index) {}
}

View File

@ -0,0 +1,59 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_EXTRUSION_JUNCTION_H
#define UTILS_EXTRUSION_JUNCTION_H
#include "../../Point.hpp"
namespace Slic3r::Arachne
{
/*!
* This struct represents one vertex in an extruded path.
*
* It contains information on how wide the extruded path must be at this point,
* and which perimeter it represents.
*/
struct ExtrusionJunction
{
/*!
* The position of the centreline of the path when it reaches this junction.
* This is the position that should end up in the g-code eventually.
*/
Point p;
/*!
* The width of the extruded path at this junction.
*/
coord_t w;
/*!
* Which perimeter this junction is part of.
*
* Perimeters are counted from the outside inwards. The outer wall has index
* 0.
*/
size_t perimeter_index;
ExtrusionJunction(const Point p, const coord_t w, const coord_t perimeter_index);
bool operator==(const ExtrusionJunction& other) const;
};
inline Point operator-(const ExtrusionJunction& a, const ExtrusionJunction& b)
{
return a.p - b.p;
}
// Identity function, used to be able to make templated algorithms that do their operations on 'point-like' input.
inline const Point& make_point(const ExtrusionJunction& ej)
{
return ej.p;
}
using LineJunctions = std::vector<ExtrusionJunction>; //<! The junctions along a line without further information. See \ref ExtrusionLine for a more extensive class.
}
#endif // UTILS_EXTRUSION_JUNCTION_H

View File

@ -0,0 +1,280 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include <algorithm>
#include "ExtrusionLine.hpp"
#include "linearAlg2D.hpp"
#include "../../PerimeterGenerator.hpp"
namespace Slic3r::Arachne
{
ExtrusionLine::ExtrusionLine(const size_t inset_idx, const bool is_odd) : inset_idx(inset_idx), is_odd(is_odd), is_closed(false) {}
int64_t ExtrusionLine::getLength() const
{
if (junctions.empty())
return 0;
int64_t len = 0;
ExtrusionJunction prev = junctions.front();
for (const ExtrusionJunction &next : junctions) {
len += (next.p - prev.p).cast<int64_t>().norm();
prev = next;
}
if (is_closed)
len += (front().p - back().p).cast<int64_t>().norm();
return len;
}
coord_t ExtrusionLine::getMinimalWidth() const
{
return std::min_element(junctions.cbegin(), junctions.cend(),
[](const ExtrusionJunction& l, const ExtrusionJunction& r)
{
return l.w < r.w;
})->w;
}
void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared, const int64_t maximum_extrusion_area_deviation)
{
const size_t min_path_size = is_closed ? 3 : 2;
if (junctions.size() <= min_path_size)
return;
// TODO: allow for the first point to be removed in case of simplifying closed Extrusionlines.
/* ExtrusionLines are treated as (open) polylines, so in case an ExtrusionLine is actually a closed polygon, its
* starting and ending points will be equal (or almost equal). Therefore, the simplification of the ExtrusionLine
* should not touch the first and last points. As a result, start simplifying from point at index 1.
* */
std::vector<ExtrusionJunction> new_junctions;
// Starting junction should always exist in the simplified path
new_junctions.emplace_back(junctions.front());
/* Initially, previous_previous is always the same as previous because, for open ExtrusionLines the last junction
* cannot be taken into consideration when checking the points at index 1. For closed ExtrusionLines, the first and
* last junctions are anyway the same.
* */
ExtrusionJunction previous_previous = junctions.front();
ExtrusionJunction previous = junctions.front();
/* When removing a vertex, we check the height of the triangle of the area
being removed from the original polygon by the simplification. However,
when consecutively removing multiple vertices the height of the previously
removed vertices w.r.t. the shortcut path changes.
In order to not recompute the new height value of previously removed
vertices we compute the height of a representative triangle, which covers
the same amount of area as the area being cut off. We use the Shoelace
formula to accumulate the area under the removed segments. This works by
computing the area in a 'fan' where each of the blades of the fan go from
the origin to one of the segments. While removing vertices the area in
this fan accumulates. By subtracting the area of the blade connected to
the short-cutting segment we obtain the total area of the cutoff region.
From this area we compute the height of the representative triangle using
the standard formula for a triangle area: A = .5*b*h
*/
const ExtrusionJunction& initial = junctions.at(1);
int64_t accumulated_area_removed = int64_t(previous.p.x()) * int64_t(initial.p.y()) - int64_t(previous.p.y()) * int64_t(initial.p.x()); // Twice the Shoelace formula for area of polygon per line segment.
for (size_t point_idx = 1; point_idx < junctions.size() - 1; point_idx++)
{
const ExtrusionJunction& current = junctions[point_idx];
// Spill over in case of overflow, unless the [next] vertex will then be equal to [previous].
const bool spill_over = point_idx + 1 == junctions.size() && new_junctions.size() > 1;
ExtrusionJunction& next = spill_over ? new_junctions[0] : junctions[point_idx + 1];
const int64_t removed_area_next = int64_t(current.p.x()) * int64_t(next.p.y()) - int64_t(current.p.y()) * int64_t(next.p.x()); // Twice the Shoelace formula for area of polygon per line segment.
const int64_t negative_area_closing = int64_t(next.p.x()) * int64_t(previous.p.y()) - int64_t(next.p.y()) * int64_t(previous.p.x()); // Area between the origin and the short-cutting segment
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<coord_t>(0.025))
{
// We're allowed to always delete segments of less than 5 micron. The width in this case doesn't matter that much.
continue;
}
const int64_t area_removed_so_far = accumulated_area_removed + negative_area_closing; // Close the shortcut area polygon
const int64_t base_length_2 = (next - previous).cast<int64_t>().squaredNorm();
if (base_length_2 == 0) // Two line segments form a line back and forth with no area.
{
continue; // Remove the junction (vertex).
}
//We want to check if the height of the triangle formed by previous, current and next vertices is less than allowed_error_distance_squared.
//1/2 L = A [actual area is half of the computed shoelace value] // Shoelace formula is .5*(...) , but we simplify the computation and take out the .5
//A = 1/2 * b * h [triangle area formula]
//L = b * h [apply above two and take out the 1/2]
//h = L / b [divide by b]
//h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor]
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
coord_t weighted_average_width;
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next, weighted_average_width);
if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
&& extrusion_area_error <= maximum_extrusion_area_deviation)
{
// Remove the current junction (vertex).
continue;
}
if (length2 < smallest_line_segment_squared
&& height_2 <= allowed_error_distance_squared) // Removing the junction (vertex) doesn't introduce too much error.
{
const int64_t next_length2 = (current - next).cast<int64_t>().squaredNorm();
if (next_length2 > 4 * smallest_line_segment_squared)
{
// Special case; The next line is long. If we were to remove this, it could happen that we get quite noticeable artifacts.
// We should instead move this point to a location where both edges are kept and then remove the previous point that we wanted to keep.
// By taking the intersection of these two lines, we get a point that preserves the direction (so it makes the corner a bit more pointy).
// We just need to be sure that the intersection point does not introduce an artifact itself.
Point intersection_point;
bool has_intersection = Line(previous_previous.p, previous.p).intersection_infinite(Line(current.p, next.p), &intersection_point);
if (!has_intersection
|| Line::distance_to_infinite_squared(intersection_point, previous.p, current.p) > double(allowed_error_distance_squared)
|| (intersection_point - previous.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared // The intersection point is way too far from the 'previous'
|| (intersection_point - next.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared) // and 'next' points, so it shouldn't replace 'current'
{
// We can't find a better spot for it, but the size of the line is more than 5 micron.
// So the only thing we can do here is leave it in...
}
else
{
// New point seems like a valid one.
const ExtrusionJunction new_to_add = ExtrusionJunction(intersection_point, current.w, current.perimeter_index);
// If there was a previous point added, remove it.
if(!new_junctions.empty())
{
new_junctions.pop_back();
previous = previous_previous;
}
// The junction (vertex) is replaced by the new one.
accumulated_area_removed = removed_area_next; // So that in the next iteration it's the area between the origin, [previous] and [current]
previous_previous = previous;
previous = new_to_add; // Note that "previous" is only updated if we don't remove the junction (vertex).
new_junctions.push_back(new_to_add);
continue;
}
}
else
{
continue; // Remove the junction (vertex).
}
}
// The junction (vertex) isn't removed.
accumulated_area_removed = removed_area_next; // So that in the next iteration it's the area between the origin, [previous] and [current]
previous_previous = previous;
previous = current; // Note that "previous" is only updated if we don't remove the junction (vertex).
new_junctions.push_back(current);
}
// Ending junction (vertex) should always exist in the simplified path
new_junctions.emplace_back(junctions.back());
/* In case this is a closed polygon (instead of a poly-line-segments), the invariant that the first and last points are the same should be enforced.
* Since one of them didn't move, and the other can't have been moved further than the constraints, if originally equal, they can simply be equated.
*/
if ((junctions.front().p - junctions.back().p).cast<int64_t>().squaredNorm() == 0)
{
new_junctions.back().p = junctions.front().p;
}
junctions = new_junctions;
}
int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width)
{
/*
* A B C A C
* --------------- **************
* | | ------------------------------------------
* | |--------------------------| B removed | |***************************|
* | | | ---------> | | |
* | |--------------------------| | |***************************|
* | | ------------------------------------------
* --------------- ^ **************
* ^ B.w + C.w / 2 ^
* A.w + B.w / 2 new_width = weighted_average_width
*
*
* ******** denote the total extrusion area deviation error in the consecutive segments as a result of using the
* weighted-average width for the entire extrusion line.
*
* */
const int64_t ab_length = (B - A).cast<int64_t>().norm();
const int64_t bc_length = (C - B).cast<int64_t>().norm();
const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w));
if (width_diff > 1)
{
// Adjust the width only if there is a difference, or else the rounding errors may produce the wrong
// weighted average value.
const int64_t ab_weight = (A.w + B.w) / 2;
const int64_t bc_weight = (B.w + C.w) / 2;
assert(((ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm()) <= std::numeric_limits<coord_t>::max());
weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast<int64_t>().norm();
assert((int64_t(std::abs(ab_weight - weighted_average_width)) * ab_length + int64_t(std::abs(bc_weight - weighted_average_width)) * bc_length) <= double(std::numeric_limits<int64_t>::max()));
return std::abs(ab_weight - weighted_average_width) * ab_length + std::abs(bc_weight - weighted_average_width) * bc_length;
}
else
{
// If the width difference is very small, then select the width of the segment that is longer
weighted_average_width = ab_length > bc_length ? A.w : B.w;
assert((int64_t(width_diff) * int64_t(bc_length)) <= std::numeric_limits<coord_t>::max());
assert((int64_t(width_diff) * int64_t(ab_length)) <= std::numeric_limits<coord_t>::max());
return ab_length > bc_length ? width_diff * bc_length : width_diff * ab_length;
}
}
bool ExtrusionLine::is_contour() const
{
if (!this->is_closed)
return false;
Polygon poly;
poly.points.reserve(this->junctions.size());
for (const ExtrusionJunction &junction : this->junctions)
poly.points.emplace_back(junction.p);
// Arachne produces contour with clockwise orientation and holes with counterclockwise orientation.
return poly.is_clockwise();
}
double ExtrusionLine::area() const
{
assert(this->is_closed);
double a = 0.;
if (this->junctions.size() >= 3) {
Vec2d p1 = this->junctions.back().p.cast<double>();
for (const ExtrusionJunction &junction : this->junctions) {
Vec2d p2 = junction.p.cast<double>();
a += cross2(p1, p2);
p1 = p2;
}
}
return 0.5 * a;
}
} // namespace Slic3r::Arachne
namespace Slic3r {
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow)
{
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), SCALED_EPSILON));
}
}
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow)
{
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion);
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), SCALED_EPSILON));
}
} // namespace Slic3r

View File

@ -0,0 +1,307 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_EXTRUSION_LINE_H
#define UTILS_EXTRUSION_LINE_H
#include "ExtrusionJunction.hpp"
#include "../../Polyline.hpp"
#include "../../Polygon.hpp"
#include "../../BoundingBox.hpp"
#include "../../ExtrusionEntity.hpp"
#include "../../Flow.hpp"
#include "../../../clipper/clipper_z.hpp"
namespace Slic3r {
class ThickPolyline;
}
namespace Slic3r::Arachne
{
/*!
* Represents a polyline (not just a line) that is to be extruded with variable
* line width.
*
* This polyline is a sequence of \ref ExtrusionJunction, with a bit of metadata
* about which inset it represents.
*/
struct ExtrusionLine
{
/*!
* Which inset this path represents, counted from the outside inwards.
*
* The outer wall has index 0.
*/
size_t inset_idx;
/*!
* If a thin piece needs to be printed with an odd number of walls (e.g. 5
* walls) then there will be one wall in the middle that is not a loop. This
* field indicates whether this path is such a line through the middle, that
* has no companion line going back on the other side and is not a closed
* loop.
*/
bool is_odd;
/*!
* Whether this is a closed polygonal path
*/
bool is_closed;
/*!
* Gets the number of vertices in this polygon.
* \return The number of vertices in this polygon.
*/
size_t size() const { return junctions.size(); }
/*!
* Whether there are no junctions.
*/
bool empty() const { return junctions.empty(); }
/*!
* The list of vertices along which this path runs.
*
* Each junction has a width, making this path a variable-width path.
*/
std::vector<ExtrusionJunction> junctions;
ExtrusionLine(const size_t inset_idx, const bool is_odd);
ExtrusionLine() : inset_idx(-1), is_odd(true), is_closed(false) {}
ExtrusionLine(const ExtrusionLine &other) : inset_idx(other.inset_idx), is_odd(other.is_odd), is_closed(other.is_closed), junctions(other.junctions) {}
ExtrusionLine &operator=(ExtrusionLine &&other)
{
junctions = std::move(other.junctions);
inset_idx = other.inset_idx;
is_odd = other.is_odd;
is_closed = other.is_closed;
return *this;
}
ExtrusionLine &operator=(const ExtrusionLine &other)
{
junctions = other.junctions;
inset_idx = other.inset_idx;
is_odd = other.is_odd;
is_closed = other.is_closed;
return *this;
}
std::vector<ExtrusionJunction>::const_iterator begin() const { return junctions.begin(); }
std::vector<ExtrusionJunction>::const_iterator end() const { return junctions.end(); }
std::vector<ExtrusionJunction>::const_reverse_iterator rbegin() const { return junctions.rbegin(); }
std::vector<ExtrusionJunction>::const_reverse_iterator rend() const { return junctions.rend(); }
std::vector<ExtrusionJunction>::const_reference front() const { return junctions.front(); }
std::vector<ExtrusionJunction>::const_reference back() const { return junctions.back(); }
const ExtrusionJunction &operator[](unsigned int index) const { return junctions[index]; }
ExtrusionJunction &operator[](unsigned int index) { return junctions[index]; }
std::vector<ExtrusionJunction>::iterator begin() { return junctions.begin(); }
std::vector<ExtrusionJunction>::iterator end() { return junctions.end(); }
std::vector<ExtrusionJunction>::reference front() { return junctions.front(); }
std::vector<ExtrusionJunction>::reference back() { return junctions.back(); }
template<typename... Args> void emplace_back(Args &&...args) { junctions.emplace_back(args...); }
void remove(unsigned int index) { junctions.erase(junctions.begin() + index); }
void insert(size_t index, const ExtrusionJunction &p) { junctions.insert(junctions.begin() + index, p); }
template<class iterator>
std::vector<ExtrusionJunction>::iterator insert(std::vector<ExtrusionJunction>::const_iterator pos, iterator first, iterator last)
{
return junctions.insert(pos, first, last);
}
void clear() { junctions.clear(); }
void reverse() { std::reverse(junctions.begin(), junctions.end()); }
/*!
* Sum the total length of this path.
*/
int64_t getLength() const;
int64_t polylineLength() const { return getLength(); }
/*!
* Put all junction locations into a polygon object.
*
* When this path is not closed the returned Polygon should be handled as a polyline, rather than a polygon.
*/
Polygon toPolygon() const
{
Polygon ret;
for (const ExtrusionJunction &j : junctions)
ret.points.emplace_back(j.p);
return ret;
}
/*!
* Get the minimal width of this path
*/
coord_t getMinimalWidth() const;
/*!
* Removes vertices of the ExtrusionLines to make sure that they are not too high
* resolution.
*
* This removes junctions which are connected to line segments that are shorter
* than the `smallest_line_segment`, unless that would introduce a deviation
* in the contour of more than `allowed_error_distance`.
*
* Criteria:
* 1. Never remove a junction if either of the connected segments is larger than \p smallest_line_segment
* 2. Never remove a junction if the distance between that junction and the final resulting polygon would be higher
* than \p allowed_error_distance
* 3. The direction of segments longer than \p smallest_line_segment always
* remains unaltered (but their end points may change if it is connected to
* a small segment)
* 4. Never remove a junction if it has a distinctively different width than the next junction, as this can
* introduce unwanted irregularities on the wall widths.
*
* Simplify uses a heuristic and doesn't necessarily remove all removable
* vertices under the above criteria, but simplify may never violate these
* criteria. Unless the segments or the distance is smaller than the
* rounding error of 5 micron.
*
* Vertices which introduce an error of less than 5 microns are removed
* anyway, even if the segments are longer than the smallest line segment.
* This makes sure that (practically) co-linear line segments are joined into
* a single line segment.
* \param smallest_line_segment Maximal length of removed line segments.
* \param allowed_error_distance If removing a vertex introduces a deviation
* from the original path that is more than this distance, the vertex may
* not be removed.
* \param maximum_extrusion_area_deviation The maximum extrusion area deviation allowed when removing intermediate
* junctions from a straight ExtrusionLine
*/
void simplify(int64_t smallest_line_segment_squared, int64_t allowed_error_distance_squared, int64_t maximum_extrusion_area_deviation);
/*!
* Computes and returns the total area error (in μm²) of the AB and BC segments of an ABC straight ExtrusionLine
* when the junction B with a width B.w is removed from the ExtrusionLine. The area changes due to the fact that the
* new simplified line AC has a uniform width which equals to the weighted average of the width of the subsegments
* (based on their length).
*
* \param A Start point of the 3-point-straight line
* \param B Intermediate point of the 3-point-straight line
* \param C End point of the 3-point-straight line
* \param weighted_average_width The weighted average of the widths of the two colinear extrusion segments
* */
static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width);
bool is_contour() const;
double area() const;
};
static inline Slic3r::ThickPolyline to_thick_polyline(const Arachne::ExtrusionLine &line_junctions)
{
assert(line_junctions.size() >= 2);
Slic3r::ThickPolyline out;
out.points.emplace_back(line_junctions.front().p);
out.width.emplace_back(line_junctions.front().w);
out.points.emplace_back(line_junctions[1].p);
out.width.emplace_back(line_junctions[1].w);
auto it_prev = line_junctions.begin() + 1;
for (auto it = line_junctions.begin() + 2; it != line_junctions.end(); ++it) {
out.points.emplace_back(it->p);
out.width.emplace_back(it_prev->w);
out.width.emplace_back(it->w);
it_prev = it;
}
return out;
}
static inline Slic3r::ThickPolyline to_thick_polyline(const ClipperLib_Z::Path &path)
{
assert(path.size() >= 2);
Slic3r::ThickPolyline out;
out.points.emplace_back(path.front().x(), path.front().y());
out.width.emplace_back(path.front().z());
out.points.emplace_back(path[1].x(), path[1].y());
out.width.emplace_back(path[1].z());
auto it_prev = path.begin() + 1;
for (auto it = path.begin() + 2; it != path.end(); ++it) {
out.points.emplace_back(it->x(), it->y());
out.width.emplace_back(it_prev->z());
out.width.emplace_back(it->z());
it_prev = it;
}
return out;
}
static inline Polygon to_polygon(const ExtrusionLine &line)
{
Polygon out;
assert(line.junctions.size() >= 3);
assert(line.junctions.front().p == line.junctions.back().p);
out.points.reserve(line.junctions.size() - 1);
for (auto it = line.junctions.begin(); it != line.junctions.end() - 1; ++it)
out.points.emplace_back(it->p);
return out;
}
#if 0
static BoundingBox get_extents(const ExtrusionLine &extrusion_line)
{
BoundingBox bbox;
for (const ExtrusionJunction &junction : extrusion_line.junctions)
bbox.merge(junction.p);
return bbox;
}
static BoundingBox get_extents(const std::vector<ExtrusionLine> &extrusion_lines)
{
BoundingBox bbox;
for (const ExtrusionLine &extrusion_line : extrusion_lines)
bbox.merge(get_extents(extrusion_line));
return bbox;
}
static BoundingBox get_extents(const std::vector<const ExtrusionLine *> &extrusion_lines)
{
BoundingBox bbox;
for (const ExtrusionLine *extrusion_line : extrusion_lines) {
assert(extrusion_line != nullptr);
bbox.merge(get_extents(*extrusion_line));
}
return bbox;
}
static Points to_points(const ExtrusionLine &extrusion_line)
{
Points points;
points.reserve(extrusion_line.junctions.size());
for (const ExtrusionJunction &junction : extrusion_line.junctions)
points.emplace_back(junction.p);
return points;
}
static std::vector<Points> to_points(const std::vector<const ExtrusionLine *> &extrusion_lines)
{
std::vector<Points> points;
for (const ExtrusionLine *extrusion_line : extrusion_lines) {
assert(extrusion_line != nullptr);
points.emplace_back(to_points(*extrusion_line));
}
return points;
}
#endif
using VariableWidthLines = std::vector<ExtrusionLine>; //<! The ExtrusionLines generated by libArachne
} // namespace Slic3r::Arachne
namespace Slic3r {
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow);
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow);
} // namespace Slic3r
#endif // UTILS_EXTRUSION_LINE_H

View File

@ -0,0 +1,39 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_HALF_EDGE_H
#define UTILS_HALF_EDGE_H
#include <forward_list>
#include <optional>
namespace Slic3r::Arachne
{
template<typename node_data_t, typename edge_data_t, typename derived_node_t, typename derived_edge_t>
class HalfEdgeNode;
template<typename node_data_t, typename edge_data_t, typename derived_node_t, typename derived_edge_t>
class HalfEdge
{
using edge_t = derived_edge_t;
using node_t = derived_node_t;
public:
edge_data_t data;
edge_t* twin = nullptr;
edge_t* next = nullptr;
edge_t* prev = nullptr;
node_t* from = nullptr;
node_t* to = nullptr;
HalfEdge(edge_data_t data)
: data(data)
{}
bool operator==(const edge_t& other)
{
return this == &other;
}
};
} // namespace Slic3r::Arachne
#endif // UTILS_HALF_EDGE_H

View File

@ -0,0 +1,29 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_HALF_EDGE_GRAPH_H
#define UTILS_HALF_EDGE_GRAPH_H
#include <list>
#include <cassert>
#include "HalfEdge.hpp"
#include "HalfEdgeNode.hpp"
namespace Slic3r::Arachne
{
template<class node_data_t, class edge_data_t, class derived_node_t, class derived_edge_t> // types of data contained in nodes and edges
class HalfEdgeGraph
{
public:
using edge_t = derived_edge_t;
using node_t = derived_node_t;
std::list<edge_t> edges;
std::list<node_t> nodes;
};
} // namespace Slic3r::Arachne
#endif // UTILS_HALF_EDGE_GRAPH_H

View File

@ -0,0 +1,38 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_HALF_EDGE_NODE_H
#define UTILS_HALF_EDGE_NODE_H
#include <list>
#include "../../Point.hpp"
namespace Slic3r::Arachne
{
template<typename node_data_t, typename edge_data_t, typename derived_node_t, typename derived_edge_t>
class HalfEdge;
template<typename node_data_t, typename edge_data_t, typename derived_node_t, typename derived_edge_t>
class HalfEdgeNode
{
using edge_t = derived_edge_t;
using node_t = derived_node_t;
public:
node_data_t data;
Point p;
edge_t* incident_edge = nullptr;
HalfEdgeNode(node_data_t data, Point p)
: data(data)
, p(p)
{}
bool operator==(const node_t& other)
{
return this == &other;
}
};
} // namespace Slic3r::Arachne
#endif // UTILS_HALF_EDGE_NODE_H

View File

@ -0,0 +1,180 @@
//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_POLYGONS_POINT_INDEX_H
#define UTILS_POLYGONS_POINT_INDEX_H
#include <vector>
#include "../../Point.hpp"
#include "../../Polygon.hpp"
namespace Slic3r::Arachne
{
// Identity function, used to be able to make templated algorithms where the input is sometimes points, sometimes things that contain or can be converted to points.
inline const Point &make_point(const Point &p) { return p; }
/*!
* A class for iterating over the points in one of the polygons in a \ref Polygons object
*/
template<typename Paths>
class PathsPointIndex
{
public:
/*!
* The polygons into which this index is indexing.
*/
const Paths* polygons; // (pointer to const polygons)
unsigned int poly_idx; //!< The index of the polygon in \ref PolygonsPointIndex::polygons
unsigned int point_idx; //!< The index of the point in the polygon in \ref PolygonsPointIndex::polygons
/*!
* Constructs an empty point index to no polygon.
*
* This is used as a placeholder for when there is a zero-construction
* needed. Since the `polygons` field is const you can't ever make this
* initialisation useful.
*/
PathsPointIndex() : polygons(nullptr), poly_idx(0), point_idx(0) {}
/*!
* Constructs a new point index to a vertex of a polygon.
* \param polygons The Polygons instance to which this index points.
* \param poly_idx The index of the sub-polygon to point to.
* \param point_idx The index of the vertex in the sub-polygon.
*/
PathsPointIndex(const Paths *polygons, unsigned int poly_idx, unsigned int point_idx) : polygons(polygons), poly_idx(poly_idx), point_idx(point_idx) {}
/*!
* Copy constructor to copy these indices.
*/
PathsPointIndex(const PathsPointIndex& original) = default;
Point p() const
{
if (!polygons)
return {0, 0};
return make_point((*polygons)[poly_idx][point_idx]);
}
/*!
* \brief Returns whether this point is initialised.
*/
bool initialized() const { return polygons; }
/*!
* Get the polygon to which this PolygonsPointIndex refers
*/
const Polygon &getPolygon() const { return (*polygons)[poly_idx]; }
/*!
* Test whether two iterators refer to the same polygon in the same polygon list.
*
* \param other The PolygonsPointIndex to test for equality
* \return Wether the right argument refers to the same polygon in the same ListPolygon as the left argument.
*/
bool operator==(const PathsPointIndex &other) const
{
return polygons == other.polygons && poly_idx == other.poly_idx && point_idx == other.point_idx;
}
bool operator!=(const PathsPointIndex &other) const
{
return !(*this == other);
}
bool operator<(const PathsPointIndex &other) const
{
return this->p() < other.p();
}
PathsPointIndex &operator=(const PathsPointIndex &other)
{
polygons = other.polygons;
poly_idx = other.poly_idx;
point_idx = other.point_idx;
return *this;
}
//! move the iterator forward (and wrap around at the end)
PathsPointIndex &operator++()
{
point_idx = (point_idx + 1) % (*polygons)[poly_idx].size();
return *this;
}
//! move the iterator backward (and wrap around at the beginning)
PathsPointIndex &operator--()
{
if (point_idx == 0)
point_idx = (*polygons)[poly_idx].size();
point_idx--;
return *this;
}
//! move the iterator forward (and wrap around at the end)
PathsPointIndex next() const
{
PathsPointIndex ret(*this);
++ret;
return ret;
}
//! move the iterator backward (and wrap around at the beginning)
PathsPointIndex prev() const
{
PathsPointIndex ret(*this);
--ret;
return ret;
}
};
using PolygonsPointIndex = PathsPointIndex<Polygons>;
/*!
* Locator to extract a line segment out of a \ref PolygonsPointIndex
*/
struct PolygonsPointIndexSegmentLocator
{
std::pair<Point, Point> operator()(const PolygonsPointIndex &val) const
{
const Polygon &poly = (*val.polygons)[val.poly_idx];
Point start = poly[val.point_idx];
unsigned int next_point_idx = (val.point_idx + 1) % poly.size();
Point end = poly[next_point_idx];
return std::pair<Point, Point>(start, end);
}
};
/*!
* Locator of a \ref PolygonsPointIndex
*/
template<typename Paths>
struct PathsPointIndexLocator
{
Point operator()(const PathsPointIndex<Paths>& val) const
{
return make_point(val.p());
}
};
using PolygonsPointIndexLocator = PathsPointIndexLocator<Polygons>;
}//namespace Slic3r::Arachne
namespace std
{
/*!
* Hash function for \ref PolygonsPointIndex
*/
template <>
struct hash<Slic3r::Arachne::PolygonsPointIndex>
{
size_t operator()(const Slic3r::Arachne::PolygonsPointIndex& lpi) const
{
return Slic3r::PointHash{}(lpi.p());
}
};
}//namespace std
#endif//UTILS_POLYGONS_POINT_INDEX_H

View File

@ -0,0 +1,31 @@
//Copyright (c) 2020 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_POLYGONS_SEGMENT_INDEX_H
#define UTILS_POLYGONS_SEGMENT_INDEX_H
#include <vector>
#include "PolygonsPointIndex.hpp"
namespace Slic3r::Arachne
{
/*!
* A class for iterating over the points in one of the polygons in a \ref Polygons object
*/
class PolygonsSegmentIndex : public PolygonsPointIndex
{
public:
PolygonsSegmentIndex() : PolygonsPointIndex(){};
PolygonsSegmentIndex(const Polygons *polygons, unsigned int poly_idx, unsigned int point_idx) : PolygonsPointIndex(polygons, poly_idx, point_idx){};
Point from() const { return PolygonsPointIndex::p(); }
Point to() const { return PolygonsSegmentIndex::next().p(); }
};
} // namespace Slic3r::Arachne
#endif//UTILS_POLYGONS_SEGMENT_INDEX_H

View File

@ -0,0 +1,42 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "PolylineStitcher.hpp"
#include "ExtrusionLine.hpp"
namespace Slic3r::Arachne {
template<> bool PolylineStitcher<VariableWidthLines, ExtrusionLine, ExtrusionJunction>::canReverse(const PathsPointIndex<VariableWidthLines> &ppi)
{
if ((*ppi.polygons)[ppi.poly_idx].is_odd)
return true;
else
return false;
}
template<> bool PolylineStitcher<Polygons, Polygon, Point>::canReverse(const PathsPointIndex<Polygons> &)
{
return true;
}
template<> bool PolylineStitcher<VariableWidthLines, ExtrusionLine, ExtrusionJunction>::canConnect(const ExtrusionLine &a, const ExtrusionLine &b)
{
return a.is_odd == b.is_odd;
}
template<> bool PolylineStitcher<Polygons, Polygon, Point>::canConnect(const Polygon &, const Polygon &)
{
return true;
}
template<> bool PolylineStitcher<VariableWidthLines, ExtrusionLine, ExtrusionJunction>::isOdd(const ExtrusionLine &line)
{
return line.is_odd;
}
template<> bool PolylineStitcher<Polygons, Polygon, Point>::isOdd(const Polygon &)
{
return false;
}
} // namespace Slic3r::Arachne

View File

@ -0,0 +1,234 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_POLYLINE_STITCHER_H
#define UTILS_POLYLINE_STITCHER_H
#include "SparsePointGrid.hpp"
#include "PolygonsPointIndex.hpp"
#include "../../Polygon.hpp"
#include <unordered_set>
#include <cassert>
namespace Slic3r::Arachne
{
/*!
* Class for stitching polylines into longer polylines or into polygons
*/
template<typename Paths, typename Path, typename Junction>
class PolylineStitcher
{
public:
/*!
* Stitch together the separate \p lines into \p result_lines and if they
* can be closed into \p result_polygons.
*
* Only introduce new segments shorter than \p max_stitch_distance, and
* larger than \p snap_distance but always try to take the shortest
* connection possible.
*
* Only stitch polylines into closed polygons if they are larger than 3 *
* \p max_stitch_distance, in order to prevent small segments to
* accidentally get closed into a polygon.
*
* \warning Tiny polylines (smaller than 3 * max_stitch_distance) will not
* be closed into polygons.
*
* \note Resulting polylines and polygons are added onto the existing
* containers, so you can directly output onto a polygons container with
* existing polygons in it. However, you shouldn't call this function with
* the same parameter in \p lines as \p result_lines, because that would
* duplicate (some of) the polylines.
* \param lines The lines to stitch together.
* \param result_lines[out] The stitched parts that are not closed polygons
* will be stored in here.
* \param result_polygons[out] The stitched parts that were closed as
* polygons will be stored in here.
* \param max_stitch_distance The maximum distance that will be bridged to
* connect two lines.
* \param snap_distance Points closer than this distance are considered to
* be the same point.
*/
static void stitch(const Paths& lines, Paths& result_lines, Paths& result_polygons, coord_t max_stitch_distance = scaled<coord_t>(0.1), coord_t snap_distance = scaled<coord_t>(0.01))
{
if (lines.empty())
return;
SparsePointGrid<PathsPointIndex<Paths>, PathsPointIndexLocator<Paths>> grid(max_stitch_distance, lines.size() * 2);
// populate grid
for (size_t line_idx = 0; line_idx < lines.size(); line_idx++)
{
const auto line = lines[line_idx];
grid.insert(PathsPointIndex<Paths>(&lines, line_idx, 0));
grid.insert(PathsPointIndex<Paths>(&lines, line_idx, line.size() - 1));
}
std::vector<bool> processed(lines.size(), false);
for (size_t line_idx = 0; line_idx < lines.size(); line_idx++)
{
if (processed[line_idx])
{
continue;
}
processed[line_idx] = true;
const auto line = lines[line_idx];
bool should_close = isOdd(line);
Path chain = line;
bool closest_is_closing_polygon = false;
for (bool go_in_reverse_direction : { false, true }) // first go in the unreversed direction, to try to prevent the chain.reverse() operation.
{ // NOTE: Implementation only works for this order; we currently only re-reverse the chain when it's closed.
if (go_in_reverse_direction)
{ // try extending chain in the other direction
chain.reverse();
}
int64_t chain_length = chain.polylineLength();
while (true)
{
Point from = make_point(chain.back());
PathsPointIndex<Paths> closest;
coord_t closest_distance = std::numeric_limits<coord_t>::max();
grid.processNearby(from, max_stitch_distance,
std::function<bool (const PathsPointIndex<Paths>&)> (
[from, &chain, &closest, &closest_is_closing_polygon, &closest_distance, &processed, &chain_length, go_in_reverse_direction, max_stitch_distance, snap_distance, should_close]
(const PathsPointIndex<Paths>& nearby)->bool
{
bool is_closing_segment = false;
coord_t dist = (nearby.p().template cast<int64_t>() - from.template cast<int64_t>()).norm();
if (dist > max_stitch_distance)
{
return true; // keep looking
}
if ((nearby.p().template cast<int64_t>() - make_point(chain.front()).template cast<int64_t>()).squaredNorm() < snap_distance * snap_distance)
{
if (chain_length + dist < 3 * max_stitch_distance // prevent closing of small poly, cause it might be able to continue making a larger polyline
|| chain.size() <= 2) // don't make 2 vert polygons
{
return true; // look for a better next line
}
is_closing_segment = true;
if (!should_close)
{
dist += scaled<coord_t>(0.01); // prefer continuing polyline over closing a polygon; avoids closed zigzags from being printed separately
// continue to see if closing segment is also the closest
// there might be a segment smaller than [max_stitch_distance] which closes the polygon better
}
else
{
dist -= scaled<coord_t>(0.01); //Prefer closing the polygon if it's 100% even lines. Used to create closed contours.
//Continue to see if closing segment is also the closest.
}
}
else if (processed[nearby.poly_idx])
{ // it was already moved to output
return true; // keep looking for a connection
}
bool nearby_would_be_reversed = nearby.point_idx != 0;
nearby_would_be_reversed = nearby_would_be_reversed != go_in_reverse_direction; // flip nearby_would_be_reversed when searching in the reverse direction
if (!canReverse(nearby) && nearby_would_be_reversed)
{ // connecting the segment would reverse the polygon direction
return true; // keep looking for a connection
}
if (!canConnect(chain, (*nearby.polygons)[nearby.poly_idx]))
{
return true; // keep looking for a connection
}
if (dist < closest_distance)
{
closest_distance = dist;
closest = nearby;
closest_is_closing_polygon = is_closing_segment;
}
if (dist < snap_distance)
{ // we have found a good enough next line
return false; // stop looking for alternatives
}
return true; // keep processing elements
})
);
if (!closest.initialized() // we couldn't find any next line
|| closest_is_closing_polygon // we closed the polygon
)
{
break;
}
coord_t segment_dist = (make_point(chain.back()).template cast<int64_t>() - closest.p().template cast<int64_t>()).norm();
assert(segment_dist <= max_stitch_distance + scaled<coord_t>(0.01));
const size_t old_size = chain.size();
if (closest.point_idx == 0)
{
auto start_pos = (*closest.polygons)[closest.poly_idx].begin();
if (segment_dist < snap_distance)
{
++start_pos;
}
chain.insert(chain.end(), start_pos, (*closest.polygons)[closest.poly_idx].end());
}
else
{
auto start_pos = (*closest.polygons)[closest.poly_idx].rbegin();
if (segment_dist < snap_distance)
{
++start_pos;
}
chain.insert(chain.end(), start_pos, (*closest.polygons)[closest.poly_idx].rend());
}
for(size_t i = old_size; i < chain.size(); ++i) //Update chain length.
{
chain_length += (make_point(chain[i]).template cast<int64_t>() - make_point(chain[i - 1]).template cast<int64_t>()).norm();
}
should_close = should_close & !isOdd((*closest.polygons)[closest.poly_idx]); //If we connect an even to an odd line, we should no longer try to close it.
assert( ! processed[closest.poly_idx]);
processed[closest.poly_idx] = true;
}
if (closest_is_closing_polygon)
{
if (go_in_reverse_direction)
{ // re-reverse chain to retain original direction
// NOTE: not sure if this code could ever be reached, since if a polygon can be closed that should be already possible in the forward direction
chain.reverse();
}
break; // don't consider reverse direction
}
}
if (closest_is_closing_polygon)
{
result_polygons.emplace_back(chain);
}
else
{
PathsPointIndex<Paths> ppi_here(&lines, line_idx, 0);
if ( ! canReverse(ppi_here))
{ // Since closest_is_closing_polygon is false we went through the second iterations of the for-loop, where go_in_reverse_direction is true
// the polyline isn't allowed to be reversed, so we re-reverse it.
chain.reverse();
}
result_lines.emplace_back(chain);
}
}
}
/*!
* Whether a polyline is allowed to be reversed. (Not true for wall polylines which are not odd)
*/
static bool canReverse(const PathsPointIndex<Paths> &polyline);
/*!
* Whether two paths are allowed to be connected.
* (Not true for an odd and an even wall.)
*/
static bool canConnect(const Path &a, const Path &b);
static bool isOdd(const Path &line);
};
} // namespace Slic3r::Arachne
#endif // UTILS_POLYLINE_STITCHER_H

View File

@ -0,0 +1,133 @@
//Copyright (c) 2016 Scott Lenser
//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef UTILS_SPARSE_GRID_H
#define UTILS_SPARSE_GRID_H
#include <cassert>
#include <unordered_map>
#include <vector>
#include <functional>
#include "../../Point.hpp"
#include "SquareGrid.hpp"
namespace Slic3r::Arachne {
/*! \brief Sparse grid which can locate spatially nearby elements efficiently.
*
* \note This is an abstract template class which doesn't have any functions to insert elements.
* \see SparsePointGrid
*
* \tparam ElemT The element type to store.
*/
template<class ElemT> class SparseGrid : public SquareGrid
{
public:
using Elem = ElemT;
using GridPoint = SquareGrid::GridPoint;
using grid_coord_t = SquareGrid::grid_coord_t;
using GridMap = std::unordered_multimap<GridPoint, Elem, PointHash>;
using iterator = typename GridMap::iterator;
using const_iterator = typename GridMap::const_iterator;
/*! \brief Constructs a sparse grid with the specified cell size.
*
* \param[in] cell_size The size to use for a cell (square) in the grid.
* Typical values would be around 0.5-2x of expected query radius.
* \param[in] elem_reserve Number of elements to research space for.
* \param[in] max_load_factor Maximum average load factor before rehashing.
*/
SparseGrid(coord_t cell_size, size_t elem_reserve=0U, float max_load_factor=1.0f);
iterator begin() { return m_grid.begin(); }
iterator end() { return m_grid.end(); }
const_iterator begin() const { return m_grid.begin(); }
const_iterator end() const { return m_grid.end(); }
/*! \brief Returns all data within radius of query_pt.
*
* Finds all elements with location within radius of \p query_pt. May
* return additional elements that are beyond radius.
*
* Average running time is a*(1 + 2 * radius / cell_size)**2 +
* b*cnt where a and b are proportionality constance and cnt is
* the number of returned items. The search will return items in
* an area of (2*radius + cell_size)**2 on average. The max range
* of an item from the query_point is radius + cell_size.
*
* \param[in] query_pt The point to search around.
* \param[in] radius The search radius.
* \return Vector of elements found
*/
std::vector<Elem> getNearby(const Point &query_pt, coord_t radius) const;
/*! \brief Process elements from cells that might contain sought after points.
*
* Processes elements from cell that might have elements within \p
* radius of \p query_pt. Processes all elements that are within
* radius of query_pt. May process elements that are up to radius +
* cell_size from query_pt.
*
* \param[in] query_pt The point to search around.
* \param[in] radius The search radius.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell. Processing stops if function returns false.
* \return Whether we need to continue processing after this function
*/
bool processNearby(const Point &query_pt, coord_t radius, const std::function<bool(const ElemT &)> &process_func) const;
protected:
/*! \brief Process elements from the cell indicated by \p grid_pt.
*
* \param[in] grid_pt The grid coordinates of the cell.
* \param[in] process_func Processes each element. process_func(elem) is
* called for each element in the cell. Processing stops if function returns false.
* \return Whether we need to continue processing a next cell.
*/
bool processFromCell(const GridPoint &grid_pt, const std::function<bool(const Elem &)> &process_func) const;
/*! \brief Map from grid locations (GridPoint) to elements (Elem). */
GridMap m_grid;
};
template<class ElemT> SparseGrid<ElemT>::SparseGrid(coord_t cell_size, size_t elem_reserve, float max_load_factor) : SquareGrid(cell_size)
{
// Must be before the reserve call.
m_grid.max_load_factor(max_load_factor);
if (elem_reserve != 0U)
m_grid.reserve(elem_reserve);
}
template<class ElemT> bool SparseGrid<ElemT>::processFromCell(const GridPoint &grid_pt, const std::function<bool(const Elem &)> &process_func) const
{
auto grid_range = m_grid.equal_range(grid_pt);
for (auto iter = grid_range.first; iter != grid_range.second; ++iter)
if (!process_func(iter->second))
return false;
return true;
}
template<class ElemT>
bool SparseGrid<ElemT>::processNearby(const Point &query_pt, coord_t radius, const std::function<bool(const Elem &)> &process_func) const
{
return SquareGrid::processNearby(query_pt, radius, [&process_func, this](const GridPoint &grid_pt) { return processFromCell(grid_pt, process_func); });
}
template<class ElemT> std::vector<typename SparseGrid<ElemT>::Elem> SparseGrid<ElemT>::getNearby(const Point &query_pt, coord_t radius) const
{
std::vector<Elem> ret;
const std::function<bool(const Elem &)> process_func = [&ret](const Elem &elem) {
ret.push_back(elem);
return true;
};
processNearby(query_pt, radius, process_func);
return ret;
}
} // namespace Slic3r::Arachne
#endif // UTILS_SPARSE_GRID_H

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