Merge branch 'master' into lm_sla_supports_auto

This commit is contained in:
Lukas Matena 2018-12-20 22:35:47 +01:00
commit 9571e7c209
46 changed files with 1136 additions and 581 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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

View File

@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the 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"

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -164,6 +164,8 @@ public:
/// Get the pad geometry
const TriangleMesh& get_pad() const;
void remove_pad();
};
}

View File

@ -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

View File

@ -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);
}

View File

@ -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_

View File

@ -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, &current_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)

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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([]() {

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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);
}

View File

@ -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);

View File

@ -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")));

View File

@ -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

View File

@ -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; }

View File

@ -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)

View File

@ -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();
}

View File

@ -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);
}}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
};

View File

@ -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; }

View File

@ -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);

View File

@ -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

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}