Merge branch 'master' into fs_svg

# Conflicts:
#	src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
This commit is contained in:
Filip Sykala - NTB T15p 2023-04-17 15:54:02 +02:00
commit b3d2dbeeb3
40 changed files with 2692 additions and 296 deletions

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.6.0-alpha6
1.0.3 Added Voron Switchwire.
1.0.2 Updated g-code flavor and travel accelerations.
min_slic3r_version = 2.4.2
1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants.

View File

@ -7,7 +7,7 @@
name = Voron
# 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.0.2
config_version = 1.0.3
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/
@ -106,6 +106,28 @@ bed_model = printbed-v0-120.stl
bed_texture = bedtexture-v0-120.png
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
[printer_model:Voron_SW_afterburner]
name = Voron Switchwire
variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2
technology = FFF
family = Voron Switchwire Afterburner
bed_model = printbed-SW-MK52.stl
bed_texture = bedtexture-SW-250x210.png
bed_with_grid = 1
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
thumbnail = Voron_SW_thumbnail.png
[printer_model:Voron_SW]
name = Voron Switchwire
variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2
technology = FFF
family = Voron Switchwire Mobius
bed_model = printbed-SW-MK52.stl
bed_texture = bedtexture-SW-250x210.png
bed_with_grid = 1
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
thumbnail = Voron_SW_thumbnail.png
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface
@ -310,6 +332,18 @@ max_print_height = 120
printer_model = Voron_v0_120
printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6
[printer:*Voron_Switchwire*]
inherits = *common*
bed_shape = 0x0,250x0,250x210,0x210
max_print_height = 240
printer_model = Voron_SW
printer_notes = PRINTER_HAS_BOWDEN\nSTU\nE3DV6
[printer:*Voron_Switchwire_afterburner*]
inherits = *Voron_Switchwire*; *afterburner*
printer_model = Voron_SW_afterburner
printer_notes = STU\nE3DV6
[printer:Voron_v2_250 0.25 nozzle]
inherits = *Voron_v2_250*; *0.25nozzle*
@ -658,6 +692,89 @@ printer_variant = volcano 1.2
printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 0.25 nozzle]
inherits = *Voron_Switchwire*; *0.25nozzle*
[printer:Voron_Switchwire 0.3 nozzle]
inherits = *Voron_Switchwire*; *0.3nozzle*
[printer:Voron_Switchwire 0.4 nozzle]
inherits = *Voron_Switchwire*; *0.4nozzle*
[printer:Voron_Switchwire 0.5 nozzle]
inherits = *Voron_Switchwire*; *0.5nozzle*
[printer:Voron_Switchwire 0.6 nozzle]
inherits = *Voron_Switchwire*; *0.6nozzle*
[printer:Voron_Switchwire 0.8 nozzle]
inherits = *Voron_Switchwire*; *0.8nozzle*
[printer:Voron_Switchwire 0.6 volcano]
inherits = *Voron_Switchwire*; *0.6nozzle*; *volcano*
printer_variant = volcano 0.6
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 0.8 volcano]
inherits = *Voron_Switchwire*; *0.8nozzle*; *volcano*
printer_variant = volcano 0.8
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 1.0 volcano]
inherits = *Voron_Switchwire*; *1.0nozzle*; *volcano*
printer_variant = volcano 1.0
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 1.2 volcano]
inherits = *Voron_Switchwire*; *1.2nozzle*; *volcano*
printer_variant = volcano 1.2
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner 0.25 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.25nozzle*
[printer:Voron_Switchwire_afterburner 0.3 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.3nozzle*
[printer:Voron_Switchwire_afterburner 0.4 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.4nozzle*
[printer:Voron_Switchwire_afterburner 0.5 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.5nozzle*
[printer:Voron_Switchwire_afterburner 0.6 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.6nozzle*
[printer:Voron_Switchwire_afterburner 0.8 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.8nozzle*
[printer:Voron_Switchwire_afterburner volcano 0.6 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.6nozzle*; *volcano_afterburner*
printer_variant = volcano 0.6
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 0.8 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.8nozzle*; *volcano_afterburner*
printer_variant = volcano 0.8
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 1.0 nozzle]
inherits = *Voron_Switchwire_afterburner*; *1.0nozzle*; *volcano_afterburner*
printer_variant = volcano 1.0
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 1.2 nozzle]
inherits = *Voron_Switchwire_afterburner*; *1.2nozzle*; *volcano_afterburner*
printer_variant = volcano 1.2
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
# Common print preset, mostly derived from MK2 single material with a 0.4mm nozzle.
# All other print presets will derive from the *common* print preset.
@ -1022,6 +1139,22 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.05mm*; *0.5nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.5
[print:0.05mm 0.25nozzle SW]
inherits = *0.05mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.05mm 0.3nozzle SW]
inherits = *0.05mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.05mm 0.4nozzle SW]
inherits = *0.05mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.05mm 0.5nozzle SW]
inherits = *0.05mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.10mm 0.25nozzle V2]
inherits = *0.10mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25
@ -1094,6 +1227,30 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.10mm*; *0.8nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.8
[print:0.10mm 0.25nozzle SW]
inherits = *0.10mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.10mm 0.3nozzle SW]
inherits = *0.10mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.10mm 0.4nozzle SW]
inherits = *0.10mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.10mm 0.5nozzle SW]
inherits = *0.10mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.10mm 0.6nozzle SW]
inherits = *0.10mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.10mm 0.8nozzle SW]
inherits = *0.10mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.15mm 0.25nozzle V2]
inherits = *0.15mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25
@ -1182,6 +1339,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.15mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.15mm 0.25nozzle SW]
inherits = *0.15mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.15mm 0.3nozzle SW]
inherits = *0.15mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.15mm 0.4nozzle SW]
inherits = *0.15mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.15mm 0.5nozzle SW]
inherits = *0.15mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.15mm 0.6nozzle SW]
inherits = *0.15mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.15mm 0.8nozzle SW]
inherits = *0.15mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.15mm 1.0nozzle SW]
inherits = *0.15mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.15mm 1.2nozzle SW]
inherits = *0.15mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.2mm 0.3nozzle V2]
inherits = *0.2mm*; *0.3nozzle*
@ -1263,6 +1451,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.2mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.2mm 0.25nozzle SW]
inherits = *0.2mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.2mm 0.3nozzle SW]
inherits = *0.2mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.2mm 0.4nozzle SW]
inherits = *0.2mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.2mm 0.5nozzle SW]
inherits = *0.2mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.2mm 0.6nozzle SW]
inherits = *0.2mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.2mm 0.8nozzle SW]
inherits = *0.2mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.2mm 1.0nozzle SW]
inherits = *0.2mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.2mm 1.2nozzle SW]
inherits = *0.2mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.3mm 0.4nozzle V2]
inherits = *0.3mm*; *0.4nozzle*
@ -1336,6 +1555,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.3mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.3mm 0.25nozzle SW]
inherits = *0.3mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.3mm 0.3nozzle SW]
inherits = *0.3mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.3mm 0.4nozzle SW]
inherits = *0.3mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.3mm 0.5nozzle SW]
inherits = *0.3mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.3mm 0.6nozzle SW]
inherits = *0.3mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.3mm 0.8nozzle SW]
inherits = *0.3mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.3mm 1.0nozzle SW]
inherits = *0.3mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.3mm 1.2nozzle SW]
inherits = *0.3mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.4mm 0.6nozzle V2]
inherits = *0.4mm*; *0.6nozzle*
@ -1393,6 +1643,38 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.4mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.4mm 0.25nozzle SW]
inherits = *0.4mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.4mm 0.3nozzle SW]
inherits = *0.4mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.4mm 0.4nozzle SW]
inherits = *0.4mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.4mm 0.5nozzle SW]
inherits = *0.4mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.4mm 0.6nozzle SW]
inherits = *0.4mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.4mm 0.8nozzle SW]
inherits = *0.4mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.4mm 1.0nozzle SW]
inherits = *0.4mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.4mm 1.2nozzle SW]
inherits = *0.4mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.6mm 0.8nozzle V2]
inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.8
@ -1429,6 +1711,18 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.6mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.6mm 0.8nozzle SW]
inherits = *0.6mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.6mm 1.0nozzle SW]
inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.6mm 1.2nozzle SW]
inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.8mm 1.2nozzle V2]
inherits = *0.8mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==1.2
@ -1441,6 +1735,10 @@ compatible_printers_condition = printer_model=~/.*Voron_v1.*/ and nozzle_diamete
inherits = *0.8mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.8mm 1.2nozzle SW]
inherits = *0.8mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[filament:*common*]
cooling = 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

7
src/ankerl/README.txt Normal file
View File

@ -0,0 +1,7 @@
THIS DIRECTORY CONTAINS PIECES OF THE
ankerl::unordered_dense::{map, set}
https://github.com/martinus/unordered_dense
unordered_dense 3.1.1 10782bfc651c2bb75b11bf90491f50da122e5432
SOURCE DISTRIBUTION.
THIS IS NOT THE COMPLETE unordered_dense DISTRIBUTION. ONLY FILES NEEDED FOR COMPILING PRUSASLICER WERE PUT INTO THE PRUSASLICER SOURCE DISTRIBUTION.

1584
src/ankerl/unordered_dense.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -206,7 +206,7 @@ std::vector<WaveSeed> wave_seeds(
{
assert(tiny_expansion > 0);
if (src.empty())
if (src.empty() || boundary.empty())
return {};
using Intersection = ClipperZUtils::ClipperZIntersectionVisitor::Intersection;

View File

@ -1,6 +1,7 @@
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "ShortestPath.hpp"
#include "Utils.hpp"
// #define CLIPPER_UTILS_DEBUG
@ -1167,34 +1168,45 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
return out;
}
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
assert(ClipperLib::Area(expoly.contour.points) > 0.);
for (auto &h : expoly.holes)
assert(ClipperLib::Area(h.points) < 0.);
#endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
// 1) Offset the outer contour.
contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
#ifndef NDEBUG
// Shrinking a contour may split it into pieces, but never create a new hole inside the contour.
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
// 2) Offset the holes one by one, collect the results.
holes.reserve(expoly.holes.size());
for (const Polygon &hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
// Offsetting a hole curve of a C shape may close the C into a ring with a new hole inside, thus creating a hole inside a hole shape, thus a hole will be created with negative area
// and the following test will fail.
// for (auto &c : holes)
// assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
}
// 3) Subtract holes from the contours.
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
ClipperLib::Paths contours, holes;
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
// Subtract holes from the contours.
ClipperLib::Paths output;
if (holes.empty())
output = std::move(contours);
@ -1202,6 +1214,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
ClipperLib::Clipper clipper;
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
// Holes may contain holes in holes produced by expanding a C hole shape.
// The situation is processed correctly by Clipper diff operation.
clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
@ -1209,129 +1223,120 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
return to_polygons(std::move(output));
}
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
ClipperLib::Paths contours, holes;
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
// Subtract holes from the contours.
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
// Shrinking a CCW contour may only produce more CCW contours, but never new holes.
for (ClipperLib::Path &path : contours)
output.emplace_back(std::move(path));
} else {
ClipperLib::Clipper clipper;
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
// Holes may contain holes in holes produced by expanding a C hole shape.
// The situation is processed correctly by Clipper diff operation, producing concentric expolygons.
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
output = PolyTreeToExPolygons(std::move(polytree));
}
return output;
}
static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
assert(ClipperLib::Area(expoly.contour.points) > 0.);
for (auto &h : expoly.holes)
assert(ClipperLib::Area(h.points) < 0.);
#endif /* NDEBUG */
// 1) Offset the outer contour.
contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
// Inflating a contour must not remove it.
assert(contours.size() >= 1);
#ifndef NDEBUG
// Offsetting a positive curve of a C shape may close the C into a ring with hole shape, thus a hole will be created with negative area
// and the following test will fail.
// for (auto &c : contours)
// assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
#ifndef NDEBUG
// Shrinking a hole may split it into pieces, but never create a new hole inside a hole.
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
}
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
ClipperLib::Paths contours, holes;
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
// 1) Offset the outer contour.
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// Subtract holes from the contours.
ClipperLib::Paths output;
if (holes.empty())
output = std::move(contours);
else {
//FIXME the difference is not needed as the holes may never intersect with other holes.
ClipperLib::Clipper clipper;
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
// 2) Offset the holes one by one, collect the results.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
ClipperLib::Paths output;
if (holes.empty())
output = std::move(contours);
else {
ClipperLib::Clipper clipper;
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
return to_polygons(std::move(output));
return to_polygons(std::move(output));
}
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
ClipperLib::Paths contours, holes;
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
// 1) Offset the outer contour.
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
// Subtract holes from the contours.
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
for (ClipperLib::Path &path : contours)
output.emplace_back(std::move(path));
} else {
ClipperLib::Clipper clipper;
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
output = PolyTreeToExPolygons(std::move(polytree));
}
return output;
}
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
for (ClipperLib::Path &path : contours)
output.emplace_back(std::move(path));
output.reserve(1);
if (contours.size() > 1) {
// One expolygon with holes created by closing a C shape. Which is which?
output.push_back({});
ExPolygon &out = output.back();
out.holes.reserve(contours.size() - 1);
for (ClipperLib::Path &path : contours) {
if (ClipperLib::Area(path) > 0) {
// Only one contour with positive area is expected to be created by an outer offset of an ExPolygon.
assert(out.contour.empty());
out.contour.points = std::move(path);
} else
out.holes.push_back(Polygon{ std::move(path) });
}
} else {
// Single contour must be CCW.
assert(contours.size() == 1);
assert(ClipperLib::Area(contours.front()) > 0);
output.push_back(ExPolygon{ std::move(contours.front()) });
}
} else {
//FIXME the difference is not needed as the holes may never intersect with other holes.
ClipperLib::Clipper clipper;
// Contours may have holes if they were created by closing a C shape.
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
@ -1339,6 +1344,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
output = PolyTreeToExPolygons(std::move(polytree));
}
assert(output.size() == 1);
return output;
}

View File

@ -597,7 +597,8 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
}
ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.);
if (out_vec.size() == 1)
if (out_vec.size() == 1 && out_vec.front().holes.size() == resampled.holes.size())
// No contour of the original compensated expolygon was lost.
out = std::move(out_vec.front());
else {
// Something went wrong, don't compensate.
@ -610,6 +611,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
{ { out_vec }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } });
}
#endif /* TESTS_EXPORT_SVGS */
// It may be that the source expolygons contained non-manifold vertices, for which the variable offset may not produce the same number of contours or holes.
assert(out_vec.size() == 1);
}
}

View File

@ -10,6 +10,8 @@
#include <cassert>
#include <list>
#include <ankerl/unordered_dense.h>
namespace Slic3r {
void ExPolygon::scale(double factor)
@ -416,20 +418,36 @@ bool has_duplicate_points(const ExPolygons &expolys)
{
#if 1
// Check globally.
size_t cnt = 0;
for (const ExPolygon &expoly : expolys) {
cnt += expoly.contour.points.size();
for (const Polygon &hole : expoly.holes)
cnt += hole.points.size();
}
#if 0
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
std::vector<Point> allpts;
allpts.reserve(cnt);
allpts.reserve(count_points(expolys));
for (const ExPolygon &expoly : expolys) {
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
for (const Polygon &hole : expoly.holes)
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
}
return has_duplicate_points(std::move(allpts));
#else
// Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort.
struct PointHash {
uint64_t operator()(const Point &p) const noexcept {
uint64_t h;
static_assert(sizeof(h) == sizeof(p));
memcpy(&h, &p, sizeof(p));
return ankerl::unordered_dense::detail::wyhash::hash(h);
}
};
ankerl::unordered_dense::set<Point, PointHash> allpts;
allpts.reserve(count_points(expolys));
for (const ExPolygon &expoly : expolys)
for (size_t icontour = 0; icontour < expoly.num_contours(); ++ icontour)
for (const Point &pt : expoly.contour_or_hole(icontour).points)
if (! allpts.insert(pt).second)
// Duplicate point was discovered.
return true;
return false;
#endif
#else
// Check per contour.
for (const ExPolygon &expoly : expolys)

View File

@ -2354,11 +2354,10 @@ void GCode::process_layer_single_object(
// Round 1 (wiping into object or infill) or round 2 (normal extrusions).
const bool print_wipe_extrusions)
{
//FIXME what the heck ID is this? Layer ID or Object ID? More likely an Object ID.
uint32_t layer_id = 0;
bool first = true;
bool first = true;
int object_id = 0;
// Delay layer initialization as many layers may not print with all extruders.
auto init_layer_delayed = [this, &print_instance, &layer_to_print, layer_id, &first, &gcode]() {
auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &object_id, &gcode]() {
if (first) {
first = false;
const PrintObject &print_object = print_instance.print_object;
@ -2374,8 +2373,14 @@ void GCode::process_layer_single_object(
m_avoid_crossing_perimeters.use_external_mp_once();
m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset));
if (this->config().gcode_label_objects)
gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
if (this->config().gcode_label_objects) {
for (const PrintObject *po : print_object.print()->objects())
if (po == &print_object)
break;
else
++ object_id;
gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
}
}
};
@ -2548,7 +2553,7 @@ void GCode::process_layer_single_object(
}
}
if (! first && this->config().gcode_label_objects)
gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
}
void GCode::apply_print_config(const PrintConfig &print_config)
@ -3020,9 +3025,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
{100, ConfigOptionInts{0}}};
}
double external_perim_reference_speed = std::min(m_config.get_abs_value("external_perimeter_speed"),
std::min(EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm,
m_config.max_volumetric_speed.value / path.mm3_per_mm));
double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed");
if (external_perim_reference_speed == 0)
external_perim_reference_speed = m_volumetric_speed / path.mm3_per_mm;
if (m_config.max_volumetric_speed.value > 0)
external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path.mm3_per_mm);
if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) {
external_perim_reference_speed = std::min(external_perim_reference_speed,
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm);
}
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
m_writer.extruder()->id(), external_perim_reference_speed,
speed);

View File

@ -272,6 +272,7 @@ public:
float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
overhangs_w_speeds[i].second.value;
if (speed < EPSILON) speed = speed_base;
speed_sections[distance] = speed;
}

View File

@ -3566,7 +3566,7 @@ void GCodeProcessor::post_process()
++m_curr_g1_id;
}
if (it != init_it || m_curr_g1_id == 0)
if ((it != m_machine.g1_times_cache.end() && it != init_it) || m_curr_g1_id == 0)
m_time = it->elapsed_time;
}

View File

@ -106,6 +106,7 @@ Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
#include <libslic3r/BoundingBox.hpp>
namespace priv {
//FIXME O(n^2) complexity!
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
{
using namespace Slic3r;

View File

@ -6,6 +6,7 @@
namespace Slic3r {
// collect all intersecting points
//FIXME O(n^2) complexity!
Pointfs intersection_points(const Lines &lines);
Pointfs intersection_points(const Polygon &polygon);
Pointfs intersection_points(const Polygons &polygons);

View File

@ -60,7 +60,10 @@ void Layer::make_slices()
}
// used by Layer::build_up_down_graph()
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths(const ExPolygons &expolygons, coord_t isrc)
// Shrink source polygons one by one, so that they will be separated if they were touching
// at vertices (non-manifold situation).
// Then convert them to Z-paths with Z coordinate indicating index of the source expolygon.
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths_shrunk(const ExPolygons &expolygons, coord_t isrc)
{
size_t num_paths = 0;
for (const ExPolygon &expolygon : expolygons)
@ -69,14 +72,49 @@ void Layer::make_slices()
ClipperLib_Z::Paths out;
out.reserve(num_paths);
for (const ExPolygon &expolygon : expolygons) {
for (size_t icontour = 0; icontour < expolygon.num_contours(); ++ icontour) {
const Polygon &contour = expolygon.contour_or_hole(icontour);
out.emplace_back();
ClipperLib_Z::Path &path = out.back();
path.reserve(contour.size());
for (const Point &p : contour.points)
path.push_back({ p.x(), p.y(), isrc });
ClipperLib::Paths contours;
ClipperLib::Paths holes;
ClipperLib::Clipper clipper;
ClipperLib::ClipperOffset co;
ClipperLib::Paths out2;
static constexpr const float delta = ClipperSafetyOffset; // *10.f;
co.MiterLimit = scaled<double>(3.);
// Use the default zero edge merging distance. For this kind of safety offset the accuracy of normal direction is not important.
// co.ShortestEdgeLength = delta * ClipperOffsetShortestEdgeFactor;
for (const ExPolygon &expoly : expolygons) {
contours.clear();
co.Clear();
co.AddPath(expoly.contour.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
co.Execute(contours, - delta);
if (! contours.empty()) {
holes.clear();
for (const Polygon &hole : expoly.holes) {
co.Clear();
co.AddPath(hole.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
out2.clear();
co.Execute(out2, delta);
append(holes, std::move(out2));
}
// Subtract holes from the contours.
if (! holes.empty()) {
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
contours.clear();
clipper.Execute(ClipperLib::ctDifference, contours, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
for (const auto &contour : contours) {
out.emplace_back();
ClipperLib_Z::Path &path = out.back();
path.reserve(contour.size());
for (const Point &p : contour)
path.push_back({ p.x(), p.y(), isrc });
}
}
++ isrc;
}
@ -183,6 +221,10 @@ static void connect_layer_slices(
break;
}
}
//FIXME remove the following block one day, it should not be needed.
// The following shall not happen now as the source expolygons are being shrunk a bit before intersecting,
// thus each point of each intersection polygon should fit completely inside one of the original (unshrunk) expolygons.
assert(found);
if (!found) {
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
@ -191,10 +233,7 @@ static void connect_layer_slices(
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
Polygon contour_poly;
for (const auto& p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
Polygon contour_poly(ClipperZUtils::from_zpath(polynode.Contour));
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_above.lslices_ex[l];
@ -222,11 +261,11 @@ static void connect_layer_slices(
break;
}
}
//FIXME remove the following block one day, it should not be needed.
// The following shall not happen now as the source expolygons are being shrunk a bit before intersecting,
// thus each point of each intersection polygon should fit completely inside one of the original (unshrunk) expolygons.
if (!found) { // Explanation for aditional check is above.
Polygon contour_poly;
for (const auto &p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
Polygon contour_poly(ClipperZUtils::from_zpath(polynode.Contour));
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_below.lslices_ex[l];
@ -249,6 +288,8 @@ static void connect_layer_slices(
found = true;
}
if (found) {
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
// Subtract area of holes from the area of outer contour.
double area = ClipperLib_Z::Area(polynode.Contour);
for (int icontour = 0; icontour < polynode.ChildCount(); ++ icontour)
@ -340,9 +381,9 @@ static void connect_layer_slices(
void Layer::build_up_down_graph(Layer& below, Layer& above)
{
coord_t paths_below_offset = 0;
ClipperLib_Z::Paths paths_below = expolygons_to_zpaths(below.lslices, paths_below_offset);
ClipperLib_Z::Paths paths_below = expolygons_to_zpaths_shrunk(below.lslices, paths_below_offset);
coord_t paths_above_offset = paths_below_offset + coord_t(below.lslices.size());
ClipperLib_Z::Paths paths_above = expolygons_to_zpaths(above.lslices, paths_above_offset);
ClipperLib_Z::Paths paths_above = expolygons_to_zpaths_shrunk(above.lslices, paths_above_offset);
#ifndef NDEBUG
coord_t paths_end = paths_above_offset + coord_t(above.lslices.size());
#endif // NDEBUG

View File

@ -837,13 +837,10 @@ ModelInstance* ModelObject::add_instance(const ModelInstance &other)
return i;
}
ModelInstance* ModelObject::add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror)
ModelInstance* ModelObject::add_instance(const Geometry::Transformation& trafo)
{
auto *instance = add_instance();
instance->set_offset(offset);
instance->set_scaling_factor(scaling_factor);
instance->set_rotation(rotation);
instance->set_mirror(mirror);
ModelInstance* instance = add_instance();
instance->set_transformation(trafo);
return instance;
}

View File

@ -393,7 +393,7 @@ public:
ModelInstance* add_instance();
ModelInstance* add_instance(const ModelInstance &instance);
ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror);
ModelInstance* add_instance(const Geometry::Transformation& trafo);
void delete_instance(size_t idx);
void delete_last_instance();
void clear_instances();

View File

@ -4,6 +4,8 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
#include <ankerl/unordered_dense.h>
namespace Slic3r {
double Polygon::length() const
@ -400,14 +402,32 @@ bool has_duplicate_points(const Polygons &polys)
{
#if 1
// Check globally.
size_t cnt = 0;
for (const Polygon &poly : polys)
cnt += poly.points.size();
#if 0
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
std::vector<Point> allpts;
allpts.reserve(cnt);
allpts.reserve(count_points(polys));
for (const Polygon &poly : polys)
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
return has_duplicate_points(std::move(allpts));
#else
// Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort.
struct PointHash {
uint64_t operator()(const Point &p) const noexcept {
uint64_t h;
static_assert(sizeof(h) == sizeof(p));
memcpy(&h, &p, sizeof(p));
return ankerl::unordered_dense::detail::wyhash::hash(h);
}
};
ankerl::unordered_dense::set<Point, PointHash> allpts;
allpts.reserve(count_points(polys));
for (const Polygon &poly : polys)
for (const Point &pt : poly.points)
if (! allpts.insert(pt).second)
// Duplicate point was discovered.
return true;
return false;
#endif
#else
// Check per contour.
for (const Polygon &poly : polys)

View File

@ -518,8 +518,12 @@ std::string Print::validate(std::string* warning) const
//FIXME It is quite expensive to generate object layers just to get the print height!
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx));
! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) {
return _u8L("The print is taller than the maximum allowed height. You might want to reduce the size of your model"
" or change current print settings and retry.");
return
// Test whether the last slicing plane is below or above the print volume.
0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON ?
format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.");
}
}

View File

@ -2908,7 +2908,8 @@ void PrintConfigDef::init_fff_params()
// TRN PrintSettings: "Organic supports" > "Tip Diameter"
def->tooltip = L("Branch tip diameter for organic supports.");
def->sidetext = L("mm");
def->min = 0;
def->min = 0.1f;
def->max = 100.f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.8));
@ -2919,7 +2920,8 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The diameter of the thinnest branches of organic support. Thicker branches are more sturdy. "
"Branches towards the base will be thicker than this.");
def->sidetext = L("mm");
def->min = 0;
def->min = 0.1f;
def->max = 100.f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2));

View File

@ -44,7 +44,7 @@ enum class MachineLimitsUsage {
};
enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htMainSail
htPrusaLink, htPrusaConnect, htOctoPrint, htMainSail, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
};
enum AuthorizationType {

View File

@ -102,7 +102,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
m_size.z() = model_object->max_z();
m_size.z() = coord_t(model_object->max_z() * (1. / SCALING_FACTOR));
this->set_instances(std::move(instances));
}
@ -1603,21 +1603,18 @@ void PrintObject::bridge_over_infill()
int layer_index,
Polygons new_polys,
const LayerRegion *region,
double bridge_angle,
bool supported_by_lightning)
double bridge_angle)
: original_surface(original_surface)
, layer_index(layer_index)
, new_polys(new_polys)
, region(region)
, bridge_angle(bridge_angle)
, supported_by_lightning(supported_by_lightning)
{}
const Surface *original_surface;
int layer_index;
Polygons new_polys;
const LayerRegion *region;
double bridge_angle;
bool supported_by_lightning;
};
std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
@ -1677,7 +1674,7 @@ void PrintObject::bridge_over_infill()
}
}
worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon);
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning));
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
#ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
@ -2135,6 +2132,7 @@ void PrintObject::bridge_over_infill()
deep_infill_area = expand(deep_infill_area, spacing * 1.5);
// Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
Polygons lightning_area;
Polygons expansion_area;
Polygons total_fill_area;
for (const LayerRegion *region : layer->regions()) {
@ -2142,6 +2140,10 @@ void PrintObject::bridge_over_infill()
expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end());
Polygons fill_polys = to_polygons(region->fill_expolygons());
total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end());
if (region->region().config().fill_pattern == ipLightning) {
Polygons l = to_polygons(region->fill_surfaces().filter_by_type(stInternal));
lightning_area.insert(lightning_area.end(), l.begin(), l.end());
}
}
total_fill_area = closing(total_fill_area, SCALED_EPSILON);
expansion_area = closing(expansion_area, SCALED_EPSILON);
@ -2196,7 +2198,7 @@ void PrintObject::bridge_over_infill()
}
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
if (candidate.supported_by_lightning) {
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10)));
}
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
@ -2229,7 +2231,7 @@ void PrintObject::bridge_over_infill()
#endif
expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area,
candidate.region, bridging_angle, candidate.supported_by_lightning));
candidate.region, bridging_angle));
}
surfaces_by_layer[lidx].swap(expanded_surfaces);
expanded_surfaces.clear();

View File

@ -257,6 +257,8 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
auto it = radius_until_layer.find(r);
if (it == radius_until_layer.end())
radius_until_layer.emplace_hint(it, r, current_layer);
else
assert(it->second >= current_layer);
};
// regular radius
update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0) + m_current_min_xy_dist_delta));
@ -359,7 +361,7 @@ const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerI
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
tree_supports_show_error("Not precalculated Collision requested."sv, false);
}
const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx, {});
const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx, []{});
return getCollision(orig_radius, layer_idx, min_xy_dist);
}

View File

@ -512,7 +512,7 @@ private:
*/
void calculateCollisionHolefree(RadiusLayerPair key)
{
calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, {});
calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
}
/*!
@ -533,7 +533,7 @@ private:
*/
void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model)
{
calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model, {});
calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model, []{});
}
/*!
@ -567,7 +567,7 @@ private:
*/
void calculateWallRestrictions(RadiusLayerPair key)
{
calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, {});
calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
}
/*!

View File

@ -70,16 +70,17 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius),
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller.
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)),
@ -96,7 +97,7 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius);
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
@ -1857,7 +1858,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
}
radius = config.getCollisionRadius(current_elem);
const coord_t foot_radius_increase = config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0));
const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0);
// Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
// which could cause the radius to become bigger than precalculated.
double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase);
@ -2015,9 +2016,9 @@ static void increase_areas_one_layer(
config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) {
// can guarantee elephant foot radius increase
if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist))
extra_speed += config.branch_radius * config.diameter_scale_bp_radius;
extra_speed += config.bp_radius_increase_per_layer;
else
extra_slow_speed += std::min(coord_t(config.branch_radius * config.diameter_scale_bp_radius),
extra_slow_speed += std::min(coord_t(config.bp_radius_increase_per_layer),
config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed));
}
@ -2236,11 +2237,11 @@ static void increase_areas_one_layer(
out.to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious
out.elephant_foot_increases = 0;
if (config.diameter_scale_bp_radius > 0) {
if (config.bp_radius_increase_per_layer > 0) {
coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(out));
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
out.elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor));
out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer);
}
// set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior.

View File

@ -302,9 +302,9 @@ public:
*/
size_t tip_layers;
/*!
* \brief Factor by which to increase the branch radius.
* \brief How much a branch radius increases with each layer to guarantee the prescribed tree widening.
*/
double diameter_angle_scale_factor;
double branch_radius_increase_per_layer;
/*!
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
*/
@ -330,17 +330,18 @@ public:
*/
coord_t xy_distance;
/*!
* \brief Radius a branch should have when reaching the buildplate.
* \brief A minimum radius a tree trunk should expand to at the buildplate if possible.
*/
coord_t bp_radius;
/*!
* \brief The layer index at which an increase in radius may be required to reach the bp_radius.
*/
coord_t layer_start_bp_radius;
LayerIndex layer_start_bp_radius;
/*!
* \brief Factor by which to increase the branch radius to reach the required bp_radius at layer 0. Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
* \brief How much one is allowed to increase the tree branch radius close to print bed to reach the required bp_radius at layer 0.
* Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
*/
double diameter_scale_bp_radius;
double bp_radius_increase_per_layer;
/*!
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
*/
@ -418,7 +419,9 @@ public:
public:
bool operator==(const TreeSupportSettings& other) const
{
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && diameter_angle_scale_factor == other.diameter_angle_scale_factor && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius && diameter_scale_bp_radius == other.diameter_scale_bp_radius && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance && // as a recalculation of the collision areas is required to set a new min_radius.
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && branch_radius_increase_per_layer == other.branch_radius_increase_per_layer && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius &&
// as a recalculation of the collision areas is required to set a new min_radius.
bp_radius_increase_per_layer == other.bp_radius_increase_per_layer && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance &&
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
@ -470,9 +473,9 @@ public:
{
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
branch_radius + // base
branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor)
(distance_to_top - tip_layers) * branch_radius_increase_per_layer)
+ // gradual increase
branch_radius * elephant_foot_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0));
elephant_foot_increases * (std::max(bp_radius_increase_per_layer - branch_radius_increase_per_layer, 0.0));
}
/*!
@ -502,8 +505,8 @@ public:
*/
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
{
double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius;
return scale > 0 ? branch_radius + branch_radius * scale : 0;
double num_layers_widened = layer_start_bp_radius - layer_idx;
return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
}
/*!

View File

@ -1911,6 +1911,16 @@ std::vector<ExPolygons> slice_mesh_ex(
this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? ClipperLib::pftEvenOdd :
this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? ClipperLib::pftPositive : ClipperLib::pftNonZero,
&expolygons);
#if 0
//#ifndef _NDEBUG
for (const ExPolygon &ex : expolygons) {
assert(! has_duplicate_points(ex.contour));
for (const Polygon &hole : ex.holes)
assert(! has_duplicate_points(hole));
assert(! has_duplicate_points(ex));
}
assert(!has_duplicate_points(expolygons));
#endif // _NDEBUG
//FIXME simplify
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
keep_largest_contour_only(expolygons);
@ -1921,6 +1931,16 @@ std::vector<ExPolygons> slice_mesh_ex(
append(simplified, ex.simplify(resolution));
expolygons = std::move(simplified);
}
#if 0
//#ifndef _NDEBUG
for (const ExPolygon &ex : expolygons) {
assert(! has_duplicate_points(ex.contour));
for (const Polygon &hole : ex.holes)
assert(! has_duplicate_points(hole));
assert(! has_duplicate_points(ex));
}
assert(! has_duplicate_points(expolygons));
#endif // _NDEBUG
}
});
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end";

View File

@ -252,6 +252,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Http.hpp
Utils/FixModelByWin10.cpp
Utils/FixModelByWin10.hpp
Utils/Mainsail.cpp
Utils/Mainsail.hpp
Utils/OctoPrint.cpp
Utils/OctoPrint.hpp
Utils/Duet.cpp

View File

@ -362,6 +362,16 @@ bool GLGizmoEmboss::init_create(ModelVolumeType volume_type)
return true;
}
namespace {
TransformationType get_transformation_type(const Selection &selection)
{
assert(selection.is_single_full_object() || selection.is_single_volume());
return selection.is_single_volume() ?
TransformationType::Local_Relative_Joint :
TransformationType::Instance_Relative_Joint; // object
}
} // namespace
bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
{
if (mouse_event.Moving()) return false;
@ -381,9 +391,8 @@ bool GLGizmoEmboss::on_mouse_for_rotation(const wxMouseEvent &mouse_event)
angle -= PI / 2; // Grabber is upward
// temporary rotation
const TransformationType transformation_type = m_parent.get_selection().is_single_text() ?
TransformationType::Local_Relative_Joint : TransformationType::World_Relative_Joint;
m_parent.get_selection().rotate(Vec3d(0., 0., angle), transformation_type);
Selection& selection = m_parent.get_selection();
selection.rotate(Vec3d(0., 0., angle), get_transformation_type(selection));
angle += *m_rotate_start_angle;
// move to range <-M_PI, M_PI>
@ -2466,6 +2475,38 @@ bool GLGizmoEmboss::rev_slider(const std::string &name,
undo_tooltip, undo_offset, draw_slider_float);
}
void GLGizmoEmboss::do_translate(const Vec3d &relative_move)
{
assert(m_volume != nullptr);
assert(m_volume->text_configuration.has_value());
Selection &selection = m_parent.get_selection();
assert(!selection.is_empty());
selection.setup_cache();
selection.translate(relative_move, TransformationType::Local);
std::string snapshot_name; // empty mean no store undo / redo
// NOTE: it use L instead of _L macro because prefix _ is appended inside
// function do_move
// snapshot_name = L("Set surface distance");
m_parent.do_move(snapshot_name);
}
void GLGizmoEmboss::do_rotate(float relative_z_angle)
{
assert(m_volume != nullptr);
assert(m_volume->text_configuration.has_value());
Selection &selection = m_parent.get_selection();
assert(!selection.is_empty());
selection.setup_cache();
selection.rotate(Vec3d(0., 0., relative_z_angle), get_transformation_type(selection));
std::string snapshot_name; // empty meand no store undo / redo
// NOTE: it use L instead of _L macro because prefix _ is appended
// inside function do_move
// snapshot_name = L("Set text rotation");
m_parent.do_rotate(snapshot_name);
}
void GLGizmoEmboss::draw_advanced()
{
const auto &ff = m_style_manager.get_font_file_with_cache();

View File

@ -135,7 +135,11 @@ void GLGizmoSlaBase::render_volumes()
const Camera& camera = wxGetApp().plater()->get_camera();
ClippingPlane clipping_plane = (m_c->object_clipper()->get_position() == 0.0) ? ClippingPlane::ClipsNothing() : *m_c->object_clipper()->get_clipping_plane();
clipping_plane.set_normal(-clipping_plane.get_normal());
if (m_c->object_clipper()->get_position() != 0.0)
clipping_plane.set_normal(-clipping_plane.get_normal());
else
// on Linux the clipping plane does not work when using DBL_MAX
clipping_plane.set_offset(FLT_MAX);
m_volumes.set_clipping_plane(clipping_plane.get_data());
for (GLVolume* v : m_volumes.volumes) {

View File

@ -1827,6 +1827,7 @@ struct Plater::priv
const Selection& get_selection() const;
Selection& get_selection();
int get_selected_object_idx() const;
int get_selected_instance_idx() const;
int get_selected_volume_idx() const;
void selection_changed();
void object_list_changed();
@ -2967,6 +2968,17 @@ int Plater::priv::get_selected_object_idx() const
return (0 <= idx && idx < int(model.objects.size())) ? idx : -1;
}
int Plater::priv::get_selected_instance_idx() const
{
const int obj_idx = get_selected_object_idx();
if (obj_idx >= 0) {
const int inst_idx = get_selection().get_instance_idx();
return (0 <= inst_idx && inst_idx < int(model.objects[obj_idx]->instances.size())) ? inst_idx : -1;
}
else
return -1;
}
int Plater::priv::get_selected_volume_idx() const
{
auto& selection = get_selection();
@ -6062,7 +6074,8 @@ void Plater::increase_instances(size_t num, int obj_idx/* = -1*/)
}
ModelObject* model_object = p->model.objects[obj_idx];
ModelInstance* model_instance = model_object->instances.back();
const int inst_idx = p->get_selected_instance_idx();
ModelInstance* model_instance = (inst_idx >= 0) ? model_object->instances[inst_idx] : model_object->instances.back();
bool was_one_instance = model_object->instances.size()==1;
@ -6070,7 +6083,9 @@ void Plater::increase_instances(size_t num, int obj_idx/* = -1*/)
double offset = offset_base;
for (size_t i = 0; i < num; i++, offset += offset_base) {
Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0);
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation(), model_instance->get_mirror());
Geometry::Transformation trafo = model_instance->get_transformation();
trafo.set_offset(offset_vec);
model_object->add_instance(trafo);
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
}

View File

@ -18,6 +18,10 @@
#include <devpropdef.h>
#include <devpkey.h>
#include <usbioctl.h>
#include <atlbase.h>
#include <atlcom.h>
#include <shldisp.h>
#else
// unix, linux & OSX includes
#include <errno.h>
@ -80,7 +84,7 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
namespace {
#if 0
// From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview
typedef struct _STRING_DESCRIPTOR_NODE
{
@ -581,6 +585,57 @@ void eject_alt(std::string path, wxEvtHandler* callback_evt_handler, DriveData d
if (callback_evt_handler)
wxPostEvent(callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(drive_data), true)));
}
#endif // 0
// C++ equivavalent of PowerShell script:
// $driveEject = New - Object - comObject Shell.Application
// $driveEject.Namespace(17).ParseName("E:").InvokeVerb("Eject")
// from https://superuser.com/a/1750403
bool eject_inner(const std::string& path)
{
std::wstring wpath = boost::nowide::widen(path);
CoInitialize(nullptr);
CComPtr<IShellDispatch> pShellDisp;
HRESULT hr = pShellDisp.CoCreateInstance(CLSID_Shell, nullptr, CLSCTX_INPROC_SERVER);
if (!SUCCEEDED(hr)) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to get Shell pointer has failed.", path);
CoUninitialize();
return false;
}
CComPtr<Folder> pFolder;
VARIANT vtDrives;
VariantInit(&vtDrives);
vtDrives.vt = VT_I4;
vtDrives.lVal = ssfDRIVES;
hr = pShellDisp->NameSpace(vtDrives, &pFolder);
if (!SUCCEEDED(hr)) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to create Namespace has failed.", path);
CoUninitialize();
return false;
}
CComPtr<FolderItem> pItem;
hr = pFolder->ParseName(static_cast<BSTR>(const_cast<wchar_t*>(wpath.c_str())), &pItem);
if (!SUCCEEDED(hr)) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to Parse name has failed.", path);
CoUninitialize();
return false;
}
VARIANT vtEject;
VariantInit(&vtEject);
vtEject.vt = VT_BSTR;
vtEject.bstrVal = SysAllocString(L"Eject");
hr = pItem->InvokeVerb(vtEject);
if (!SUCCEEDED(hr)) {
BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Attempt to Invoke Verb has failed.", path);
VariantClear(&vtEject);
CoUninitialize();
return false;
}
BOOST_LOG_TRIVIAL(debug) << "Ejecting via InvokeVerb has succeeded.";
VariantClear(&vtEject);
CoUninitialize();
return true;
}
} // namespace
// Called from UI therefore it blocks the UI thread.
@ -597,27 +652,19 @@ void RemovableDriveManager::eject_drive()
BOOST_LOG_TRIVIAL(info) << "Ejecting started";
std::scoped_lock<std::mutex> lock(m_drives_mutex);
auto it_drive_data = this->find_last_save_path_drive_data();
#if 1
if (it_drive_data != m_current_drives.end()) {
if (!eject_inner(m_last_save_path)) {
if (eject_inner(m_last_save_path)) {
// success
BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded.";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
} else {
if (m_eject_thread.joinable())
m_eject_thread.join();
m_eject_thread = boost::thread(eject_alt, m_last_save_path, m_callback_evt_handler, std::move(*it_drive_data));
// failed to eject
// this should not happen, throwing exception might be the way here
/*
BOOST_LOG_TRIVIAL(error) << "Ejecting has failed.";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
*/
}
} else {
// drive not found in m_current_drives
@ -626,47 +673,6 @@ void RemovableDriveManager::eject_drive()
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>({"",""}, false)));
}
#endif
#if 0
// Implementation used until 2.5.x version
// Some usb drives does not eject properly (still visible in file explorer). Some even does not write all content and eject.
if (it_drive_data != m_current_drives.end()) {
// get handle to device
std::string mpath = "\\\\.\\" + m_last_save_path;
mpath = mpath.substr(0, mpath.size() - 1);
HANDLE handle = CreateFileW(boost::nowide::widen(mpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError();
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
return;
}
DWORD deviceControlRetVal(0);
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
BOOL e1 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(error) << "FSCTL_LOCK_VOLUME " << e1 << " ; " << deviceControlRetVal << " ; " << GetLastError();
BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
BOOST_LOG_TRIVIAL(error) << "FSCTL_DISMOUNT_VOLUME " << e2 << " ; " << deviceControlRetVal << " ; " << GetLastError();
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here with FALSE as third parameter, which should set PreventMediaRemoval
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0) {
CloseHandle(handle);
BOOST_LOG_TRIVIAL(error) << "Ejecting " << mpath << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError();
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair<DriveData, bool>(*it_drive_data, false)));
return;
}
CloseHandle(handle);
BOOST_LOG_TRIVIAL(info) << "Ejecting finished";
assert(m_callback_evt_handler);
if (m_callback_evt_handler)
wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true)));
m_current_drives.erase(it_drive_data);
}
#endif // 0
}
std::string RemovableDriveManager::get_removable_drive_path(const std::string &path)

View File

@ -106,12 +106,6 @@ private:
#endif /* _WIN32 */
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
#ifdef _WIN32
// Another worker thread, used only to perform alt_eject method (external SD cards only).
// Does not share data with m_thread
boost::thread m_eject_thread;
#endif /* _WIN32 */
// Called from update() to enumerate removable drives.
std::vector<DriveData> search_for_removable_drives() const;

View File

@ -0,0 +1,257 @@
#include "Mainsail.hpp"
#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/nowide/convert.hpp>
#include <curl/curl.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/format.hpp"
#include "libslic3r/AppConfig.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
namespace {
#ifdef WIN32
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
std::string substitute_host(const std::string& orig_addr, std::string sub_addr)
{
// put ipv6 into [] brackets
if (sub_addr.find(':') != std::string::npos && sub_addr.at(0) != '[')
sub_addr = "[" + sub_addr + "]";
// Using the new CURL API for handling URL. https://everything.curl.dev/libcurl/url
// If anything fails, return the input unchanged.
std::string out = orig_addr;
CURLU* hurl = curl_url();
if (hurl) {
// Parse the input URL.
CURLUcode rc = curl_url_set(hurl, CURLUPART_URL, orig_addr.c_str(), 0);
if (rc == CURLUE_OK) {
// Replace the address.
rc = curl_url_set(hurl, CURLUPART_HOST, sub_addr.c_str(), 0);
if (rc == CURLUE_OK) {
// Extract a string fromt the CURL URL handle.
char* url;
rc = curl_url_get(hurl, CURLUPART_URL, &url, 0);
if (rc == CURLUE_OK) {
out = url;
curl_free(url);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint substitute_host: failed to extract the URL after substitution";
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint substitute_host: failed to substitute host " << sub_addr << " in URL " << orig_addr;
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint substitute_host: failed to parse URL " << orig_addr;
curl_url_cleanup(hurl);
}
else
BOOST_LOG_TRIVIAL(error) << "OctoPrint substitute_host: failed to allocate curl_url";
return out;
}
#endif
}
Mainsail::Mainsail(DynamicPrintConfig *config) :
m_host(config->opt_string("print_host")),
m_apikey(config->opt_string("printhost_apikey")),
m_cafile(config->opt_string("printhost_cafile")),
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
{}
const char* Mainsail::get_name() const { return "Mainsail"; }
wxString Mainsail::get_test_ok_msg () const
{
return _(L("Connection to Mainsail works correctly."));
}
wxString Mainsail::get_test_failed_msg (wxString &msg) const
{
return GUI::format_wxstr("%s: %s"
, _L("Could not connect to Mainsail")
, msg);
}
bool Mainsail::test(wxString& msg) const
{
// GET /server/info
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
const char* name = get_name();
bool res = true;
auto url = make_url("server/info");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got server/info: %2%") % name % body;
try {
// All successful HTTP requests will return a json encoded object in the form of :
// {result: <response data>}
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
if (ptree.front().first != "result") {
msg = "Could not parse server response";
res = false;
return;
}
if (!ptree.front().second.get_optional<std::string>("moonraker_version")) {
msg = "Could not parse server response";
res = false;
return;
}
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Got version: %2%") % name % ptree.front().second.get_optional<std::string>("moonraker_version");
} catch (const std::exception&) {
res = false;
msg = "Could not parse server response";
}
})
#ifdef _WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Remember resolved address to be reused at successive REST API call.
msg = GUI::from_u8(address);
})
#endif // _WIN32
.perform_sync();
return res;
}
bool Mainsail::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
{
// POST /server/files/upload
const char* name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
// If test fails, test_msg_or_host_ip contains the error message.
wxString test_msg_or_host_ip;
if (!test(test_msg_or_host_ip)) {
error_fn(std::move(test_msg_or_host_ip));
return false;
}
std::string url;
bool res = true;
#ifdef WIN32
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
#endif // _WIN32
{
// If https is entered we assume signed ceritificate is being used
// IP resolving will not happen - it could resolve into address not being specified in cert
url = make_url("server/files/upload");
}
#ifdef WIN32
else {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Curl uses easy_getinfo to get ip address of last successful transaction.
// If it got the address use it instead of the stored in "host" variable.
// This new address returns in "test_msg_or_host_ip" variable.
// Solves troubles of uploades failing with name address.
// in original address (m_host) replace host for resolved ip
info_fn(L"resolve", test_msg_or_host_ip);
url = substitute_host(make_url("server/files/upload"), GUI::into_u8(test_msg_or_host_ip));
BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
}
#endif // _WIN32
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
% name
% upload_data.source_path
% url
% upload_filename.string()
% upload_parent_path.string()
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
/*
The file must be uploaded in the request's body multipart/form-data (ie: <input type="file">). The following arguments may also be added to the form-data:
root: The root location in which to upload the file.Currently this may be gcodes or config.If not specified the default is gcodes.
path : This argument may contain a path(relative to the root) indicating a subdirectory to which the file is written.If a path is present the server will attempt to create any subdirectories that do not exist.
checksum : A SHA256 hex digest calculated by the client for the uploaded file.If this argument is supplied the server will compare it to its own checksum calculation after the upload has completed.A checksum mismatch will result in a 422 error.
Arguments available only for the gcodes root :
print: If set to "true", Klippy will attempt to start the print after uploading.Note that this value should be a string type, not boolean.This provides compatibility with OctoPrint's upload API.
*/
auto http = Http::post(std::move(url));
set_auth(http);
http.form_add("root", "gcodes");
if (!upload_parent_path.empty())
http.form_add("path", upload_parent_path.string());
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
http.form_add("print", "true");
http.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool& cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
res = false;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
#endif
.perform_sync();
return res;
}
void Mainsail::set_auth(Http &http) const
{
if (!m_apikey.empty())
http.header("X-Api-Key", m_apikey);
if (!m_cafile.empty())
http.ca_file(m_cafile);
}
std::string Mainsail::make_url(const std::string &path) const
{
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
if (m_host.back() == '/') {
return (boost::format("%1%%2%") % m_host % path).str();
} else {
return (boost::format("%1%/%2%") % m_host % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % m_host % path).str();
}
}
}

View File

@ -0,0 +1,64 @@
#ifndef slic3r_Mainsail_hpp_
#define slic3r_Mainsail_hpp_
#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#include <boost/asio/ip/address.hpp>
#include "PrintHost.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
// https://moonraker.readthedocs.io/en/latest/web_api
class Mainsail : public PrintHost
{
public:
Mainsail(DynamicPrintConfig *config);
~Mainsail() override = default;
const char* get_name() const override;
virtual bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg () const override;
wxString get_test_failed_msg (wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
bool has_auto_discovery() const override { return true; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
std::string get_host() const override { return m_host; }
const std::string& get_apikey() const { return m_apikey; }
const std::string& get_cafile() const { return m_cafile; }
protected:
/*
#ifdef WIN32
virtual bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const;
#endif
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const;
*/
std::string m_host;
std::string m_apikey;
std::string m_cafile;
bool m_ssl_revoke_best_effort;
virtual void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
private:
/*
#ifdef WIN32
bool test_with_resolved_ip(wxString& curl_msg) const;
#endif
*/
};
}
#endif

View File

@ -203,7 +203,7 @@ bool OctoPrint::test_with_resolved_ip(wxString &msg) const
const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text);
if (!res) {
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
}
}
catch (const std::exception&) {
@ -252,7 +252,7 @@ bool OctoPrint::test(wxString& msg) const
const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text);
if (! res) {
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : name));
}
}
catch (const std::exception &) {
@ -396,7 +396,7 @@ bool OctoPrint::upload_inner_with_resolved_ip(PrintHostUpload upload_data, Progr
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
result = false;
}
})
@ -473,7 +473,7 @@ bool OctoPrint::upload_inner_with_host(PrintHostUpload upload_data, ProgressFn p
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
res = false;
}
})
@ -1126,5 +1126,4 @@ wxString PrusaConnect::get_test_failed_msg(wxString& msg) const
{
return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa Connect"), msg);
}
}

View File

@ -117,16 +117,6 @@ protected:
void set_http_post_header_args(Http& http, PrintHostPostUploadAction post_action) const override;
};
class Mainsail : public OctoPrint
{
public:
Mainsail(DynamicPrintConfig* config) : OctoPrint(config) {}
~Mainsail() override = default;
const char* get_name() const override { return "Mainsail/Fluidd"; }
};
class SL1Host : public PrusaLink
{
public:

View File

@ -19,6 +19,7 @@
#include "AstroBox.hpp"
#include "Repetier.hpp"
#include "MKS.hpp"
#include "Mainsail.hpp"
#include "../GUI/PrintHostDialogs.hpp"
namespace fs = boost::filesystem;