Merge branch 'master' into lm_sla_supports_auto
BIN
resources/icons/Slic3r_32px.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1,6 +1,21 @@
|
||||
min_slic3r_version = 1.42.0-alpha
|
||||
0.4.0-alpha3 Update of SLA profiles
|
||||
0.4.0-alpha2 First SLA profiles
|
||||
min_slic3r_version = 1.41.1
|
||||
0.3.3 Prusament PETG released
|
||||
0.3.2 New MK2.5 and MK3 FW versions
|
||||
0.3.1 New MK2.5 and MK3 FW versions
|
||||
0.3.0 New MK2.5 and MK3 FW version
|
||||
min_slic3r_version = 1.41.0-alpha
|
||||
0.2.9 New MK2.5 and MK3 FW versions
|
||||
0.2.8 New MK2.5 and MK3 FW version
|
||||
min_slic3r_version = 1.41.1
|
||||
0.2.7 New MK2.5 and MK3 FW version
|
||||
0.2.6 Added MMU2 MK2.5 settings
|
||||
min_slic3r_version = 1.41.0-alpha
|
||||
0.2.5 Prusament is out - added prusament settings
|
||||
0.2.4 Added soluble support profiles for MMU2
|
||||
0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
|
||||
0.2.2 Edited MMU2 Single mode purge line
|
||||
0.2.1 Added PET and BVOH settings for MMU2
|
||||
0.2.0-beta5 Fixed MMU1 ramming parameters
|
||||
|
@ -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 Slic3r configuration to be downgraded.
|
||||
config_version = 0.4.0-alpha2
|
||||
config_version = 0.4.0-alpha3
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
|
||||
|
||||
@ -41,7 +41,7 @@ variants = 0.4
|
||||
|
||||
[printer_model:SL1]
|
||||
name = Original Prusa SL1
|
||||
variants = default; dummy
|
||||
variants = default
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
@ -1137,19 +1137,132 @@ min_fan_speed = 100
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
|
||||
temperature = 220
|
||||
|
||||
[sla_material:*common*]
|
||||
[sla_print:*common*]
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/
|
||||
layer_height = 0.05
|
||||
initial_layer_height = 0.3
|
||||
output_filename_format = [input_filename_base].dwz
|
||||
pad_edge_radius = 0.5
|
||||
pad_enable = 1
|
||||
pad_max_merge_distance = 50
|
||||
pad_wall_height = 3
|
||||
pad_wall_thickness = 1
|
||||
support_base_diameter = 3
|
||||
support_base_height = 0.5
|
||||
support_critical_angle = 45
|
||||
support_density_at_45 = 250
|
||||
support_density_at_horizontal = 500
|
||||
support_head_front_diameter = 0.4
|
||||
support_head_penetration = 0.4
|
||||
support_head_width = 3
|
||||
support_max_bridge_length = 10
|
||||
support_minimal_z = 0
|
||||
support_object_elevation = 5
|
||||
support_pillar_diameter = 1
|
||||
support_pillar_widening_factor = 0
|
||||
supports_enable = 1
|
||||
|
||||
[sla_print:0.025 UltraDetail]
|
||||
inherits = *common*
|
||||
layer_height = 0.025
|
||||
support_head_width = 2
|
||||
|
||||
[sla_print:0.035 Detail]
|
||||
inherits = *common*
|
||||
layer_height = 0.035
|
||||
|
||||
[sla_print:0.05 Normal]
|
||||
inherits = *common*
|
||||
layer_height = 0.05
|
||||
|
||||
[sla_print:0.1 Fast]
|
||||
inherits = *common*
|
||||
layer_height = 0.1
|
||||
|
||||
########### Materials 0.025
|
||||
|
||||
[sla_material:*common 0.05*]
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/
|
||||
compatible_prints_condition = layer_height == 0.05
|
||||
exposure_time = 12
|
||||
initial_exposure_time = 45
|
||||
initial_layer_height = 0.5
|
||||
material_correction_curing = 1,1,1
|
||||
material_correction_printing = 1,1,1
|
||||
material_notes =
|
||||
|
||||
[sla_material:*common 0.025*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.025
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 15
|
||||
material_correction_printing = 1, 1, 1
|
||||
material_correction_curing = 1, 1, 1
|
||||
initial_exposure_time = 35
|
||||
|
||||
[sla_material:Material 1]
|
||||
inherits = *common*
|
||||
[sla_material:*common 0.035*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.035
|
||||
exposure_time = 13
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:Material 2]
|
||||
inherits = *common*
|
||||
[sla_material:*common 0.1*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.1
|
||||
exposure_time = 20
|
||||
initial_exposure_time = 90
|
||||
|
||||
########### Materials 0.025
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
########### Materials 0.05
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
########### Materials 0.035
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
########### Materials 0.1
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[printer:*common*]
|
||||
printer_technology = FFF
|
||||
@ -1467,21 +1580,20 @@ end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG
|
||||
printer_technology = SLA
|
||||
printer_model = SL1
|
||||
printer_variant = default
|
||||
default_sla_material_profile = Material 1
|
||||
bed_shape = 0x0,150x0,150x100,0x100
|
||||
max_print_height = 100
|
||||
display_width = 150
|
||||
display_height = 100
|
||||
display_pixels_x = 2000
|
||||
display_pixels_y = 1000
|
||||
default_sla_material_profile = Jamg He Transparent Green 0.05
|
||||
default_sla_print_profile = 0.05 Normal
|
||||
bed_shape = 0.98x1.02,119.98x1.02,119.98x68.02,0.98x68.02
|
||||
display_height = 68.04
|
||||
display_orientation = portrait
|
||||
display_pixels_x = 2560
|
||||
display_pixels_y = 1440
|
||||
display_width = 120.96
|
||||
max_print_height = 150
|
||||
printer_correction = 1,1,1
|
||||
|
||||
[printer:Original Prusa SL1 dummy]
|
||||
inherits = Original Prusa SL1
|
||||
printer_variant = dummy
|
||||
default_sla_material_profile = Material 2
|
||||
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_PRUSA3D\nPRINTER_MODEL_SL1\n
|
||||
|
||||
# The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer.
|
||||
[obsolete_presets]
|
||||
print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2"
|
||||
filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm"
|
||||
printer="Original Prusa SL1 dummy"
|
||||
|
@ -545,8 +545,8 @@ public:
|
||||
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
||||
|
||||
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
|
||||
_NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
_NofitPolyPlacer(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
|
||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
|
||||
#endif
|
||||
|
||||
static inline double overfit(const Box& bb, const RawShape& bin) {
|
||||
|
@ -77,8 +77,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Unlocked observers/hints
|
||||
// Thread unsafe! Keep in mind you need to re-verify the result after locking!
|
||||
// Unlocked observer/hint. Thread unsafe! Keep in mind you need to re-verify the result after locking.
|
||||
size_t size_hint() const noexcept { return m_queue.size(); }
|
||||
|
||||
LockedConstPtr lock_read() const
|
||||
|
@ -135,11 +135,6 @@ objfunc(const PointImpl& bincenter,
|
||||
const ItemGroup& remaining
|
||||
)
|
||||
{
|
||||
using Coord = TCoord<PointImpl>;
|
||||
|
||||
static const double ROUNDNESS_RATIO = 0.5;
|
||||
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
|
||||
|
||||
// We will treat big items (compared to the print bed) differently
|
||||
auto isBig = [bin_area](double a) {
|
||||
return a/bin_area > BIG_ITEM_TRESHOLD ;
|
||||
@ -629,11 +624,12 @@ BedShapeHint bedShape(const Polyline &bed) {
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
Circle ret(center, avg_dist);
|
||||
for (auto el: vertex_distances)
|
||||
for(auto el : vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
|
||||
ret = Circle();
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -665,8 +661,6 @@ bool arrange(Model &model,
|
||||
std::function<void (unsigned)> progressind,
|
||||
std::function<bool ()> stopcondition)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
|
@ -722,6 +722,10 @@ public:
|
||||
return m_pad;
|
||||
}
|
||||
|
||||
void remove_pad() {
|
||||
m_pad = Pad();
|
||||
}
|
||||
|
||||
const Pad& pad() const { return m_pad; }
|
||||
|
||||
// WITHOUT THE PAD!!!
|
||||
@ -1729,6 +1733,11 @@ const TriangleMesh &SLASupportTree::get_pad() const
|
||||
return m_impl->pad().tmesh;
|
||||
}
|
||||
|
||||
void SLASupportTree::remove_pad()
|
||||
{
|
||||
m_impl->remove_pad();
|
||||
}
|
||||
|
||||
SLASupportTree::SLASupportTree(const PointSet &points,
|
||||
const EigenMesh3D& emesh,
|
||||
const SupportConfig &cfg,
|
||||
|
@ -164,6 +164,8 @@ public:
|
||||
/// Get the pad geometry
|
||||
const TriangleMesh& get_pad() const;
|
||||
|
||||
void remove_pad();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -107,7 +107,8 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh,
|
||||
// structure
|
||||
EigenMesh3D mesh;
|
||||
Eigen::VectorXi SVI, SVJ;
|
||||
igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6,
|
||||
static const double dEPS = 1e-6;
|
||||
igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS,
|
||||
mesh.V, SVI, SVJ, mesh.F);
|
||||
|
||||
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
||||
@ -155,6 +156,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh,
|
||||
ia = trindex(0); ib = trindex(2);
|
||||
}
|
||||
|
||||
// vector for the neigboring triangles including the detected one.
|
||||
std::vector<Vec3i> neigh;
|
||||
if(ic >= 0) { // The point is right on a vertex of the triangle
|
||||
for(int n = 0; n < mesh.F.rows(); ++n) {
|
||||
@ -175,17 +177,32 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh,
|
||||
}
|
||||
}
|
||||
|
||||
if(!neigh.empty()) { // there were neighbors to count with
|
||||
// Calculate the normals for the neighboring triangles
|
||||
std::vector<Vec3d> neighnorms; neighnorms.reserve(neigh.size());
|
||||
for(const Vec3i& tri : neigh) {
|
||||
const Vec3d& pt1 = mesh.V.row(tri(0));
|
||||
const Vec3d& pt2 = mesh.V.row(tri(1));
|
||||
const Vec3d& pt3 = mesh.V.row(tri(2));
|
||||
Eigen::Vector3d U = pt2 - pt1;
|
||||
Eigen::Vector3d V = pt3 - pt1;
|
||||
neighnorms.emplace_back(U.cross(V).normalized());
|
||||
}
|
||||
|
||||
// Throw out duplicates. They would case trouble with summing.
|
||||
auto lend = std::unique(neighnorms.begin(), neighnorms.end(),
|
||||
[](const Vec3d& n1, const Vec3d& n2) {
|
||||
// Compare normals for equivalence. This is controvers stuff.
|
||||
// We will go for the third significant digit precision.
|
||||
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);
|
||||
for(const Vec3i& tri : neigh) {
|
||||
const Vec3d& pt1 = mesh.V.row(tri(0));
|
||||
const Vec3d& pt2 = mesh.V.row(tri(1));
|
||||
const Vec3d& pt3 = mesh.V.row(tri(2));
|
||||
Eigen::Vector3d U = pt2 - pt1;
|
||||
Eigen::Vector3d V = pt3 - pt1;
|
||||
sumnorm += U.cross(V).normalized();
|
||||
}
|
||||
sumnorm /= neigh.size();
|
||||
sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm);
|
||||
sumnorm.normalize();
|
||||
ret.row(i) = sumnorm;
|
||||
}
|
||||
else { // point lies safely within its triangle
|
||||
|
@ -592,9 +592,13 @@ void SLAPrint::process()
|
||||
// and before the supports had been sliced. (or the slicing has to be
|
||||
// repeated)
|
||||
|
||||
if(po.m_config.pad_enable.getBool() &&
|
||||
po.m_supportdata &&
|
||||
po.m_supportdata->support_tree_ptr)
|
||||
if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Uninitialized support data at "
|
||||
<< "pad creation.";
|
||||
return;
|
||||
}
|
||||
|
||||
if(po.m_config.pad_enable.getBool())
|
||||
{
|
||||
double wt = po.m_config.pad_wall_thickness.getFloat();
|
||||
double h = po.m_config.pad_wall_height.getFloat();
|
||||
@ -618,6 +622,8 @@ void SLAPrint::process()
|
||||
|
||||
pcfg.throw_on_cancel = thrfn;
|
||||
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
|
||||
} else {
|
||||
po.m_supportdata->support_tree_ptr->remove_pad();
|
||||
}
|
||||
|
||||
po.throw_if_canceled();
|
||||
@ -896,6 +902,7 @@ void SLAPrint::process()
|
||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||
report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
|
||||
pobj_program[currentstep](*po);
|
||||
throw_if_canceled();
|
||||
po->set_done(currentstep);
|
||||
}
|
||||
|
||||
@ -921,6 +928,7 @@ void SLAPrint::process()
|
||||
{
|
||||
report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]);
|
||||
print_program[currentstep]();
|
||||
throw_if_canceled();
|
||||
set_done(currentstep);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0)
|
||||
// Show visual hints in the 3D scene when sidebar matrix fields have focus
|
||||
#define ENABLE_SIDEBAR_VISUAL_HINTS (0 && ENABLE_1_42_0)
|
||||
#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0)
|
||||
|
||||
#endif // _technologies_h_
|
||||
|
||||
|
@ -1872,6 +1872,7 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
|
||||
GLModel::GLModel()
|
||||
: m_useVBOs(false)
|
||||
{
|
||||
m_volume.shader_outside_printer_detection_enabled = false;
|
||||
}
|
||||
|
||||
GLModel::~GLModel()
|
||||
@ -1879,11 +1880,36 @@ GLModel::~GLModel()
|
||||
m_volume.release_geometry();
|
||||
}
|
||||
|
||||
void GLModel::set_color(float* color, unsigned int size)
|
||||
void GLModel::set_color(const float* color, unsigned int size)
|
||||
{
|
||||
m_volume.set_render_color(color, size);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_offset() const
|
||||
{
|
||||
return m_volume.get_volume_offset();
|
||||
}
|
||||
|
||||
void GLModel::set_offset(const Vec3d& offset)
|
||||
{
|
||||
m_volume.set_volume_offset(offset);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_rotation() const
|
||||
{
|
||||
return m_volume.get_volume_rotation();
|
||||
}
|
||||
|
||||
void GLModel::set_rotation(const Vec3d& rotation)
|
||||
{
|
||||
m_volume.set_volume_rotation(rotation);
|
||||
}
|
||||
|
||||
const Vec3d& GLModel::get_scale() const
|
||||
{
|
||||
return m_volume.get_volume_scaling_factor();
|
||||
}
|
||||
|
||||
void GLModel::set_scale(const Vec3d& scale)
|
||||
{
|
||||
m_volume.set_volume_scaling_factor(scale);
|
||||
@ -1894,8 +1920,7 @@ void GLModel::render() const
|
||||
if (m_useVBOs)
|
||||
render_VBOs();
|
||||
else
|
||||
{
|
||||
}
|
||||
render_legacy();
|
||||
}
|
||||
|
||||
void GLModel::render_VBOs() const
|
||||
@ -1910,8 +1935,9 @@ void GLModel::render_VBOs() const
|
||||
GLint current_program_id;
|
||||
::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id);
|
||||
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
|
||||
GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
|
||||
|
||||
m_volume.render_VBOs(color_id, -1, -1);
|
||||
m_volume.render_VBOs(color_id, print_box_detection_id, -1);
|
||||
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
@ -1922,9 +1948,23 @@ void GLModel::render_VBOs() const
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
GLArrow::GLArrow()
|
||||
: GLModel()
|
||||
void GLModel::render_legacy() const
|
||||
{
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
::glCullFace(GL_BACK);
|
||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||
::glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
m_volume.render_legacy();
|
||||
|
||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
bool GLArrow::on_init(bool useVBOs)
|
||||
@ -1932,7 +1972,7 @@ bool GLArrow::on_init(bool useVBOs)
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3crd> triangles;
|
||||
|
||||
// top face
|
||||
// bottom face
|
||||
vertices.emplace_back(0.5, 0.0, -0.1);
|
||||
vertices.emplace_back(0.5, 2.0, -0.1);
|
||||
vertices.emplace_back(1.0, 2.0, -0.1);
|
||||
@ -1941,7 +1981,7 @@ bool GLArrow::on_init(bool useVBOs)
|
||||
vertices.emplace_back(-0.5, 2.0, -0.1);
|
||||
vertices.emplace_back(-0.5, 0.0, -0.1);
|
||||
|
||||
// bottom face
|
||||
// top face
|
||||
vertices.emplace_back(0.5, 0.0, 0.1);
|
||||
vertices.emplace_back(0.5, 2.0, 0.1);
|
||||
vertices.emplace_back(1.0, 2.0, 0.1);
|
||||
@ -1990,6 +2030,126 @@ bool GLArrow::on_init(bool useVBOs)
|
||||
m_volume.finalize_geometry(m_useVBOs);
|
||||
return true;
|
||||
}
|
||||
|
||||
GLCurvedArrow::GLCurvedArrow(unsigned int resolution)
|
||||
: GLModel()
|
||||
, m_resolution(resolution)
|
||||
{
|
||||
if (m_resolution == 0)
|
||||
m_resolution = 1;
|
||||
}
|
||||
|
||||
bool GLCurvedArrow::on_init(bool useVBOs)
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<Vec3crd> triangles;
|
||||
|
||||
double ext_radius = 2.5;
|
||||
double int_radius = 1.5;
|
||||
double step = 0.5 * (double)PI / (double)m_resolution;
|
||||
|
||||
unsigned int vertices_per_level = 4 + 2 * m_resolution;
|
||||
|
||||
// bottom face
|
||||
vertices.emplace_back(0.0, 1.5, -0.1);
|
||||
vertices.emplace_back(0.0, 1.0, -0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, -0.1);
|
||||
vertices.emplace_back(0.0, 3.0, -0.1);
|
||||
vertices.emplace_back(0.0, 2.5, -0.1);
|
||||
|
||||
for (unsigned int i = 1; i <= m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = ext_radius * ::sin(angle);
|
||||
double y = ext_radius * ::cos(angle);
|
||||
|
||||
vertices.emplace_back(x, y, -0.1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = int_radius * ::cos(angle);
|
||||
double y = int_radius * ::sin(angle);
|
||||
|
||||
vertices.emplace_back(x, y, -0.1);
|
||||
}
|
||||
|
||||
// top face
|
||||
vertices.emplace_back(0.0, 1.5, 0.1);
|
||||
vertices.emplace_back(0.0, 1.0, 0.1);
|
||||
vertices.emplace_back(-1.0, 2.0, 0.1);
|
||||
vertices.emplace_back(0.0, 3.0, 0.1);
|
||||
vertices.emplace_back(0.0, 2.5, 0.1);
|
||||
|
||||
for (unsigned int i = 1; i <= m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = ext_radius * ::sin(angle);
|
||||
double y = ext_radius * ::cos(angle);
|
||||
|
||||
vertices.emplace_back(x, y, 0.1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < m_resolution; ++i)
|
||||
{
|
||||
double angle = (double)i * step;
|
||||
double x = int_radius * ::cos(angle);
|
||||
double y = int_radius * ::sin(angle);
|
||||
|
||||
vertices.emplace_back(x, y, 0.1);
|
||||
}
|
||||
|
||||
// bottom face
|
||||
triangles.emplace_back(0, 1, 2);
|
||||
triangles.emplace_back(0, 2, 4);
|
||||
triangles.emplace_back(4, 2, 3);
|
||||
|
||||
int first_id = 4;
|
||||
int last_id = (int)vertices_per_level;
|
||||
triangles.emplace_back(last_id, 0, first_id);
|
||||
triangles.emplace_back(last_id, first_id, first_id + 1);
|
||||
for (unsigned int i = 1; i < m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(last_id - i, last_id - i + 1, first_id + i);
|
||||
triangles.emplace_back(last_id - i, first_id + i, first_id + i + 1);
|
||||
}
|
||||
|
||||
// top face
|
||||
last_id += 1;
|
||||
triangles.emplace_back(last_id + 0, last_id + 2, last_id + 1);
|
||||
triangles.emplace_back(last_id + 0, last_id + 4, last_id + 2);
|
||||
triangles.emplace_back(last_id + 4, last_id + 3, last_id + 2);
|
||||
|
||||
first_id = last_id + 4;
|
||||
last_id = last_id + 4 + 2 * (int)m_resolution;
|
||||
triangles.emplace_back(last_id, first_id, (int)vertices_per_level + 1);
|
||||
triangles.emplace_back(last_id, first_id + 1, first_id);
|
||||
for (unsigned int i = 1; i < m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(last_id - i, first_id + i, last_id - i + 1);
|
||||
triangles.emplace_back(last_id - i, first_id + i + 1, first_id + i);
|
||||
}
|
||||
|
||||
// side face
|
||||
for (unsigned int i = 0; i < 4 + 2 * (int)m_resolution; ++i)
|
||||
{
|
||||
triangles.emplace_back(i, vertices_per_level + 2 + i, i + 1);
|
||||
triangles.emplace_back(i, vertices_per_level + 1 + i, vertices_per_level + 2 + i);
|
||||
}
|
||||
triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0);
|
||||
triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1);
|
||||
|
||||
m_useVBOs = useVBOs;
|
||||
|
||||
if (m_useVBOs)
|
||||
m_volume.indexed_vertex_array.load_mesh_full_shading(TriangleMesh(vertices, triangles));
|
||||
else
|
||||
m_volume.indexed_vertex_array.load_mesh_flat_shading(TriangleMesh(vertices, triangles));
|
||||
|
||||
m_volume.finalize_geometry(m_useVBOs);
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
|
||||
|
@ -595,7 +595,13 @@ public:
|
||||
|
||||
bool init(bool useVBOs) { return on_init(useVBOs); }
|
||||
|
||||
void set_color(float* color, unsigned int size);
|
||||
void set_color(const float* color, unsigned int size);
|
||||
|
||||
const Vec3d& get_offset() const;
|
||||
void set_offset(const Vec3d& offset);
|
||||
const Vec3d& get_rotation() const;
|
||||
void set_rotation(const Vec3d& rotation);
|
||||
const Vec3d& get_scale() const;
|
||||
void set_scale(const Vec3d& scale);
|
||||
|
||||
void render() const;
|
||||
@ -605,12 +611,21 @@ protected:
|
||||
|
||||
private:
|
||||
void render_VBOs() const;
|
||||
void render_legacy() const;
|
||||
};
|
||||
|
||||
class GLArrow : public GLModel
|
||||
{
|
||||
protected:
|
||||
virtual bool on_init(bool useVBOs);
|
||||
};
|
||||
|
||||
class GLCurvedArrow : public GLModel
|
||||
{
|
||||
unsigned int m_resolution;
|
||||
|
||||
public:
|
||||
GLArrow();
|
||||
explicit GLCurvedArrow(unsigned int resolution);
|
||||
|
||||
protected:
|
||||
virtual bool on_init(bool useVBOs);
|
||||
|
@ -84,26 +84,7 @@ void BackgroundSlicingProcess::process_fff()
|
||||
run_post_process_scripts(export_path, m_fff_print->config());
|
||||
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||
} else if (! m_upload_job.empty()) {
|
||||
// A print host upload job has been scheduled
|
||||
|
||||
// XXX: is fs::path::string() right?
|
||||
|
||||
// Generate a unique temp path to which the gcode is copied
|
||||
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||
|
||||
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
|
||||
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||
}
|
||||
|
||||
m_print->set_status(95, "Running post-processing scripts");
|
||||
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||
|
||||
m_upload_job.upload_data.source_path = std::move(source_path);
|
||||
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||
|
||||
GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job));
|
||||
prepare_upload();
|
||||
} else {
|
||||
m_print->set_status(100, "Slicing complete");
|
||||
}
|
||||
@ -170,6 +151,10 @@ void BackgroundSlicingProcess::process_sla()
|
||||
if (! m_export_path.empty()) {
|
||||
m_sla_print->export_raster<SLAZipFmt>(m_export_path);
|
||||
m_print->set_status(100, "Zip file exported to " + m_export_path);
|
||||
} else if (! m_upload_job.empty()) {
|
||||
prepare_upload();
|
||||
} else {
|
||||
m_print->set_status(100, "Slicing complete");
|
||||
}
|
||||
this->set_step_done(bspsGCodeFinalize);
|
||||
}
|
||||
@ -440,4 +425,35 @@ bool BackgroundSlicingProcess::invalidate_all_steps()
|
||||
return m_step_state.invalidate_all([this](){ this->stop_internal(); });
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::prepare_upload()
|
||||
{
|
||||
// A print host upload job has been scheduled, enqueue it to the printhost job queue
|
||||
|
||||
// XXX: is fs::path::string() right?
|
||||
|
||||
// Generate a unique temp path to which the gcode/zip file is copied/exported
|
||||
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||
|
||||
if (m_print == m_fff_print) {
|
||||
m_print->set_status(95, "Running post-processing scripts");
|
||||
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||
|
||||
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
|
||||
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||
}
|
||||
|
||||
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||
} else {
|
||||
m_sla_print->export_raster<SLAZipFmt>(source_path.string());
|
||||
// TODO: Also finalize upload path like with FFF when there are statistics for SLA print
|
||||
}
|
||||
|
||||
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||
|
||||
m_upload_job.upload_data.source_path = std::move(source_path);
|
||||
|
||||
GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job));
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
@ -167,6 +167,7 @@ private:
|
||||
bool invalidate_all_steps();
|
||||
// If the background processing stop was requested, throw CanceledException.
|
||||
void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); }
|
||||
void prepare_upload();
|
||||
|
||||
// wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue.
|
||||
int m_event_slicing_completed_id = 0;
|
||||
|
@ -73,6 +73,11 @@ static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f };
|
||||
static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
|
||||
static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f };
|
||||
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f };
|
||||
static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@ -1154,12 +1159,6 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec
|
||||
}
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
const float GLCanvas3D::Selection::RED[3] = { 1.0f, 0.0f, 0.0f };
|
||||
const float GLCanvas3D::Selection::GREEN[3] = { 0.0f, 1.0f, 0.0f };
|
||||
const float GLCanvas3D::Selection::BLUE[3] = { 0.0f, 0.0f, 1.0f };
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
GLCanvas3D::Selection::Selection()
|
||||
: m_volumes(nullptr)
|
||||
, m_model(nullptr)
|
||||
@ -1167,6 +1166,7 @@ GLCanvas3D::Selection::Selection()
|
||||
, m_type(Empty)
|
||||
, m_valid(false)
|
||||
, m_bounding_box_dirty(true)
|
||||
, m_curved_arrow(16)
|
||||
{
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
m_quadric = ::gluNewQuadric();
|
||||
@ -1192,13 +1192,16 @@ void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes)
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
bool GLCanvas3D::Selection::init(bool useVBOs)
|
||||
{
|
||||
if (m_arrow.init(useVBOs))
|
||||
{
|
||||
m_arrow.set_scale(5.0 * Vec3d::Ones());
|
||||
return true;
|
||||
}
|
||||
if (!m_arrow.init(useVBOs))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
m_arrow.set_scale(5.0 * Vec3d::Ones());
|
||||
|
||||
if (!m_curved_arrow.init(useVBOs))
|
||||
return false;
|
||||
|
||||
m_curved_arrow.set_scale(5.0 * Vec3d::Ones());
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
@ -2094,14 +2097,22 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel
|
||||
else if (is_single_volume() || is_single_modifier())
|
||||
{
|
||||
const GLVolume* volume = (*m_volumes)[*m_list.begin()];
|
||||
Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true) * volume->get_volume_transformation().get_matrix(true, false, true, true);
|
||||
Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true);
|
||||
const Vec3d& offset = get_bounding_box().center();
|
||||
|
||||
::glTranslated(offset(0), offset(1), offset(2));
|
||||
::glMultMatrixd(orient_matrix.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
::glTranslated(center(0), center(1), center(2));
|
||||
if (requires_local_axes())
|
||||
{
|
||||
const GLVolume* volume = (*m_volumes)[*m_list.begin()];
|
||||
Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true);
|
||||
::glMultMatrixd(orient_matrix.data());
|
||||
}
|
||||
}
|
||||
|
||||
if (boost::starts_with(sidebar_field, "position"))
|
||||
_render_sidebar_position_hints(sidebar_field);
|
||||
@ -2544,6 +2555,18 @@ void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& si
|
||||
|
||||
void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const
|
||||
{
|
||||
if (boost::ends_with(sidebar_field, "x"))
|
||||
{
|
||||
::glRotated(90.0, 0.0, 1.0, 0.0);
|
||||
_render_sidebar_rotation_hint(X);
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "y"))
|
||||
{
|
||||
::glRotated(-90.0, 1.0, 0.0, 0.0);
|
||||
_render_sidebar_rotation_hint(Y);
|
||||
}
|
||||
else if (boost::ends_with(sidebar_field, "z"))
|
||||
_render_sidebar_rotation_hint(Z);
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const
|
||||
@ -2579,33 +2602,22 @@ void GLCanvas3D::Selection::_render_sidebar_size_hints(const std::string& sideba
|
||||
|
||||
void GLCanvas3D::Selection::_render_sidebar_position_hint(Axis axis) const
|
||||
{
|
||||
float color[3];
|
||||
switch (axis)
|
||||
{
|
||||
case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; }
|
||||
case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; }
|
||||
case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; }
|
||||
}
|
||||
|
||||
m_arrow.set_color(color, 3);
|
||||
m_arrow.set_color(AXES_COLOR[axis], 3);
|
||||
m_arrow.render();
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis, double length) const
|
||||
void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const
|
||||
{
|
||||
m_curved_arrow.set_color(AXES_COLOR[axis], 3);
|
||||
m_curved_arrow.render();
|
||||
|
||||
::glRotated(180.0, 0.0, 0.0, 1.0);
|
||||
m_curved_arrow.render();
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const
|
||||
{
|
||||
float color[3];
|
||||
switch (axis)
|
||||
{
|
||||
case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; }
|
||||
case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; }
|
||||
case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; }
|
||||
}
|
||||
|
||||
m_arrow.set_color(color, 3);
|
||||
m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3);
|
||||
|
||||
::glTranslated(0.0, 5.0, 0.0);
|
||||
m_arrow.render();
|
||||
@ -3818,7 +3830,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||
, m_legend_texture_enabled(false)
|
||||
, m_picking_enabled(false)
|
||||
, m_moving_enabled(false)
|
||||
, m_shader_enabled(false)
|
||||
, m_dynamic_background_enabled(false)
|
||||
, m_multisample_allowed(false)
|
||||
, m_regenerate_volumes(true)
|
||||
@ -4132,11 +4143,6 @@ void GLCanvas3D::enable_toolbar(bool enable)
|
||||
m_toolbar.set_enabled(enable);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_shader(bool enable)
|
||||
{
|
||||
m_shader_enabled = enable;
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_force_zoom_to_bed(bool enable)
|
||||
{
|
||||
m_force_zoom_to_bed_enabled = enable;
|
||||
@ -6310,9 +6316,7 @@ void GLCanvas3D::_render_objects() const
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
|
||||
if (!m_shader_enabled)
|
||||
_render_volumes(false);
|
||||
else if (m_use_VBOs)
|
||||
if (m_use_VBOs)
|
||||
{
|
||||
if (m_picking_enabled)
|
||||
{
|
||||
|
@ -369,12 +369,6 @@ class GLCanvas3D
|
||||
public:
|
||||
class Selection
|
||||
{
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
static const float RED[3];
|
||||
static const float GREEN[3];
|
||||
static const float BLUE[3];
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
public:
|
||||
typedef std::set<unsigned int> IndicesList;
|
||||
|
||||
@ -504,6 +498,7 @@ public:
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
mutable GLArrow m_arrow;
|
||||
mutable GLCurvedArrow m_curved_arrow;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
public:
|
||||
@ -619,7 +614,7 @@ public:
|
||||
void _render_sidebar_scale_hints(const std::string& sidebar_field) const;
|
||||
void _render_sidebar_size_hints(const std::string& sidebar_field) const;
|
||||
void _render_sidebar_position_hint(Axis axis) const;
|
||||
void _render_sidebar_rotation_hint(Axis axis, double length) const;
|
||||
void _render_sidebar_rotation_hint(Axis axis) const;
|
||||
void _render_sidebar_scale_hint(Axis axis) const;
|
||||
void _render_sidebar_size_hint(Axis axis, double length) const;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
@ -855,7 +850,6 @@ private:
|
||||
bool m_legend_texture_enabled;
|
||||
bool m_picking_enabled;
|
||||
bool m_moving_enabled;
|
||||
bool m_shader_enabled;
|
||||
bool m_dynamic_background_enabled;
|
||||
bool m_multisample_allowed;
|
||||
bool m_regenerate_volumes;
|
||||
@ -955,7 +949,6 @@ public:
|
||||
void enable_moving(bool enable);
|
||||
void enable_gizmos(bool enable);
|
||||
void enable_toolbar(bool enable);
|
||||
void enable_shader(bool enable);
|
||||
void enable_force_zoom_to_bed(bool enable);
|
||||
void enable_dynamic_background(bool enable);
|
||||
void allow_multisample(bool allow);
|
||||
|
@ -43,7 +43,7 @@ namespace GUI {
|
||||
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
static const wxString defaults[FT_SIZE] = {
|
||||
static const std::string defaults[FT_SIZE] = {
|
||||
/* FT_STL */ "STL files (*.stl)|*.stl;*.STL",
|
||||
/* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ",
|
||||
/* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
|
||||
@ -57,13 +57,17 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
/* FT_PNGZIP */"Zipped PNG files (*.zip)|*.zip;*.ZIP", // This is lame, but that's what we use for SLA
|
||||
};
|
||||
|
||||
wxString out = defaults[file_type];
|
||||
std::string out = defaults[file_type];
|
||||
if (! custom_extension.empty()) {
|
||||
// Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it.
|
||||
out += ";*";
|
||||
out += from_u8(custom_extension);
|
||||
// Find the custom extension in the template.
|
||||
if (out.find(std::string("*") + custom_extension + ",") == std::string::npos && out.find(std::string("*") + custom_extension + ")") == std::string::npos) {
|
||||
// The custom extension was not found in the template.
|
||||
// Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it.
|
||||
boost::replace_first(out, ")|", std::string(", *") + custom_extension + ")|");
|
||||
out += std::string(";*") + custom_extension;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
return wxString::FromUTF8(out.c_str());
|
||||
}
|
||||
|
||||
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
|
||||
@ -137,7 +141,7 @@ bool GUI_App::OnInit()
|
||||
std::cerr << "Creating main frame..." << std::endl;
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler());
|
||||
mainframe = new MainFrame(no_plater, false);
|
||||
mainframe = new MainFrame();
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
update_mode();
|
||||
SetTopWindow(mainframe);
|
||||
@ -177,6 +181,9 @@ bool GUI_App::OnInit()
|
||||
|
||||
if (app_config->dirty())
|
||||
app_config->save();
|
||||
|
||||
if (this->plater() != nullptr)
|
||||
this->obj_manipul()->update_if_dirty();
|
||||
});
|
||||
|
||||
// On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...)
|
||||
@ -277,8 +284,8 @@ void GUI_App::recreate_GUI()
|
||||
{
|
||||
std::cerr << "recreate_GUI" << std::endl;
|
||||
|
||||
auto topwindow = GetTopWindow();
|
||||
mainframe = new MainFrame(no_plater,false);
|
||||
MainFrame* topwindow = dynamic_cast<MainFrame*>(GetTopWindow());
|
||||
mainframe = new MainFrame();
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
update_mode();
|
||||
|
||||
@ -287,6 +294,20 @@ void GUI_App::recreate_GUI()
|
||||
topwindow->Destroy();
|
||||
}
|
||||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
CallAfter([this]() {
|
||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
auto& panel = sidebar();
|
||||
if (panel.obj_list()->GetMinHeight() > 200) {
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&panel);
|
||||
panel.obj_list()->SetMinSize(wxSize(-1, 200));
|
||||
panel.Layout();
|
||||
}
|
||||
});
|
||||
|
||||
mainframe->Show(true);
|
||||
|
||||
// On OSX the UI was not initialized correctly if the wizard was called
|
||||
// before the UI was up and running.
|
||||
CallAfter([]() {
|
||||
|
@ -71,7 +71,6 @@ static wxString dots("…", wxConvUTF8);
|
||||
|
||||
class GUI_App : public wxApp
|
||||
{
|
||||
bool no_plater{ false };
|
||||
bool app_conf_exists{ false };
|
||||
|
||||
// Lock to guard the callback stack
|
||||
|
@ -47,6 +47,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
m_og->m_fill_empty_value = [this](const std::string& opt_key)
|
||||
{
|
||||
this->update_if_dirty();
|
||||
|
||||
std::string param;
|
||||
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||
|
||||
@ -83,6 +85,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
m_og->m_set_focus = [this](const std::string& opt_key)
|
||||
{
|
||||
this->update_if_dirty();
|
||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true);
|
||||
};
|
||||
|
||||
@ -179,22 +182,19 @@ bool ObjectManipulation::IsShown()
|
||||
|
||||
void ObjectManipulation::UpdateAndShow(const bool show)
|
||||
{
|
||||
if (show)
|
||||
if (show) {
|
||||
update_settings_value(wxGetApp().plater()->canvas3D()->get_selection());
|
||||
update_if_dirty();
|
||||
}
|
||||
|
||||
OG_Settings::UpdateAndShow(show);
|
||||
}
|
||||
|
||||
int ObjectManipulation::ol_selection()
|
||||
{
|
||||
return wxGetApp().obj_list()->get_selected_obj_idx();
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
wxString move_label = _(L("Position:"));
|
||||
wxString rotate_label = _(L("Rotation:"));
|
||||
wxString scale_label = _(L("Scale factors:"));
|
||||
m_new_move_label_string = L("Position:");
|
||||
m_new_rotate_label_string = L("Rotation:");
|
||||
m_new_scale_label_string = L("Scale factors:");
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
if (selection.is_single_full_instance())
|
||||
#else
|
||||
@ -205,10 +205,10 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||
{
|
||||
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
update_position_value(volume->get_offset());
|
||||
update_rotation_value(volume->get_rotation());
|
||||
update_scale_value(volume->get_scaling_factor());
|
||||
m_og->enable();
|
||||
m_new_position = volume->get_offset();
|
||||
m_new_rotation = volume->get_rotation();
|
||||
m_new_scale = volume->get_scaling_factor();
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else
|
||||
reset_settings_value();
|
||||
@ -219,143 +219,104 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
update_position_value(volume->get_instance_offset());
|
||||
update_rotation_value(volume->get_instance_rotation());
|
||||
update_scale_value(volume->get_instance_scaling_factor());
|
||||
update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size());
|
||||
m_new_position = volume->get_instance_offset();
|
||||
m_new_rotation = volume->get_instance_rotation();
|
||||
m_new_scale = volume->get_instance_scaling_factor();
|
||||
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size();
|
||||
#else
|
||||
update_position_value(volume->get_offset());
|
||||
update_rotation_value(volume->get_rotation());
|
||||
update_scale_value(volume->get_scaling_factor());
|
||||
m_new_position = volume->get_offset();
|
||||
m_new_rotation = volume->get_rotation();
|
||||
m_new_scale = volume->get_scaling_factor();
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
m_og->enable();
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (selection.is_single_full_object())
|
||||
{
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
update_position_value(box.center());
|
||||
reset_rotation_value();
|
||||
reset_scale_value();
|
||||
update_size_value(box.size());
|
||||
rotate_label = _(L("Rotate:"));
|
||||
scale_label = _(L("Scale:"));
|
||||
m_og->enable();
|
||||
m_new_position = box.center();
|
||||
m_new_rotation = Vec3d::Zero();
|
||||
m_new_scale = Vec3d(1.0, 1.0, 1.0);
|
||||
m_new_size = box.size();
|
||||
m_new_rotate_label_string = L("Rotate:");
|
||||
m_new_scale_label_string = L("Scale:");
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (selection.is_single_modifier() || selection.is_single_volume())
|
||||
{
|
||||
// the selection contains a single volume
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
update_position_value(volume->get_volume_offset());
|
||||
update_rotation_value(volume->get_volume_rotation());
|
||||
update_scale_value(volume->get_volume_scaling_factor());
|
||||
update_size_value(volume->bounding_box.size());
|
||||
m_new_position = volume->get_volume_offset();
|
||||
m_new_rotation = volume->get_volume_rotation();
|
||||
m_new_scale = volume->get_volume_scaling_factor();
|
||||
m_new_size = volume->bounding_box.size();
|
||||
#else
|
||||
update_position_value(volume->get_offset());
|
||||
update_rotation_value(volume->get_rotation());
|
||||
update_scale_value(volume->get_scaling_factor());
|
||||
m_new_position = volume->get_offset();
|
||||
m_new_rotation = volume->get_rotation();
|
||||
m_new_scale = volume->get_scaling_factor();
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
m_og->enable();
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (wxGetApp().obj_list()->multiple_selection())
|
||||
{
|
||||
reset_settings_value();
|
||||
move_label = _(L("Translate:"));
|
||||
rotate_label = _(L("Rotate:"));
|
||||
scale_label = _(L("Scale:"));
|
||||
update_size_value(selection.get_bounding_box().size());
|
||||
m_og->enable();
|
||||
m_new_move_label_string = L("Translate:");
|
||||
m_new_rotate_label_string = L("Rotate:");
|
||||
m_new_scale_label_string = L("Scale:");
|
||||
m_new_size = selection.get_bounding_box().size();
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else
|
||||
reset_settings_value();
|
||||
|
||||
m_move_Label->SetLabel(move_label);
|
||||
m_rotate_Label->SetLabel(rotate_label);
|
||||
m_scale_Label->SetLabel(scale_label);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_if_dirty()
|
||||
{
|
||||
if (! m_dirty)
|
||||
return;
|
||||
|
||||
m_move_Label->SetLabel(_(m_new_move_label_string));
|
||||
m_rotate_Label->SetLabel(_(m_new_rotate_label_string));
|
||||
m_scale_Label->SetLabel(_(m_new_scale_label_string));
|
||||
|
||||
m_og->set_value("position_x", double_to_string(m_new_position(0), 2));
|
||||
m_og->set_value("position_y", double_to_string(m_new_position(1), 2));
|
||||
m_og->set_value("position_z", double_to_string(m_new_position(2), 2));
|
||||
cache_position = m_new_position;
|
||||
|
||||
auto scale = m_new_scale * 100.0;
|
||||
m_og->set_value("scale_x", double_to_string(scale(0), 2));
|
||||
m_og->set_value("scale_y", double_to_string(scale(1), 2));
|
||||
m_og->set_value("scale_z", double_to_string(scale(2), 2));
|
||||
cache_scale = scale;
|
||||
|
||||
m_og->set_value("size_x", double_to_string(m_new_size(0), 2));
|
||||
m_og->set_value("size_y", double_to_string(m_new_size(1), 2));
|
||||
m_og->set_value("size_z", double_to_string(m_new_size(2), 2));
|
||||
cache_size = m_new_size;
|
||||
|
||||
m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2));
|
||||
m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2));
|
||||
m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2));
|
||||
cache_rotation = m_new_rotation;
|
||||
|
||||
if (m_new_enabled)
|
||||
m_og->enable();
|
||||
else
|
||||
m_og->disable();
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_settings_value()
|
||||
{
|
||||
reset_position_value();
|
||||
reset_rotation_value();
|
||||
reset_scale_value();
|
||||
m_og->disable();
|
||||
}
|
||||
|
||||
wxString def_0 {"0"};
|
||||
wxString def_100 {"100"};
|
||||
|
||||
void ObjectManipulation::reset_position_value()
|
||||
{
|
||||
m_og->set_value("position_x", def_0);
|
||||
m_og->set_value("position_y", def_0);
|
||||
m_og->set_value("position_z", def_0);
|
||||
|
||||
cache_position = Vec3d::Zero();
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_rotation_value()
|
||||
{
|
||||
m_og->set_value("rotation_x", def_0);
|
||||
m_og->set_value("rotation_y", def_0);
|
||||
m_og->set_value("rotation_z", def_0);
|
||||
|
||||
cache_rotation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_scale_value()
|
||||
{
|
||||
m_og->set_value("scale_x", def_100);
|
||||
m_og->set_value("scale_y", def_100);
|
||||
m_og->set_value("scale_z", def_100);
|
||||
|
||||
cache_scale = Vec3d(100.0, 100.0, 100.0);
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_size_value()
|
||||
{
|
||||
m_og->set_value("size_x", def_0);
|
||||
m_og->set_value("size_y", def_0);
|
||||
m_og->set_value("size_z", def_0);
|
||||
|
||||
cache_size = Vec3d::Zero();
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_position_value(const Vec3d& position)
|
||||
{
|
||||
m_og->set_value("position_x", double_to_string(position(0), 2));
|
||||
m_og->set_value("position_y", double_to_string(position(1), 2));
|
||||
m_og->set_value("position_z", double_to_string(position(2), 2));
|
||||
|
||||
cache_position = position;
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor)
|
||||
{
|
||||
auto scale = scaling_factor * 100.0;
|
||||
m_og->set_value("scale_x", double_to_string(scale(0), 2));
|
||||
m_og->set_value("scale_y", double_to_string(scale(1), 2));
|
||||
m_og->set_value("scale_z", double_to_string(scale(2), 2));
|
||||
|
||||
cache_scale = scale;
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_size_value(const Vec3d& size)
|
||||
{
|
||||
m_og->set_value("size_x", double_to_string(size(0), 2));
|
||||
m_og->set_value("size_y", double_to_string(size(1), 2));
|
||||
m_og->set_value("size_z", double_to_string(size(2), 2));
|
||||
|
||||
cache_size = size;
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_rotation_value(const Vec3d& rotation)
|
||||
{
|
||||
m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2));
|
||||
m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2));
|
||||
m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2));
|
||||
|
||||
cache_rotation = rotation;
|
||||
m_new_position = Vec3d::Zero();
|
||||
m_new_rotation = Vec3d::Zero();
|
||||
m_new_scale = Vec3d(1.0, 1.0, 1.0);
|
||||
m_new_size = Vec3d::Zero();
|
||||
m_new_enabled = false;
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_position_value(const Vec3d& position)
|
||||
|
@ -23,6 +23,19 @@ class ObjectManipulation : public OG_Settings
|
||||
wxStaticText* m_scale_Label = nullptr;
|
||||
wxStaticText* m_rotate_Label = nullptr;
|
||||
|
||||
|
||||
// Needs to be updated from OnIdle?
|
||||
bool m_dirty = false;
|
||||
// Cached labels for the delayed update, not localized!
|
||||
std::string m_new_move_label_string;
|
||||
std::string m_new_rotate_label_string;
|
||||
std::string m_new_scale_label_string;
|
||||
Vec3d m_new_position;
|
||||
Vec3d m_new_rotation;
|
||||
Vec3d m_new_scale;
|
||||
Vec3d m_new_size;
|
||||
bool m_new_enabled;
|
||||
|
||||
public:
|
||||
ObjectManipulation(wxWindow* parent);
|
||||
~ObjectManipulation() {}
|
||||
@ -31,19 +44,14 @@ public:
|
||||
bool IsShown() override;
|
||||
void UpdateAndShow(const bool show) override;
|
||||
|
||||
int ol_selection();
|
||||
void update_settings_value(const GLCanvas3D::Selection& selection);
|
||||
|
||||
void update_settings_value(const GLCanvas3D::Selection& selection);
|
||||
// Called from the App to update the UI if dirty.
|
||||
void update_if_dirty();
|
||||
|
||||
private:
|
||||
void reset_settings_value();
|
||||
void reset_position_value();
|
||||
void reset_rotation_value();
|
||||
void reset_scale_value();
|
||||
void reset_size_value();
|
||||
|
||||
// update position values displacements or "gizmos"
|
||||
void update_position_value(const Vec3d& position);
|
||||
// update scale values after scale unit changing or "gizmos"
|
||||
void update_scale_value(const Vec3d& scaling_factor);
|
||||
// update size values after scale unit changing or "gizmos"
|
||||
void update_size_value(const Vec3d& size);
|
||||
// update rotation value after "gizmos"
|
||||
|
@ -70,7 +70,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
||||
m_canvas->set_config(config);
|
||||
m_canvas->enable_gizmos(true);
|
||||
m_canvas->enable_toolbar(true);
|
||||
m_canvas->enable_shader(true);
|
||||
m_canvas->enable_force_zoom_to_bed(true);
|
||||
|
||||
#if !ENABLE_IMGUI
|
||||
@ -214,7 +213,6 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSli
|
||||
, m_preferred_color_mode("feature")
|
||||
, m_loaded(false)
|
||||
, m_enabled(false)
|
||||
, m_force_sliders_full_range(false)
|
||||
, m_schedule_background_process(schedule_background_process_func)
|
||||
{
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
@ -258,7 +256,6 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundS
|
||||
_3DScene::add_canvas(m_canvas_widget);
|
||||
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
|
||||
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
|
||||
m_canvas->enable_shader(true);
|
||||
m_canvas->set_config(m_config);
|
||||
m_canvas->set_process(process);
|
||||
m_canvas->enable_legend_texture(true);
|
||||
@ -514,14 +511,14 @@ void Preview::show_hide_ui_elements(const std::string& what)
|
||||
void Preview::reset_sliders()
|
||||
{
|
||||
m_enabled = false;
|
||||
reset_double_slider();
|
||||
// reset_double_slider();
|
||||
m_double_slider_sizer->Hide((size_t)0);
|
||||
}
|
||||
|
||||
void Preview::update_sliders(const std::vector<double>& layers_z)
|
||||
{
|
||||
m_enabled = true;
|
||||
update_double_slider(layers_z, m_force_sliders_full_range);
|
||||
update_double_slider(layers_z);
|
||||
m_double_slider_sizer->Show((size_t)0);
|
||||
Layout();
|
||||
}
|
||||
@ -587,7 +584,8 @@ void Preview::create_double_slider()
|
||||
auto& config = wxGetApp().preset_bundle->project_config;
|
||||
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
|
||||
m_schedule_background_process();
|
||||
int type = m_choice_view_type->FindString(_(L("Color Print")));
|
||||
bool color_print = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty();
|
||||
int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) );
|
||||
if (m_choice_view_type->GetSelection() != type) {
|
||||
m_choice_view_type->SetSelection(type);
|
||||
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
|
||||
@ -597,26 +595,70 @@ void Preview::create_double_slider()
|
||||
});
|
||||
}
|
||||
|
||||
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
|
||||
// Returns -1 if there is no such member.
|
||||
static int find_close_layer_idx(const std::vector<double>& zs, double &z, double eps)
|
||||
{
|
||||
if (zs.empty())
|
||||
return -1;
|
||||
auto it_h = std::lower_bound(zs.begin(), zs.end(), z);
|
||||
if (it_h == zs.end()) {
|
||||
auto it_l = it_h;
|
||||
-- it_l;
|
||||
if (z - *it_l < eps)
|
||||
return int(zs.size() - 1);
|
||||
} else if (it_h == zs.begin()) {
|
||||
if (*it_h - z < eps)
|
||||
return 0;
|
||||
} else {
|
||||
auto it_l = it_h;
|
||||
-- it_l;
|
||||
double dist_l = z - *it_l;
|
||||
double dist_h = *it_h - z;
|
||||
if (std::min(dist_l, dist_h) < eps) {
|
||||
return (dist_l < dist_h) ? int(it_l - zs.begin()) : int(it_h - zs.begin());
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Preview::update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range)
|
||||
{
|
||||
// Save the initial slider span.
|
||||
double z_low = m_slider->GetLowerValueD();
|
||||
double z_high = m_slider->GetHigherValueD();
|
||||
bool was_empty = m_slider->GetMaxValue() == 0;
|
||||
bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_slider->GetMaxValueD()) > 1e-6;
|
||||
force_sliders_full_range |= was_empty | span_changed;
|
||||
bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
|
||||
bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max();
|
||||
|
||||
std::vector<std::pair<int, double>> values;
|
||||
fill_slider_values(values, layers_z);
|
||||
|
||||
m_slider->SetMaxValue(layers_z.size() - 1);
|
||||
if (force_sliders_full_range)
|
||||
m_slider->SetHigherValue(layers_z.size() - 1);
|
||||
|
||||
m_slider->SetSliderValues(values);
|
||||
const double z_low = m_slider->GetLowerValueD();
|
||||
const double z_high = m_slider->GetHigherValueD();
|
||||
assert(m_slider->GetMinValue() == 0);
|
||||
m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1);
|
||||
|
||||
int idx_low = 0;
|
||||
int idx_high = m_slider->GetMaxValue();
|
||||
if (! layers_z.empty()) {
|
||||
if (! snap_to_min) {
|
||||
int idx_new = find_close_layer_idx(layers_z, z_low, 1e-6);
|
||||
if (idx_new != -1)
|
||||
idx_low = idx_new;
|
||||
}
|
||||
if (! snap_to_max) {
|
||||
int idx_new = find_close_layer_idx(layers_z, z_high, 1e-6);
|
||||
if (idx_new != -1)
|
||||
idx_high = idx_new;
|
||||
}
|
||||
}
|
||||
m_slider->SetSelectionSpan(idx_low, idx_high);
|
||||
|
||||
const auto& config = wxGetApp().preset_bundle->project_config;
|
||||
const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
|
||||
|
||||
m_slider->SetTicksValues(ticks_from_config);
|
||||
|
||||
set_double_slider_thumbs(layers_z, z_low, z_high);
|
||||
|
||||
bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF);
|
||||
if (color_print_enable) {
|
||||
const auto& config = wxGetApp().preset_bundle->full_config();
|
||||
@ -638,8 +680,7 @@ void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values,
|
||||
// All ticks that would end up outside the slider range should be erased.
|
||||
// TODO: this should be placed into more appropriate part of code,
|
||||
// this function is e.g. not called when the last object is deleted
|
||||
auto& config = wxGetApp().preset_bundle->project_config;
|
||||
std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
|
||||
std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values;
|
||||
unsigned int old_size = ticks_from_config.size();
|
||||
ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(),
|
||||
[values](double val) { return values.back().second < val; }),
|
||||
@ -648,32 +689,6 @@ void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values,
|
||||
m_schedule_background_process();
|
||||
}
|
||||
|
||||
void Preview::set_double_slider_thumbs(const std::vector<double> &layers_z,
|
||||
const double z_low,
|
||||
const double z_high)
|
||||
{
|
||||
// Force slider full range only when slider is created.
|
||||
// Support selected diapason on the all next steps
|
||||
if (z_high == 0.0) {
|
||||
m_slider->SetLowerValue(0);
|
||||
m_slider->SetHigherValue(layers_z.size() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = layers_z.size() - 1; i >= 0; i--)
|
||||
// if (z_low >= layers_z[i]) {
|
||||
if (fabs(z_low - layers_z[i]) <= 1e-6) {
|
||||
m_slider->SetLowerValue(i);
|
||||
break;
|
||||
}
|
||||
for (int i = layers_z.size() - 1; i >= 0; i--)
|
||||
// if (z_high >= layers_z[i]) {
|
||||
if (fabs(z_high-layers_z[i]) <= 1e-6) {
|
||||
m_slider->SetHigherValue(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::reset_double_slider()
|
||||
{
|
||||
m_slider->SetHigherValue(0);
|
||||
@ -692,7 +707,7 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event)
|
||||
if (key == 'U' || key == 'D') {
|
||||
const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1;
|
||||
m_slider->SetHigherValue(new_pos);
|
||||
if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue());
|
||||
if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue());
|
||||
}
|
||||
else if (key == 'S')
|
||||
m_slider->ChangeOneLayerLock();
|
||||
@ -778,12 +793,8 @@ void Preview::load_print_as_fff()
|
||||
|
||||
if (IsShown())
|
||||
{
|
||||
// used to set the sliders to the extremes of the current zs range
|
||||
m_force_sliders_full_range = false;
|
||||
|
||||
if (gcode_preview_data_valid)
|
||||
{
|
||||
m_force_sliders_full_range = (m_canvas->get_volumes_count() == 0);
|
||||
m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
|
||||
show_hide_ui_elements("full");
|
||||
|
||||
@ -849,7 +860,6 @@ void Preview::load_print_as_sla()
|
||||
{
|
||||
std::vector<double> layer_zs;
|
||||
std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs));
|
||||
m_force_sliders_full_range = true;
|
||||
update_sliders(layer_zs);
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,6 @@ class Preview : public wxPanel
|
||||
|
||||
bool m_loaded;
|
||||
bool m_enabled;
|
||||
bool m_force_sliders_full_range;
|
||||
|
||||
PrusaDoubleSlider* m_slider {nullptr};
|
||||
|
||||
@ -177,12 +176,9 @@ private:
|
||||
|
||||
// Create/Update/Reset double slider on 3dPreview
|
||||
void create_double_slider();
|
||||
void update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range);
|
||||
void update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range = false);
|
||||
void fill_slider_values(std::vector<std::pair<int, double>> &values,
|
||||
const std::vector<double> &layers_z);
|
||||
void set_double_slider_thumbs( const std::vector<double> &layers_z,
|
||||
const double z_low,
|
||||
const double z_high);
|
||||
void reset_double_slider();
|
||||
// update DoubleSlider after keyDown in canvas
|
||||
void update_double_slider_from_canvas(wxKeyEvent& event);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include <wx/scrolwin.h>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@ -19,41 +20,46 @@ KBShortcutsDialog::KBShortcutsDialog()
|
||||
|
||||
// fonts
|
||||
wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
|
||||
head_font.SetPointSize(19);
|
||||
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
font.SetPointSize(10);
|
||||
wxFont bold_font = font.Bold();
|
||||
#ifdef __WXOSX__
|
||||
font.SetPointSize(12);
|
||||
bold_font.SetPointSize(14);
|
||||
#endif /*__WXOSX__*/
|
||||
head_font.SetPointSize(14);
|
||||
#else
|
||||
head_font.SetPointSize(12);
|
||||
#endif // __WXOSX__
|
||||
|
||||
const wxFont& font = wxGetApp().small_font();
|
||||
const wxFont& bold_font = wxGetApp().bold_font();
|
||||
|
||||
fill_shortcuts();
|
||||
|
||||
auto panel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(500, 600));
|
||||
panel->SetScrollbars(0, 20, 1, 2);
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(sizer);
|
||||
auto panel = new wxPanel(this);
|
||||
auto main_grid_sizer = new wxFlexGridSizer(2, 10, 10);
|
||||
panel->SetSizer(main_grid_sizer);
|
||||
main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
wxBoxSizer* l_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_grid_sizer->Add(l_sizer, 0);
|
||||
|
||||
wxBoxSizer* r_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_grid_sizer->Add(r_sizer, 0);
|
||||
|
||||
for (auto& sc : m_full_shortcuts)
|
||||
{
|
||||
auto sizer = sc.first == _(L("Main Shortcuts")) ? l_sizer : r_sizer;
|
||||
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(hsizer, 0, wxEXPAND | wxTOP, 25);
|
||||
sizer->Add(hsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
|
||||
// logo
|
||||
auto *logo = new wxStaticBitmap(panel, wxID_ANY, logo_bmp);
|
||||
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
|
||||
|
||||
// head
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(400,-1));
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(200,-1));
|
||||
head->SetFont(head_font);
|
||||
hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
// Shortcuts list
|
||||
auto grid_sizer = new wxFlexGridSizer(2, 10, 25);
|
||||
sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 10);
|
||||
auto grid_sizer = new wxFlexGridSizer(2, 5, 15);
|
||||
sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT| wxRIGHT, 15);
|
||||
|
||||
for (auto pair : sc.second)
|
||||
{
|
||||
@ -69,9 +75,9 @@ KBShortcutsDialog::KBShortcutsDialog()
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK);
|
||||
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->SetEscapeId(wxID_OK);
|
||||
this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK);
|
||||
main_sizer->Add(buttons, 0, wxEXPAND | wxALL, 15);
|
||||
main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 15);
|
||||
|
||||
this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this);
|
||||
|
||||
@ -81,32 +87,40 @@ KBShortcutsDialog::KBShortcutsDialog()
|
||||
|
||||
void KBShortcutsDialog::fill_shortcuts()
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
const std::string ctrl = "Cmd+"; // #ys_FIXME_cmd_smb // Change it for the accorded symbol
|
||||
const std::string alt = "Alt+"; // #ys_FIXME_cmd_smb // Change it for the accorded symbol
|
||||
#else
|
||||
const std::string ctrl = "Ctrl+";
|
||||
const std::string alt = "Alt+";
|
||||
#endif // __WXOSX__
|
||||
|
||||
Shortcuts main_shortcuts;
|
||||
main_shortcuts.reserve(25);
|
||||
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+O", L("Open project STL/OBJ/AMF/3MF with config, delete bed")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+I", L("Import STL//OBJ/AMF/3MF without config, keep bed")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+L", L("Load Config from .ini/amf/3mf/gcode")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+Alt+L", L("Load Config from .ini/amf/3mf/gcode and merge")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+G", L("Export Gcode")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+S", L("Save project (3MF)")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+R", L("(Re)slice")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+U", L("Quick slice")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+Alt+U", L("Quick slice and Save as")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+Shift+U", L("Repeat last quick slice")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+1", L("Select Plater Tab")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+2", L("Select Print Settings Tab")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+3", L("Select Filament Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+4", L("Select Printer Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+5", L("Switch to 3D")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+6", L("Switch to Preview")));
|
||||
main_shortcuts.push_back(Shortcut("Ctrl+P", L("Preferences")));
|
||||
main_shortcuts.push_back(Shortcut("0-6", L("Camera view ")));
|
||||
main_shortcuts.push_back(Shortcut("+", L("Add Instance to selected object ")));
|
||||
main_shortcuts.push_back(Shortcut("-", L("Remove Instance from selected object")));
|
||||
main_shortcuts.push_back(Shortcut("?", L("Show keyboard shortcuts list")));
|
||||
main_shortcuts.push_back(Shortcut("PgUp/PgDn", L("Switch between 3D and Preview")));
|
||||
main_shortcuts.push_back(Shortcut("Shift+LeftMouse",L("Select multiple object/Move multiple object")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL//OBJ/AMF/3MF without config, keep bed")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export Gcode")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+alt+"L" ,L("Load Config from .ini/amf/3mf/gcode and merge")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"R" ,L("(Re)slice")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"U" ,L("Quick slice")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"Shift+U" ,L("Repeat last quick slice")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"1" ,L("Select Plater Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+alt+"U" ,L("Quick slice and Save as")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"2" ,L("Select Print Settings Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"5" ,L("Switch to 3D")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"6" ,L("Switch to Preview")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"P" ,L("Preferences")));
|
||||
main_shortcuts.push_back(Shortcut("0-6" ,L("Camera view ")));
|
||||
main_shortcuts.push_back(Shortcut("+" ,L("Add Instance to selected object ")));
|
||||
main_shortcuts.push_back(Shortcut("-" ,L("Remove Instance from selected object")));
|
||||
main_shortcuts.push_back(Shortcut("?" ,L("Show keyboard shortcuts list")));
|
||||
main_shortcuts.push_back(Shortcut("PgUp/PgDn" ,L("Switch between 3D and Preview")));
|
||||
main_shortcuts.push_back(Shortcut("Shift+LeftMouse" ,L("Select multiple object/Move multiple object")));
|
||||
|
||||
m_full_shortcuts.emplace(_(L("Main Shortcuts")), main_shortcuts);
|
||||
|
||||
@ -115,9 +129,9 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||
plater_shortcuts.reserve(20);
|
||||
|
||||
plater_shortcuts.push_back(Shortcut("A", L("Arrange")));
|
||||
plater_shortcuts.push_back(Shortcut("Ctrl+A", L("Select All objects")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects")));
|
||||
plater_shortcuts.push_back(Shortcut("Del", L("Delete selected")));
|
||||
plater_shortcuts.push_back(Shortcut("Ctrl+Del", L("Delete all")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete all")));
|
||||
plater_shortcuts.push_back(Shortcut("M", L("Gizmo move")));
|
||||
plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale")));
|
||||
plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate")));
|
||||
|
@ -28,10 +28,8 @@
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
||||
MainFrame::MainFrame() :
|
||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||
m_no_plater(no_plater),
|
||||
m_loaded(loaded),
|
||||
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||
{
|
||||
// Load the icon either from the exe, or from the ico file.
|
||||
@ -125,11 +123,9 @@ void MainFrame::init_tabpanel()
|
||||
}
|
||||
});
|
||||
|
||||
if (!m_no_plater) {
|
||||
m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
|
||||
wxGetApp().plater_ = m_plater;
|
||||
m_tabpanel->AddPage(m_plater, _(L("Plater")));
|
||||
}
|
||||
m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
|
||||
wxGetApp().plater_ = m_plater;
|
||||
m_tabpanel->AddPage(m_plater, _(L("Plater")));
|
||||
|
||||
// The following event is emited by Tab implementation on config value change.
|
||||
Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this);
|
||||
@ -380,7 +376,7 @@ void MainFrame::init_menubar()
|
||||
|
||||
windowMenu->AppendSeparator();
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"),
|
||||
[this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png");
|
||||
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png");
|
||||
}
|
||||
|
||||
// View menu
|
||||
|
@ -42,10 +42,7 @@ struct PresetTab {
|
||||
|
||||
class MainFrame : public wxFrame
|
||||
{
|
||||
bool m_no_plater;
|
||||
bool m_loaded;
|
||||
int m_lang_ch_event;
|
||||
int m_preferences_event;
|
||||
bool m_loaded {false};
|
||||
|
||||
wxString m_qs_last_input_file = wxEmptyString;
|
||||
wxString m_qs_last_output_file = wxEmptyString;
|
||||
@ -71,8 +68,7 @@ class MainFrame : public wxFrame
|
||||
bool can_delete_all() const;
|
||||
|
||||
public:
|
||||
MainFrame() {}
|
||||
MainFrame(const bool no_plater, const bool loaded);
|
||||
MainFrame();
|
||||
~MainFrame() {}
|
||||
|
||||
Plater* plater() { return m_plater; }
|
||||
|
@ -1117,6 +1117,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
view3D = new View3D(q, &model, config, &background_process);
|
||||
preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
|
||||
// Let the Tab key switch between the 3D view and the layer preview.
|
||||
view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->q->select_view_3D("Preview"); });
|
||||
preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->q->select_view_3D("3D"); });
|
||||
|
||||
panels.push_back(view3D);
|
||||
panels.push_back(preview);
|
||||
@ -1132,7 +1135,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
this->canvas3D->set_config(config);
|
||||
this->canvas3D->enable_gizmos(true);
|
||||
this->canvas3D->enable_toolbar(true);
|
||||
this->canvas3D->enable_shader(true);
|
||||
this->canvas3D->enable_force_zoom_to_bed(true);
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
@ -1162,9 +1164,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);
|
||||
q->SetSizer(hsizer);
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
set_current_panel(view3D);
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
//#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
// set_current_panel(view3D);
|
||||
//#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
init_object_menu();
|
||||
|
||||
@ -1251,6 +1253,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
|
||||
update_ui_from_settings();
|
||||
q->Layout();
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
set_current_panel(view3D);
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
}
|
||||
|
||||
void Plater::priv::update(bool force_full_scene_refresh)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <wx/debug.h>
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
@ -59,7 +60,8 @@ bool PrintHostSendDialog::start_print() const
|
||||
|
||||
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
|
||||
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||
: wxEvent(winid, eventType)
|
||||
@ -87,6 +89,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||
, on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this)
|
||||
{
|
||||
enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 };
|
||||
|
||||
@ -95,22 +98,47 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||
// Note: Keep these in sync with Column
|
||||
job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn("error_message", wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
|
||||
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||
btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||
btn_cancel->Disable();
|
||||
btn_error = new wxButton(this, wxID_ANY, _(L("Show error message")));
|
||||
btn_error->Disable();
|
||||
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
|
||||
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||
btnsizer->Add(btn_error, 0);
|
||||
btnsizer->AddStretchSpacer();
|
||||
btnsizer->Add(btn_close);
|
||||
|
||||
topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||
topsizer->Add(btnsizer, 0, wxEXPAND);
|
||||
SetSizer(topsizer);
|
||||
|
||||
job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); });
|
||||
|
||||
btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
int selected = job_list->GetSelectedRow();
|
||||
if (selected == wxNOT_FOUND) { return; }
|
||||
|
||||
const JobState state = get_state(selected);
|
||||
if (state < ST_ERROR) {
|
||||
// TODO: cancel
|
||||
GUI::wxGetApp().printhost_job_queue().cancel(selected);
|
||||
}
|
||||
});
|
||||
|
||||
btn_error->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
int selected = job_list->GetSelectedRow();
|
||||
if (selected == wxNOT_FOUND) { return; }
|
||||
GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG));
|
||||
});
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||
@ -123,24 +151,82 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||
fields.push_back(wxVariant(_(L("Enqueued"))));
|
||||
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||
job_list->AppendItem(fields);
|
||||
fields.push_back(wxVariant(""));
|
||||
job_list->AppendItem(fields, static_cast<wxUIntPtr>(ST_NEW));
|
||||
}
|
||||
|
||||
PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx)
|
||||
{
|
||||
wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list");
|
||||
return static_cast<JobState>(job_list->GetItemData(job_list->RowToItem(idx)));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::set_state(int idx, JobState state)
|
||||
{
|
||||
wxCHECK_RET(idx >= 0 && idx < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
job_list->SetItemData(job_list->RowToItem(idx), static_cast<wxUIntPtr>(state));
|
||||
|
||||
switch (state) {
|
||||
case ST_NEW: job_list->SetValue(_(L("Enqueued")), idx, COL_STATUS); break;
|
||||
case ST_PROGRESS: job_list->SetValue(_(L("Uploading")), idx, COL_STATUS); break;
|
||||
case ST_ERROR: job_list->SetValue(_(L("Error")), idx, COL_STATUS); break;
|
||||
case ST_CANCELLING: job_list->SetValue(_(L("Cancelling")), idx, COL_STATUS); break;
|
||||
case ST_CANCELLED: job_list->SetValue(_(L("Cancelled")), idx, COL_STATUS); break;
|
||||
case ST_COMPLETED: job_list->SetValue(_(L("Completed")), idx, COL_STATUS); break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_list_select()
|
||||
{
|
||||
int selected = job_list->GetSelectedRow();
|
||||
if (selected != wxNOT_FOUND) {
|
||||
const JobState state = get_state(selected);
|
||||
btn_cancel->Enable(state < ST_ERROR);
|
||||
btn_error->Enable(state == ST_ERROR);
|
||||
Layout();
|
||||
} else {
|
||||
btn_cancel->Disable();
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_progress(Event &evt)
|
||||
{
|
||||
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete")));
|
||||
if (evt.progress < 100) {
|
||||
set_state(evt.job_id, ST_PROGRESS);
|
||||
job_list->SetValue(wxVariant(evt.progress), evt.job_id, COL_PROGRESS);
|
||||
} else {
|
||||
set_state(evt.job_id, ST_COMPLETED);
|
||||
job_list->SetValue(wxVariant(100), evt.job_id, COL_PROGRESS);
|
||||
}
|
||||
|
||||
job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1);
|
||||
job_list->SetValue(status, evt.job_id, 2);
|
||||
on_list_select();
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_error(Event &evt)
|
||||
{
|
||||
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
// TODO
|
||||
set_state(evt.job_id, ST_ERROR);
|
||||
|
||||
auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error);
|
||||
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
||||
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later
|
||||
|
||||
on_list_select();
|
||||
|
||||
GUI::show_error(nullptr, std::move(errormsg));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_cancel(Event &evt)
|
||||
{
|
||||
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||
|
||||
set_state(evt.job_id, ST_CANCELLED);
|
||||
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
||||
|
||||
on_list_select();
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,10 +13,12 @@
|
||||
#include "MsgDialog.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
||||
class wxButton;
|
||||
class wxTextCtrl;
|
||||
class wxCheckBox;
|
||||
class wxDataViewListCtrl;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct PrintHostJob;
|
||||
@ -60,17 +62,43 @@ public:
|
||||
|
||||
void append_job(const PrintHostJob &job);
|
||||
private:
|
||||
enum Column {
|
||||
COL_ID,
|
||||
COL_PROGRESS,
|
||||
COL_STATUS,
|
||||
COL_HOST,
|
||||
COL_FILENAME,
|
||||
COL_ERRORMSG,
|
||||
};
|
||||
|
||||
enum JobState {
|
||||
ST_NEW,
|
||||
ST_PROGRESS,
|
||||
ST_ERROR,
|
||||
ST_CANCELLING,
|
||||
ST_CANCELLED,
|
||||
ST_COMPLETED,
|
||||
};
|
||||
|
||||
wxButton *btn_cancel;
|
||||
wxButton *btn_error;
|
||||
wxDataViewListCtrl *job_list;
|
||||
// Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog
|
||||
EventGuard on_progress_evt;
|
||||
EventGuard on_error_evt;
|
||||
EventGuard on_cancel_evt;
|
||||
|
||||
JobState get_state(int idx);
|
||||
void set_state(int idx, JobState);
|
||||
void on_list_select();
|
||||
void on_progress(Event&);
|
||||
void on_error(Event&);
|
||||
void on_cancel(Event&);
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event);
|
||||
|
||||
|
||||
}}
|
||||
|
@ -116,7 +116,7 @@ SysInfoDialog::SysInfoDialog()
|
||||
buttons->Insert(0, btn_copy_to_clipboard, 0, wxLEFT, 5);
|
||||
btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this);
|
||||
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->SetEscapeId(wxID_OK);
|
||||
this->Bind(wxEVT_BUTTON, &SysInfoDialog::onCloseDialog, this, wxID_OK);
|
||||
main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
|
@ -1601,8 +1601,9 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
||||
optgroup->append_line(host_line);
|
||||
optgroup->append_single_option_line("printhost_apikey");
|
||||
|
||||
if (Http::ca_file_supported()) {
|
||||
const auto ca_file_hint = _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."));
|
||||
|
||||
if (Http::ca_file_supported()) {
|
||||
Line cafile_line = optgroup->create_single_option_line("printhost_cafile");
|
||||
|
||||
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
|
||||
@ -1625,19 +1626,31 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
||||
cafile_line.append_widget(printhost_cafile_browse);
|
||||
optgroup->append_line(cafile_line);
|
||||
|
||||
auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) {
|
||||
auto txt = new wxStaticText(parent, wxID_ANY,
|
||||
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
|
||||
Line cafile_hint { "", "" };
|
||||
cafile_hint.full_width = 1;
|
||||
cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) {
|
||||
auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint);
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(txt);
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(cafile_hint);
|
||||
} else {
|
||||
Line line { "", "" };
|
||||
line.full_width = 1;
|
||||
|
||||
line.widget = [this, ca_file_hint] (wxWindow* parent) {
|
||||
auto txt = new wxStaticText(parent, wxID_ANY, wxString::Format("%s\n\n\t%s",
|
||||
_(L("HTTPS CA File:\n\
|
||||
\tOn this system, Slic3r uses HTTPS certificates from the system Certificate Store or Keychain.\n\
|
||||
\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain.")),
|
||||
ca_file_hint));
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(txt);
|
||||
return sizer;
|
||||
};
|
||||
|
||||
Line cafile_hint { "", "" };
|
||||
cafile_hint.full_width = 1;
|
||||
cafile_hint.widget = std::move(printhost_cafile_hint);
|
||||
optgroup->append_line(cafile_hint);
|
||||
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1506,6 +1506,21 @@ void PrusaDoubleSlider::SetHigherValue(const int higher_val)
|
||||
ProcessWindowEvent(e);
|
||||
}
|
||||
|
||||
void PrusaDoubleSlider::SetSelectionSpan(const int lower_val, const int higher_val)
|
||||
{
|
||||
m_lower_value = std::max(lower_val, m_min_value);
|
||||
m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value);
|
||||
if (m_lower_value < m_higher_value)
|
||||
m_is_one_layer = false;
|
||||
|
||||
Refresh();
|
||||
Update();
|
||||
|
||||
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||
e.SetEventObject(this);
|
||||
ProcessWindowEvent(e);
|
||||
}
|
||||
|
||||
void PrusaDoubleSlider::SetMaxValue(const int max_value)
|
||||
{
|
||||
m_max_value = max_value;
|
||||
@ -2043,7 +2058,7 @@ void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter)
|
||||
// - value decrease (if wxSL_HORIZONTAL)
|
||||
void PrusaDoubleSlider::move_current_thumb(const bool condition)
|
||||
{
|
||||
m_is_one_layer = wxGetKeyState(WXK_CONTROL);
|
||||
// m_is_one_layer = wxGetKeyState(WXK_CONTROL);
|
||||
int delta = condition ? -1 : 1;
|
||||
if (is_horizontal())
|
||||
delta *= -1;
|
||||
|
@ -697,18 +697,20 @@ public:
|
||||
const wxString& name = wxEmptyString);
|
||||
~PrusaDoubleSlider() {}
|
||||
|
||||
int GetLowerValue() const {
|
||||
return m_lower_value;
|
||||
}
|
||||
int GetHigherValue() const {
|
||||
return m_higher_value;
|
||||
}
|
||||
int GetMinValue() const { return m_min_value; }
|
||||
int GetMaxValue() const { return m_max_value; }
|
||||
double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value].second; }
|
||||
double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value].second; }
|
||||
int GetLowerValue() const { return m_lower_value; }
|
||||
int GetHigherValue() const { return m_higher_value; }
|
||||
int GetActiveValue() const;
|
||||
double GetLowerValueD() { return get_double_value(ssLower); }
|
||||
double GetHigherValueD() { return get_double_value(ssHigher); }
|
||||
wxSize DoGetBestSize() const override;
|
||||
void SetLowerValue(const int lower_val);
|
||||
void SetHigherValue(const int higher_val);
|
||||
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
|
||||
void SetSelectionSpan(const int lower_val, const int higher_val);
|
||||
void SetMaxValue(const int max_value);
|
||||
void SetKoefForLabels(const double koef) {
|
||||
m_label_koef = koef;
|
||||
@ -726,6 +728,12 @@ public:
|
||||
EnableTickManipulation(false);
|
||||
}
|
||||
|
||||
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
|
||||
bool is_one_layer() const { return m_is_one_layer; }
|
||||
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
|
||||
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
|
||||
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
|
||||
|
||||
void OnPaint(wxPaintEvent& ) { render();}
|
||||
void OnLeftDown(wxMouseEvent& event);
|
||||
void OnMotion(wxMouseEvent& event);
|
||||
@ -762,7 +770,6 @@ protected:
|
||||
|
||||
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
|
||||
int is_point_near_tick(const wxPoint& pt);
|
||||
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
|
||||
|
||||
double get_scroll_step();
|
||||
wxString get_label(const SelectedSlider& selection) const;
|
||||
|
@ -46,7 +46,7 @@ bool Duet::test(wxString &msg) const
|
||||
|
||||
wxString Duet::get_test_ok_msg () const
|
||||
{
|
||||
return wxString::Format("%s", _(L("Connection to Duet works correctly.")));
|
||||
return _(L("Connection to Duet works correctly."));
|
||||
}
|
||||
|
||||
wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||
@ -54,91 +54,46 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
||||
}
|
||||
|
||||
// bool Duet::send_gcode(const std::string &filename) const
|
||||
// {
|
||||
// enum { PROGRESS_RANGE = 1000 };
|
||||
|
||||
// const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||
// fs::path filepath(filename);
|
||||
|
||||
// GUI::PrintHostSendDialog send_dialog(filepath.filename());
|
||||
// if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||
|
||||
// const bool print = send_dialog.start_print();
|
||||
// const auto upload_filepath = send_dialog.filename();
|
||||
// const auto upload_filename = upload_filepath.filename();
|
||||
// const auto upload_parent_path = upload_filepath.parent_path();
|
||||
|
||||
// wxProgressDialog progress_dialog(
|
||||
// _(L("Duet upload")),
|
||||
// _(L("Sending G-code file to Duet...")),
|
||||
// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||
// progress_dialog.Pulse();
|
||||
|
||||
// wxString connect_msg;
|
||||
// if (!connect(connect_msg)) {
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// bool res = true;
|
||||
|
||||
// auto upload_cmd = get_upload_url(upload_filepath.string());
|
||||
// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
||||
// % filepath.string()
|
||||
// % upload_filename.string()
|
||||
// % upload_parent_path.string()
|
||||
// % print
|
||||
// % upload_cmd;
|
||||
|
||||
// auto http = Http::post(std::move(upload_cmd));
|
||||
// http.set_post_body(filename)
|
||||
// .on_complete([&](std::string body, unsigned status) {
|
||||
// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||
// progress_dialog.Update(PROGRESS_RANGE);
|
||||
|
||||
// int err_code = get_err_code_from_body(body);
|
||||
// if (err_code != 0) {
|
||||
// auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||
// GUI::show_error(&progress_dialog, std::move(msg));
|
||||
// res = false;
|
||||
// } else if (print) {
|
||||
// wxString errormsg;
|
||||
// res = start_print(errormsg, upload_filepath.string());
|
||||
// if (!res) {
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .on_error([&](std::string body, std::string error, unsigned status) {
|
||||
// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// res = false;
|
||||
// })
|
||||
// .on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
// if (cancel) {
|
||||
// // Upload was canceled
|
||||
// res = false;
|
||||
// } else if (progress.ultotal > 0) {
|
||||
// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
||||
// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
||||
// } else {
|
||||
// cancel = !progress_dialog.Pulse();
|
||||
// }
|
||||
// })
|
||||
// .perform_sync();
|
||||
|
||||
// disconnect();
|
||||
|
||||
// return res;
|
||||
// }
|
||||
|
||||
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
{
|
||||
// XXX: TODO
|
||||
throw "unimplemented";
|
||||
wxString connect_msg;
|
||||
if (!connect(connect_msg)) {
|
||||
error_fn(std::move(connect_msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
auto upload_cmd = get_upload_url(upload_data.upload_path.string());
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, print: %3%, command: %4%")
|
||||
% upload_data.source_path
|
||||
% upload_data.upload_path
|
||||
% upload_data.start_print
|
||||
% upload_cmd;
|
||||
|
||||
auto http = Http::post(std::move(upload_cmd));
|
||||
http.set_post_body(upload_data.source_path)
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % 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) << "Duet: Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
disconnect();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Duet::has_auto_discovery() const
|
||||
@ -241,16 +196,6 @@ std::string Duet::timestamp_str() const
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status)
|
||||
{
|
||||
if (status != 0) {
|
||||
auto wxbody = wxString::FromUTF8(body.data());
|
||||
return wxString::Format("HTTP %u: %s", status, wxbody);
|
||||
} else {
|
||||
return wxString::FromUTF8(error.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool Duet::start_print(wxString &msg, const std::string &filename) const
|
||||
{
|
||||
bool res = false;
|
||||
|
@ -22,10 +22,11 @@ public:
|
||||
virtual bool test(wxString &curl_msg) const;
|
||||
virtual wxString get_test_ok_msg () const;
|
||||
virtual wxString get_test_failed_msg (wxString &msg) const;
|
||||
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
|
||||
virtual bool has_auto_discovery() const;
|
||||
virtual bool can_test() const;
|
||||
virtual std::string get_host() const { return host; }
|
||||
|
||||
private:
|
||||
std::string host;
|
||||
std::string password;
|
||||
@ -38,7 +39,6 @@ private:
|
||||
void disconnect() const;
|
||||
bool start_print(wxString &msg, const std::string &filename) const;
|
||||
int get_err_code_from_body(const std::string &body) const;
|
||||
static wxString format_error(const std::string &body, const std::string &error, unsigned status);
|
||||
};
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ class CurlGlobalInit
|
||||
struct Http::priv
|
||||
{
|
||||
enum {
|
||||
DEFAULT_TIMEOUT = 10,
|
||||
DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
|
||||
};
|
||||
|
||||
@ -63,6 +64,7 @@ struct Http::priv
|
||||
static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
|
||||
|
||||
void set_timeout(long timeout);
|
||||
void form_add_file(const char *name, const fs::path &path, const char* filename);
|
||||
void set_post_body(const fs::path &path);
|
||||
|
||||
@ -84,6 +86,7 @@ Http::priv::priv(const std::string &url)
|
||||
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||
}
|
||||
|
||||
set_timeout(DEFAULT_TIMEOUT);
|
||||
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
|
||||
@ -146,7 +149,9 @@ int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_o
|
||||
self->progressfn(progress, cb_cancel);
|
||||
}
|
||||
|
||||
return self->cancel || cb_cancel;
|
||||
if (cb_cancel) { self->cancel = true; }
|
||||
|
||||
return self->cancel;
|
||||
}
|
||||
|
||||
int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
@ -167,6 +172,12 @@ size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, v
|
||||
return stream->gcount();
|
||||
}
|
||||
|
||||
void Http::priv::set_timeout(long timeout)
|
||||
{
|
||||
::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
|
||||
}
|
||||
|
||||
void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename)
|
||||
{
|
||||
// We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
|
||||
@ -203,10 +214,10 @@ void Http::priv::set_post_body(const fs::path &path)
|
||||
|
||||
std::string Http::priv::curl_error(CURLcode curlcode)
|
||||
{
|
||||
return (boost::format("%1% (%2%): %3%")
|
||||
return (boost::format("%1%:\n%2%\n[Error %3%]")
|
||||
% ::curl_easy_strerror(curlcode)
|
||||
% error_buffer.c_str()
|
||||
% curlcode
|
||||
% error_buffer
|
||||
).str();
|
||||
}
|
||||
|
||||
@ -226,7 +237,9 @@ void Http::priv::http_perform()
|
||||
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
|
||||
::curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xfercb);
|
||||
::curl_easy_setopt(curl, CURLOPT_XFERINFODATA, static_cast<void*>(this));
|
||||
#ifndef _WIN32
|
||||
(void)xfercb_legacy; // prevent unused function warning
|
||||
#endif
|
||||
#else
|
||||
::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb);
|
||||
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
||||
@ -293,6 +306,13 @@ Http::~Http()
|
||||
}
|
||||
|
||||
|
||||
Http& Http::timeout(long timeout)
|
||||
{
|
||||
if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; }
|
||||
if (p) { p->set_timeout(timeout); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::size_limit(size_t sizeLimit)
|
||||
{
|
||||
if (p) { p->limit = sizeLimit; }
|
||||
|
@ -55,6 +55,8 @@ public:
|
||||
Http& operator=(const Http &) = delete;
|
||||
Http& operator=(Http &&) = delete;
|
||||
|
||||
// Sets a maximum connection timeout in seconds
|
||||
Http& timeout(long timeout);
|
||||
// Sets a maximum size of the data that can be received.
|
||||
// A value of zero sets the default limit, which is is 5MB.
|
||||
Http& size_limit(size_t sizeLimit);
|
||||
|
@ -62,7 +62,7 @@ bool OctoPrint::test(wxString &msg) const
|
||||
const auto text = ptree.get_optional<std::string>("text");
|
||||
res = validate_version_text(text);
|
||||
if (! res) {
|
||||
msg = wxString::Format("Mismatched type of print host: %s", text ? *text : "OctoPrint");
|
||||
msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint");
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
@ -77,28 +77,24 @@ bool OctoPrint::test(wxString &msg) const
|
||||
|
||||
wxString OctoPrint::get_test_ok_msg () const
|
||||
{
|
||||
return wxString::Format("%s", _(L("Connection to OctoPrint works correctly.")));
|
||||
return _(L("Connection to OctoPrint works correctly."));
|
||||
}
|
||||
|
||||
wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||
{
|
||||
return wxString::Format("%s: %s\n\n%s",
|
||||
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
||||
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
||||
}
|
||||
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
{
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
|
||||
wxString test_msg;
|
||||
if (! test(test_msg)) {
|
||||
|
||||
// FIXME:
|
||||
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// return false;
|
||||
error_fn(std::move(test_msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
@ -106,7 +102,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn
|
||||
auto url = make_url("api/files/local");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
||||
% upload_data.source_path.string()
|
||||
% upload_data.source_path
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
@ -122,14 +118,14 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||
error_fn(std::move(body), std::move(error), status);
|
||||
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(error) << "Octoprint: Upload canceled";
|
||||
BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
@ -175,16 +171,6 @@ std::string OctoPrint::make_url(const std::string &path) const
|
||||
}
|
||||
}
|
||||
|
||||
wxString OctoPrint::format_error(const std::string &body, const std::string &error, unsigned status)
|
||||
{
|
||||
if (status != 0) {
|
||||
auto wxbody = wxString::FromUTF8(body.data());
|
||||
return wxString::Format("HTTP %u: %s", status, wxbody);
|
||||
} else {
|
||||
return wxString::FromUTF8(error.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SLAHost
|
||||
|
||||
@ -192,7 +178,7 @@ SLAHost::~SLAHost() {}
|
||||
|
||||
wxString SLAHost::get_test_ok_msg () const
|
||||
{
|
||||
return wxString::Format("%s", _(L("Connection to Prusa SLA works correctly.")));
|
||||
return _(L("Connection to Prusa SLA works correctly."));
|
||||
}
|
||||
|
||||
wxString SLAHost::get_test_failed_msg (wxString &msg) const
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
virtual bool test(wxString &curl_msg) const;
|
||||
virtual wxString get_test_ok_msg () const;
|
||||
virtual wxString get_test_failed_msg (wxString &msg) const;
|
||||
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
|
||||
virtual bool has_auto_discovery() const;
|
||||
virtual bool can_test() const;
|
||||
virtual std::string get_host() const { return host; }
|
||||
@ -38,7 +38,6 @@ private:
|
||||
|
||||
void set_auth(Http &http) const;
|
||||
std::string make_url(const std::string &path) const;
|
||||
static wxString format_error(const std::string &body, const std::string &error, unsigned status);
|
||||
};
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
@ -112,7 +113,7 @@ struct PresetUpdater::priv
|
||||
bool get_file(const std::string &url, const fs::path &target_path) const;
|
||||
void prune_tmps() const;
|
||||
void sync_version() const;
|
||||
void sync_config(const std::set<VendorProfile> vendors) const;
|
||||
void sync_config(const std::set<VendorProfile> vendors);
|
||||
|
||||
void check_install_indices() const;
|
||||
Updates get_config_updates() const;
|
||||
@ -130,7 +131,7 @@ PresetUpdater::priv::priv() :
|
||||
{
|
||||
set_download_prefs(GUI::wxGetApp().app_config);
|
||||
check_install_indices();
|
||||
index_db = std::move(Index::load_db());
|
||||
index_db = Index::load_db();
|
||||
}
|
||||
|
||||
// Pull relevant preferences from AppConfig
|
||||
@ -220,14 +221,14 @@ void PresetUpdater::priv::sync_version() const
|
||||
|
||||
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
|
||||
// Both are saved in cache.
|
||||
void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) const
|
||||
void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
|
||||
|
||||
if (!enabled_config_update) { return; }
|
||||
|
||||
// Donwload vendor preset bundles
|
||||
for (const auto &index : index_db) {
|
||||
for (auto &index : index_db) {
|
||||
if (cancel) { return; }
|
||||
|
||||
const auto vendor_it = vendors.find(VendorProfile(index.vendor()));
|
||||
@ -245,17 +246,33 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) con
|
||||
// Download a fresh index
|
||||
BOOST_LOG_TRIVIAL(info) << "Downloading index for vendor: " << vendor.name;
|
||||
const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME;
|
||||
const auto idx_path = cache_path / (vendor.id + ".idx");
|
||||
if (! get_file(idx_url, idx_path)) { continue; }
|
||||
const std::string idx_path = (cache_path / (vendor.id + ".idx")).string();
|
||||
const std::string idx_path_temp = idx_path + "-update";
|
||||
if (!get_file(idx_url, idx_path_temp)) { continue; }
|
||||
if (cancel) { return; }
|
||||
|
||||
// Load the fresh index up
|
||||
Index new_index;
|
||||
new_index.load(idx_path);
|
||||
{
|
||||
Index new_index;
|
||||
try {
|
||||
new_index.load(idx_path_temp);
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading a downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
|
||||
continue;
|
||||
}
|
||||
if (new_index.version() < index.version()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.") % idx_path_temp % vendor.name;
|
||||
continue;
|
||||
}
|
||||
Slic3r::rename_file(idx_path_temp, idx_path);
|
||||
index = std::move(new_index);
|
||||
if (cancel)
|
||||
return;
|
||||
}
|
||||
|
||||
// See if a there's a new version to download
|
||||
const auto recommended_it = new_index.recommended();
|
||||
if (recommended_it == new_index.end()) {
|
||||
const auto recommended_it = index.recommended();
|
||||
if (recommended_it == index.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name;
|
||||
continue;
|
||||
}
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <exception>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/app.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
@ -36,6 +38,16 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||
}
|
||||
}
|
||||
|
||||
wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const
|
||||
{
|
||||
if (status != 0) {
|
||||
auto wxbody = wxString::FromUTF8(body.data());
|
||||
return wxString::Format("HTTP %u: %s", status, wxbody);
|
||||
} else {
|
||||
return wxString::FromUTF8(error.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct PrintHostJobQueue::priv
|
||||
{
|
||||
@ -47,6 +59,7 @@ struct PrintHostJobQueue::priv
|
||||
Channel<size_t> channel_cancels;
|
||||
size_t job_id = 0;
|
||||
int prev_progress = -1;
|
||||
fs::path source_to_remove;
|
||||
|
||||
std::thread bg_thread;
|
||||
bool bg_exit = false;
|
||||
@ -55,10 +68,14 @@ struct PrintHostJobQueue::priv
|
||||
|
||||
priv(PrintHostJobQueue *q) : q(q) {}
|
||||
|
||||
void emit_progress(int progress);
|
||||
void emit_error(wxString error);
|
||||
void emit_cancel(size_t id);
|
||||
void start_bg_thread();
|
||||
void bg_thread_main();
|
||||
void progress_fn(Http::Progress progress, bool &cancel);
|
||||
void error_fn(std::string body, std::string error, unsigned http_status);
|
||||
void remove_source(const fs::path &path);
|
||||
void remove_source();
|
||||
void perform_job(PrintHostJob the_job);
|
||||
};
|
||||
|
||||
@ -77,6 +94,24 @@ PrintHostJobQueue::~PrintHostJobQueue()
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_progress(int progress)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, progress);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_error(wxString error)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error));
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::emit_cancel(size_t id)
|
||||
{
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, queue_dialog->GetId(), id);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::start_bg_thread()
|
||||
{
|
||||
if (bg_thread.joinable()) { return; }
|
||||
@ -95,18 +130,43 @@ void PrintHostJobQueue::priv::bg_thread_main()
|
||||
// Pick up jobs from the job channel:
|
||||
while (! bg_exit) {
|
||||
auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs
|
||||
source_to_remove = job.upload_data.source_path;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Received job: [%1%]: `%2%` -> `%3%`, cancelled: %4%")
|
||||
% job_id
|
||||
% job.upload_data.upload_path
|
||||
% job.printhost->get_host()
|
||||
% job.cancelled;
|
||||
|
||||
if (! job.cancelled) {
|
||||
perform_job(std::move(job));
|
||||
}
|
||||
|
||||
remove_source();
|
||||
job_id++;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
emit_error(e.what());
|
||||
} catch (...) {
|
||||
wxTheApp->OnUnhandledException();
|
||||
emit_error("Unknown exception");
|
||||
}
|
||||
|
||||
// Cleanup leftover files, if any
|
||||
remove_source();
|
||||
auto jobs = channel_jobs.lock_rw();
|
||||
for (const PrintHostJob &job : *jobs) {
|
||||
remove_source(job.upload_data.source_path);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||
{
|
||||
if (cancel) {
|
||||
// When cancel is true from the start, Http indicates request has been cancelled
|
||||
emit_cancel(job_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bg_exit) {
|
||||
cancel = true;
|
||||
return;
|
||||
@ -121,48 +181,59 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||
if (cancel_id == job_id) {
|
||||
cancel = true;
|
||||
} else if (cancel_id > job_id) {
|
||||
jobs->at(cancel_id - job_id).cancelled = true;
|
||||
const size_t idx = cancel_id - job_id - 1;
|
||||
if (idx < jobs->size()) {
|
||||
jobs->at(idx).cancelled = true;
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue: Job id %1% cancelled") % cancel_id;
|
||||
emit_cancel(cancel_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cancels->clear();
|
||||
}
|
||||
|
||||
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
||||
if (gui_progress != prev_progress) {
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
prev_progress = gui_progress;
|
||||
if (! cancel) {
|
||||
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
||||
if (gui_progress != prev_progress) {
|
||||
emit_progress(gui_progress);
|
||||
prev_progress = gui_progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status)
|
||||
void PrintHostJobQueue::priv::remove_source(const fs::path &path)
|
||||
{
|
||||
// TODO
|
||||
if (! path.empty()) {
|
||||
boost::system::error_code ec;
|
||||
fs::remove(path, ec);
|
||||
if (ec) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % path % ec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::remove_source()
|
||||
{
|
||||
remove_source(source_to_remove);
|
||||
source_to_remove.clear();
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||
{
|
||||
if (bg_exit || the_job.empty()) { return; }
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`")
|
||||
% the_job.upload_data.upload_path
|
||||
% the_job.printhost->get_host();
|
||||
emit_progress(0); // Indicate the upload is starting
|
||||
|
||||
const fs::path gcode_path = the_job.upload_data.source_path;
|
||||
|
||||
the_job.printhost->upload(std::move(the_job.upload_data),
|
||||
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||
[this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); }
|
||||
[this](wxString error) {
|
||||
emit_error(std::move(error));
|
||||
}
|
||||
);
|
||||
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100);
|
||||
wxQueueEvent(queue_dialog, evt);
|
||||
|
||||
boost::system::error_code ec;
|
||||
fs::remove(gcode_path, ec);
|
||||
if (ec) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % gcode_path % ec;
|
||||
if (success) {
|
||||
emit_progress(100);
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,5 +244,10 @@ void PrintHostJobQueue::enqueue(PrintHostJob job)
|
||||
p->channel_jobs.push(std::move(job));
|
||||
}
|
||||
|
||||
void PrintHostJobQueue::cancel(size_t id)
|
||||
{
|
||||
p->channel_cancels.push(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
@ -28,15 +29,21 @@ class PrintHost
|
||||
public:
|
||||
virtual ~PrintHost();
|
||||
|
||||
typedef Http::ProgressFn ProgressFn;
|
||||
typedef std::function<void(wxString /* error */)> ErrorFn;
|
||||
|
||||
virtual bool test(wxString &curl_msg) const = 0;
|
||||
virtual wxString get_test_ok_msg () const = 0;
|
||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0;
|
||||
virtual bool has_auto_discovery() const = 0;
|
||||
virtual bool can_test() const = 0;
|
||||
virtual std::string get_host() const = 0;
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
|
||||
protected:
|
||||
virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const;
|
||||
};
|
||||
|
||||
|
||||
@ -51,6 +58,7 @@ struct PrintHostJob
|
||||
PrintHostJob(PrintHostJob &&other)
|
||||
: upload_data(std::move(other.upload_data))
|
||||
, printhost(std::move(other.printhost))
|
||||
, cancelled(other.cancelled)
|
||||
{}
|
||||
|
||||
PrintHostJob(DynamicPrintConfig *config)
|
||||
@ -62,6 +70,7 @@ struct PrintHostJob
|
||||
{
|
||||
upload_data = std::move(other.upload_data);
|
||||
printhost = std::move(other.printhost);
|
||||
cancelled = other.cancelled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|