Merge remote-tracking branch 'origin/et_gcode_viewer'

This commit is contained in:
enricoturri1966 2020-09-01 10:15:00 +02:00
commit 5f27b0f851
89 changed files with 10849 additions and 1845 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="thumb_left.svg"
xml:space="preserve"
enable-background="new 0 0 16 16"
viewBox="0 0 16 16"
y="0px"
x="0px"
id="Layer_1"
version="1.0"><metadata
id="metadata32"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs30" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="0"
inkscape:window-y="0"
inkscape:window-x="1268"
inkscape:cy="8"
inkscape:cx="8"
inkscape:zoom="63"
showgrid="false"
id="namedview28"
inkscape:window-height="1368"
inkscape:window-width="1283"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<g
transform="rotate(-90,8.0158731,7.984127)"
id="hex_x5F_plus">
<g
id="g24">
<polygon
id="polygon22"
style="stroke:#ffffff;stroke-width:1"
points="15,7 15,5 8,0 1,5 1,7 1,8 15,8 "
fill="#ed6b21" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="thumb_right.svg"
xml:space="preserve"
enable-background="new 0 0 16 16"
viewBox="0 0 16 16"
y="0px"
x="0px"
id="Layer_1"
version="1.0"><metadata
id="metadata32"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs30" /><sodipodi:namedview
inkscape:current-layer="Layer_1"
inkscape:window-maximized="0"
inkscape:window-y="0"
inkscape:window-x="1268"
inkscape:cy="8"
inkscape:cx="8"
inkscape:zoom="63"
showgrid="false"
id="namedview28"
inkscape:window-height="1368"
inkscape:window-width="1283"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
bordercolor="#666666"
pagecolor="#ffffff" />
<g
transform="matrix(0,-1,-1,0,16.012532,16)"
id="hex_x5F_plus">
<g
id="g24">
<polygon
id="polygon22"
style="stroke:#ffffff;stroke-width:1"
points="15,8 15,7 15,5 8,0 1,5 1,7 1,8 "
fill="#ed6b21" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,11 @@
#version 110
uniform vec4 uniform_color;
// x = tainted, y = specular;
varying vec2 intensity;
void main()
{
gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View file

@ -0,0 +1,38 @@
#version 110
#define INTENSITY_CORRECTION 0.6
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 20.0
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define INTENSITY_AMBIENT 0.3
// x = tainted, y = specular;
varying vec2 intensity;
void main()
{
// First transform the normal into camera space and normalize the result.
vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
gl_Position = ftransform();
}

View file

@ -0,0 +1,8 @@
#version 110
uniform vec4 uniform_color;
void main()
{
gl_FragColor = uniform_color;
}

View file

@ -0,0 +1,11 @@
#version 110
uniform float zoom;
uniform float point_size;
uniform float near_plane_height;
void main()
{
gl_Position = ftransform();
gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w;
}

View file

@ -0,0 +1,22 @@
// version 120 is needed for gl_PointCoord
#version 120
uniform vec4 uniform_color;
uniform float percent_outline_radius;
uniform float percent_center_radius;
vec4 calc_color(float radius, vec4 color)
{
return ((radius < percent_center_radius) || (radius > 1.0 - percent_outline_radius)) ?
vec4(0.5 * color.rgb, color.a) : color;
}
void main()
{
vec2 pos = (gl_PointCoord - 0.5) * 2.0;
float radius = length(pos);
if (radius > 1.0)
discard;
gl_FragColor = calc_color(radius, uniform_color);
}

View file

@ -0,0 +1,11 @@
#version 120
uniform float zoom;
uniform float point_size;
uniform float near_plane_height;
void main()
{
gl_Position = ftransform();
gl_PointSize = (gl_Position.w == 1.0) ? zoom * near_plane_height * point_size : near_plane_height * point_size / gl_Position.w;
}

View file

@ -0,0 +1,28 @@
#version 110
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0);
// x = ambient, y = top diffuse, z = front diffuse, w = global
uniform vec4 light_intensity;
uniform vec4 uniform_color;
varying vec3 eye_normal;
void main()
{
vec3 normal = normalize(eye_normal);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. Take the abs value to light the lines no matter in which direction the normal points.
float NdotL = abs(dot(normal, LIGHT_TOP_DIR));
float intensity = light_intensity.x + NdotL * light_intensity.y;
// Perform the same lighting calculation for the 2nd light source.
NdotL = abs(dot(normal, LIGHT_FRONT_DIR));
intensity += NdotL * light_intensity.z;
gl_FragColor = vec4(uniform_color.rgb * light_intensity.w * intensity, uniform_color.a);
}

View file

@ -0,0 +1,19 @@
#version 110
varying vec3 eye_normal;
vec3 world_normal()
{
// the world normal is always parallel to the world XY plane
// the x component is stored into gl_Vertex.w
float x = gl_Vertex.w;
float y = sqrt(1.0 - x * x);
return vec3(x, y, 0.0);
}
void main()
{
vec4 world_position = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * world_position;
eye_normal = gl_NormalMatrix * world_normal();
}

View file

@ -469,7 +469,11 @@ int CLI::run(int argc, char **argv)
print->process();
if (printer_technology == ptFFF) {
// The outfile is processed by a PlaceholderParser.
#if ENABLE_GCODE_VIEWER
outfile = fff_print.export_gcode(outfile, nullptr, nullptr);
#else
outfile = fff_print.export_gcode(outfile, nullptr);
#endif // ENABLE_GCODE_VIEWER
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
} else {
outfile = sla_print.output_filepath(outfile);

View file

@ -99,6 +99,8 @@ add_library(libslic3r STATIC
GCode/ToolOrdering.hpp
GCode/WipeTower.cpp
GCode/WipeTower.hpp
GCode/GCodeProcessor.cpp
GCode/GCodeProcessor.hpp
GCode.cpp
GCode.hpp
GCodeReader.cpp

View file

@ -1,6 +1,10 @@
#include "CustomGCode.hpp"
#include "Config.hpp"
#if ENABLE_GCODE_VIEWER
#include "GCode.hpp"
#else
#include "GCode/PreviewData.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "GCodeWriter.hpp"
namespace Slic3r {
@ -17,8 +21,12 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint
return;
if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
#if ENABLE_GCODE_VIEWER
const std::vector<std::string>& colors = ColorPrintColors::get();
#else
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
#endif // ENABLE_GCODE_VIEWER
const auto& colorprint_values = colorprint_heights->values;
info.gcodes.clear();
info.gcodes.reserve(colorprint_values.size());
int i = 0;

View file

@ -306,7 +306,11 @@ double ExtrusionLoop::min_mm3_per_mm() const
std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
{
switch (role) {
#if ENABLE_GCODE_VIEWER
case erNone : return L("Unknown");
#else
case erNone : return L("None");
#endif // ENABLE_GCODE_VIEWER
case erPerimeter : return L("Perimeter");
case erExternalPerimeter : return L("External perimeter");
case erOverhangPerimeter : return L("Overhang perimeter");
@ -327,4 +331,40 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
return "";
}
ExtrusionRole ExtrusionEntity::string_to_role(const std::string& role)
{
if (role == L("Perimeter"))
return erPerimeter;
else if (role == L("External perimeter"))
return erExternalPerimeter;
else if (role == L("Overhang perimeter"))
return erOverhangPerimeter;
else if (role == L("Internal infill"))
return erInternalInfill;
else if (role == L("Solid infill"))
return erSolidInfill;
else if (role == L("Top solid infill"))
return erTopSolidInfill;
else if (role == L("Ironing"))
return erIroning;
else if (role == L("Bridge infill"))
return erBridgeInfill;
else if (role == L("Gap fill"))
return erGapFill;
else if (role == L("Skirt"))
return erSkirt;
else if (role == L("Support material"))
return erSupportMaterial;
else if (role == L("Support material interface"))
return erSupportMaterialInterface;
else if (role == L("Wipe tower"))
return erWipeTower;
else if (role == L("Custom"))
return erCustom;
else if (role == L("Mixed"))
return erMixed;
else
return erNone;
}
}

View file

@ -106,6 +106,7 @@ public:
virtual double total_volume() const = 0;
static std::string role_to_string(ExtrusionRole role);
static ExtrusionRole string_to_role(const std::string& role);
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;

File diff suppressed because it is too large Load diff

View file

@ -13,9 +13,13 @@
#include "GCode/SpiralVase.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "GCodeTimeEstimator.hpp"
#include "EdgeGrid.hpp"
#if ENABLE_GCODE_VIEWER
#include "GCode/GCodeProcessor.hpp"
#else
#include "GCode/Analyzer.hpp"
#include "GCodeTimeEstimator.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "EdgeGrid.hpp"
#include "GCode/ThumbnailData.hpp"
#include <memory>
@ -29,7 +33,9 @@ namespace Slic3r {
// Forward declarations.
class GCode;
#if !ENABLE_GCODE_VIEWER
class GCodePreviewData;
#endif // !ENABLE_GCODE_VIEWER
namespace { struct Item; }
struct PrintInstance;
@ -138,6 +144,15 @@ private:
double m_last_wipe_tower_print_z = 0.f;
};
#if ENABLE_GCODE_VIEWER
class ColorPrintColors
{
static const std::vector<std::string> Colors;
public:
static const std::vector<std::string>& get() { return Colors; }
};
#endif // ENABLE_GCODE_VIEWER
class GCode {
public:
GCode() :
@ -145,21 +160,33 @@ public:
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false),
#if ENABLE_GCODE_VIEWER
m_last_processor_extrusion_role(erNone),
#else
m_enable_analyzer(false),
m_last_analyzer_extrusion_role(erNone),
#endif // ENABLE_GCODE_VIEWER
m_layer_count(0),
m_layer_index(-1),
m_layer(nullptr),
m_volumetric_speed(0),
m_last_pos_defined(false),
m_last_extrusion_role(erNone),
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_last_mm3_per_mm(0.0),
m_last_width(0.0f),
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
#if !ENABLE_GCODE_VIEWER
m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm),
m_last_width(GCodeAnalyzer::Default_Width),
m_last_height(GCodeAnalyzer::Default_Height),
#endif // !ENABLE_GCODE_VIEWER
m_brim_done(false),
m_second_layer_things_done(false),
#if !ENABLE_GCODE_VIEWER
m_normal_time_estimator(GCodeTimeEstimator::Normal),
m_silent_time_estimator(GCodeTimeEstimator::Silent),
#endif // !ENABLE_GCODE_VIEWER
m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
@ -167,7 +194,11 @@ public:
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
#if ENABLE_GCODE_VIEWER
void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
#else
void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
#endif // ENABLE_GCODE_VIEWER
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Vec2d& origin() const { return m_origin; }
@ -327,11 +358,16 @@ private:
// Markers for the Pressure Equalizer to recognize the extrusion type.
// The Pressure Equalizer removes the markers from the final G-code.
bool m_enable_extrusion_role_markers;
#if ENABLE_GCODE_VIEWER
// Keeps track of the last extrusion role passed to the processor
ExtrusionRole m_last_processor_extrusion_role;
#else
// Enableds the G-code Analyzer.
// Extended markers will be added during G-code generation.
// The G-code Analyzer will remove these comments from the final G-code.
bool m_enable_analyzer;
ExtrusionRole m_last_analyzer_extrusion_role;
#endif // ENABLE_GCODE_VIEWER
// How many times will change_layer() be called?
// change_layer() will update the progress bar.
unsigned int m_layer_count;
@ -344,10 +380,20 @@ private:
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
ExtrusionRole m_last_extrusion_role;
#if ENABLE_GCODE_VIEWER
// Support for G-Code Processor
float m_last_height{ 0.0f };
float m_last_layer_z{ 0.0f };
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
double m_last_mm3_per_mm;
float m_last_width{ 0.0f };
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
#else
// Support for G-Code Analyzer
double m_last_mm3_per_mm;
float m_last_width;
float m_last_height;
#endif // ENABLE_GCODE_VIEWER
Point m_last_pos;
bool m_last_pos_defined;
@ -368,13 +414,20 @@ private:
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
#if !ENABLE_GCODE_VIEWER
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;
GCodeTimeEstimator m_silent_time_estimator;
#endif // !ENABLE_GCODE_VIEWER
bool m_silent_time_estimator_enabled;
#if ENABLE_GCODE_VIEWER
// Processor
GCodeProcessor m_processor;
#else
// Analyzer
GCodeAnalyzer m_analyzer;
#endif // ENABLE_GCODE_VIEWER
// Write a string into a file.
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }

View file

@ -12,6 +12,8 @@
#include "Analyzer.hpp"
#include "PreviewData.hpp"
#if !ENABLE_GCODE_VIEWER
static const std::string AXIS_STR = "XYZE";
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float INCHES_TO_MM = 25.4f;
@ -350,7 +352,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
if (delta_pos[E] < 0.0f)
{
if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
type = GCodeMove::Move;
type = GCodeMove::Move;
else
type = GCodeMove::Retract;
}
@ -651,7 +653,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
return true;
}
// color change tag
// pause print tag
pos = comment.find(Pause_Print_Tag);
if (pos != comment.npos)
{
@ -659,7 +661,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
return true;
}
// color change tag
// custom code tag
pos = comment.find(Custom_Code_Tag);
if (pos != comment.npos)
{
@ -667,7 +669,7 @@ bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
return true;
}
// color change tag
// end pause print or custom code tag
pos = comment.find(End_Pause_Print_Or_Custom_Code_Tag);
if (pos != comment.npos)
{
@ -1191,3 +1193,5 @@ size_t GCodeAnalyzer::memory_used() const
}
} // namespace Slic3r
#endif // !ENABLE_GCODE_VIEWER

View file

@ -1,6 +1,8 @@
#ifndef slic3r_GCode_Analyzer_hpp_
#define slic3r_GCode_Analyzer_hpp_
#if !ENABLE_GCODE_VIEWER
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "../ExtrusionEntity.hpp"
@ -302,4 +304,6 @@ private:
} // namespace Slic3r
#endif // !ENABLE_GCODE_VIEWER
#endif /* slic3r_GCode_Analyzer_hpp_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,559 @@
#ifndef slic3r_GCodeProcessor_hpp_
#define slic3r_GCodeProcessor_hpp_
#if ENABLE_GCODE_VIEWER
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/CustomGCode.hpp"
#include <array>
#include <vector>
#include <string>
namespace Slic3r {
enum class EMoveType : unsigned char
{
Noop,
Retract,
Unretract,
Tool_change,
Color_change,
Pause_Print,
Custom_GCode,
Travel,
Extrude,
Count
};
struct PrintEstimatedTimeStatistics
{
enum class ETimeMode : unsigned char
{
Normal,
Stealth,
Count
};
struct Mode
{
float time;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
std::vector<std::pair<EMoveType, float>> moves_times;
std::vector<std::pair<ExtrusionRole, float>> roles_times;
std::vector<float> layers_times;
void reset() {
time = 0.0f;
custom_gcode_times.clear();
moves_times.clear();
roles_times.clear();
layers_times.clear();
}
};
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
PrintEstimatedTimeStatistics() { reset(); }
void reset() {
for (auto m : modes) {
m.reset();
}
}
};
class GCodeProcessor
{
public:
static const std::string Extrusion_Role_Tag;
static const std::string Height_Tag;
static const std::string Layer_Change_Tag;
static const std::string Color_Change_Tag;
static const std::string Pause_Print_Tag;
static const std::string Custom_Code_Tag;
static const std::string First_Line_M73_Placeholder_Tag;
static const std::string Last_Line_M73_Placeholder_Tag;
static const std::string Estimated_Printing_Time_Placeholder_Tag;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
static const std::string Width_Tag;
static const std::string Mm3_Per_Mm_Tag;
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
private:
using AxisCoords = std::array<float, 4>;
using ExtruderColors = std::vector<unsigned char>;
enum class EUnits : unsigned char
{
Millimeters,
Inches
};
enum class EPositioningType : unsigned char
{
Absolute,
Relative
};
struct CachedPosition
{
AxisCoords position; // mm
float feedrate; // mm/s
void reset();
};
struct CpColor
{
unsigned char counter;
unsigned char current;
void reset();
};
public:
struct FeedrateProfile
{
float entry{ 0.0f }; // mm/s
float cruise{ 0.0f }; // mm/s
float exit{ 0.0f }; // mm/s
};
struct Trapezoid
{
float accelerate_until{ 0.0f }; // mm
float decelerate_after{ 0.0f }; // mm
float cruise_feedrate{ 0.0f }; // mm/sec
float acceleration_time(float entry_feedrate, float acceleration) const;
float cruise_time() const;
float deceleration_time(float distance, float acceleration) const;
float cruise_distance() const;
};
struct TimeBlock
{
struct Flags
{
bool recalculate{ false };
bool nominal_length{ false };
};
EMoveType move_type{ EMoveType::Noop };
ExtrusionRole role{ erNone };
unsigned int layer_id{ 0 };
float distance{ 0.0f }; // mm
float acceleration{ 0.0f }; // mm/s^2
float max_entry_speed{ 0.0f }; // mm/s
float safe_feedrate{ 0.0f }; // mm/s
Flags flags;
FeedrateProfile feedrate_profile;
Trapezoid trapezoid;
// Calculates this block's trapezoid
void calculate_trapezoid();
float time() const;
};
private:
struct TimeMachine
{
struct State
{
float feedrate; // mm/s
float safe_feedrate; // mm/s
AxisCoords axis_feedrate; // mm/s
AxisCoords abs_axis_feedrate; // mm/s
void reset();
};
struct CustomGCodeTime
{
bool needed;
float cache;
std::vector<std::pair<CustomGCode::Type, float>> times;
void reset();
};
bool enabled;
float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2
float extrude_factor_override_percentage;
float time; // s
std::string line_m73_mask;
State curr;
State prev;
CustomGCodeTime gcode_time;
std::vector<TimeBlock> blocks;
std::vector<float> g1_times_cache;
std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
std::vector<float> layers_time;
void reset();
// Simulates firmware st_synchronize() call
void simulate_st_synchronize(float additional_time = 0.0f);
void calculate_time(size_t keep_last_n_blocks = 0);
};
struct TimeProcessor
{
struct Planner
{
// Size of the firmware planner queue. The old 8-bit Marlins usually just managed 16 trapezoidal blocks.
// Let's be conservative and plan for newer boards with more memory.
static constexpr size_t queue_size = 64;
// The firmware recalculates last planner_queue_size trapezoidal blocks each time a new block is added.
// We are not simulating the firmware exactly, we calculate a sequence of blocks once a reasonable number of blocks accumulate.
static constexpr size_t refresh_threshold = queue_size * 4;
};
// extruder_id is currently used to correctly calculate filament load / unload times into the total print time.
// This is currently only really used by the MK3 MMU2:
// extruder_unloaded = true means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
bool extruder_unloaded;
// whether or not to export post-process the gcode to export lines M73 in it
bool export_remaining_time_enabled;
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope()
bool machine_envelope_processing_enabled;
MachineEnvelopeConfig machine_limits;
// Additional load / unload times for a filament exchange sequence.
std::vector<float> filament_load_times;
std::vector<float> filament_unload_times;
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
void reset();
// post process the file with the given filename to add remaining time lines M73
void post_process(const std::string& filename);
};
public:
struct MoveVertex
{
EMoveType type{ EMoveType::Noop };
ExtrusionRole extrusion_role{ erNone };
unsigned char extruder_id{ 0 };
unsigned char cp_color_id{ 0 };
Vec3f position{ Vec3f::Zero() }; // mm
float delta_extruder{ 0.0f }; // mm
float feedrate{ 0.0f }; // mm/s
float width{ 0.0f }; // mm
float height{ 0.0f }; // mm
float mm3_per_mm{ 0.0f };
float fan_speed{ 0.0f }; // percentage
float time{ 0.0f }; // s
float volumetric_rate() const { return feedrate * mm3_per_mm; }
};
struct Result
{
unsigned int id;
std::vector<MoveVertex> moves;
Pointfs bed_shape;
std::string printer_settings_id;
std::vector<std::string> extruder_colors;
PrintEstimatedTimeStatistics time_statistics;
#if ENABLE_GCODE_VIEWER_STATISTICS
long long time{ 0 };
void reset()
{
time = 0;
moves = std::vector<MoveVertex>();
bed_shape = Pointfs();
extruder_colors = std::vector<std::string>();
}
#else
void reset()
{
moves = std::vector<MoveVertex>();
bed_shape = Pointfs();
extruder_colors = std::vector<std::string>();
}
#endif // ENABLE_GCODE_VIEWER_STATISTICS
};
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
struct DataChecker
{
struct Error
{
float value;
float tag_value;
ExtrusionRole role;
};
std::string type;
float threshold{ 0.01f };
float last_tag_value{ 0.0f };
unsigned int count{ 0 };
std::vector<Error> errors;
DataChecker(const std::string& type, float threshold)
: type(type), threshold(threshold)
{}
void update(float value, ExtrusionRole role) {
++count;
if (last_tag_value != 0.0f) {
if (std::abs(value - last_tag_value) / last_tag_value > threshold)
errors.push_back({ value, last_tag_value, role });
}
}
void reset() { last_tag_value = 0.0f; errors.clear(); count = 0; }
std::pair<float, float> get_min() const {
float delta_min = FLT_MAX;
float perc_min = 0.0f;
for (const Error& e : errors) {
if (delta_min > e.value - e.tag_value) {
delta_min = e.value - e.tag_value;
perc_min = 100.0f * delta_min / e.tag_value;
}
}
return { delta_min, perc_min };
}
std::pair<float, float> get_max() const {
float delta_max = -FLT_MAX;
float perc_max = 0.0f;
for (const Error& e : errors) {
if (delta_max < e.value - e.tag_value) {
delta_max = e.value - e.tag_value;
perc_max = 100.0f * delta_max / e.tag_value;
}
}
return { delta_max, perc_max };
}
void output() const {
if (!errors.empty()) {
std::cout << type << ":\n";
std::cout << "Errors: " << errors.size() << " (" << 100.0f * float(errors.size()) / float(count) << "%)\n";
auto [min, perc_min] = get_min();
auto [max, perc_max] = get_max();
std::cout << "min: " << min << "(" << perc_min << "%) - max: " << max << "(" << perc_max << "%)\n";
}
}
};
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
private:
GCodeReader m_parser;
EUnits m_units;
EPositioningType m_global_positioning_type;
EPositioningType m_e_local_positioning_type;
std::vector<Vec3f> m_extruder_offsets;
GCodeFlavor m_flavor;
AxisCoords m_start_position; // mm
AxisCoords m_end_position; // mm
AxisCoords m_origin; // mm
CachedPosition m_cached_position;
float m_feedrate; // mm/s
float m_width; // mm
float m_height; // mm
float m_mm3_per_mm;
float m_fan_speed; // percentage
ExtrusionRole m_extrusion_role;
unsigned char m_extruder_id;
ExtruderColors m_extruder_colors;
std::vector<float> m_filament_diameters;
float m_extruded_last_z;
unsigned int m_layer_id;
CpColor m_cp_color;
enum class EProducer
{
Unknown,
PrusaSlicer,
Cura,
Simplify3D,
CraftWare,
ideaMaker
};
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
EProducer m_producer;
bool m_producers_enabled;
TimeProcessor m_time_processor;
Result m_result;
static unsigned int s_result_id;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
DataChecker m_mm3_per_mm_compare{ "mm3_per_mm", 0.01f };
DataChecker m_height_compare{ "height", 0.01f };
DataChecker m_width_compare{ "width", 0.01f };
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
public:
GCodeProcessor();
void apply_config(const PrintConfig& config);
void apply_config(const DynamicPrintConfig& config);
void enable_stealth_time_estimator(bool enabled);
bool is_stealth_time_estimator_enabled() const {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
}
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
void reset();
const Result& get_result() const { return m_result; }
Result&& extract_result() { return std::move(m_result); }
// Process the gcode contained in the file with the given filename
void process_file(const std::string& filename);
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
private:
void process_gcode_line(const GCodeReader::GCodeLine& line);
// Process tags embedded into comments
void process_tags(const std::string& comment);
bool process_producers_tags(const std::string& comment);
bool process_prusaslicer_tags(const std::string& comment);
bool process_cura_tags(const std::string& comment);
bool process_simplify3d_tags(const std::string& comment);
bool process_craftware_tags(const std::string& comment);
bool process_ideamaker_tags(const std::string& comment);
bool detect_producer(const std::string& comment);
// Move
void process_G0(const GCodeReader::GCodeLine& line);
void process_G1(const GCodeReader::GCodeLine& line);
// Retract
void process_G10(const GCodeReader::GCodeLine& line);
// Unretract
void process_G11(const GCodeReader::GCodeLine& line);
// Set Units to Inches
void process_G20(const GCodeReader::GCodeLine& line);
// Set Units to Millimeters
void process_G21(const GCodeReader::GCodeLine& line);
// Firmware controlled Retract
void process_G22(const GCodeReader::GCodeLine& line);
// Firmware controlled Unretract
void process_G23(const GCodeReader::GCodeLine& line);
// Set to Absolute Positioning
void process_G90(const GCodeReader::GCodeLine& line);
// Set to Relative Positioning
void process_G91(const GCodeReader::GCodeLine& line);
// Set Position
void process_G92(const GCodeReader::GCodeLine& line);
// Sleep or Conditional stop
void process_M1(const GCodeReader::GCodeLine& line);
// Set extruder to absolute mode
void process_M82(const GCodeReader::GCodeLine& line);
// Set extruder to relative mode
void process_M83(const GCodeReader::GCodeLine& line);
// Set fan speed
void process_M106(const GCodeReader::GCodeLine& line);
// Disable fan
void process_M107(const GCodeReader::GCodeLine& line);
// Set tool (Sailfish)
void process_M108(const GCodeReader::GCodeLine& line);
// Recall stored home offsets
void process_M132(const GCodeReader::GCodeLine& line);
// Set tool (MakerWare)
void process_M135(const GCodeReader::GCodeLine& line);
// Set max printing acceleration
void process_M201(const GCodeReader::GCodeLine& line);
// Set maximum feedrate
void process_M203(const GCodeReader::GCodeLine& line);
// Set default acceleration
void process_M204(const GCodeReader::GCodeLine& line);
// Advanced settings
void process_M205(const GCodeReader::GCodeLine& line);
// Set extrude factor override percentage
void process_M221(const GCodeReader::GCodeLine& line);
// Repetier: Store x, y and z position
void process_M401(const GCodeReader::GCodeLine& line);
// Repetier: Go to stored position
void process_M402(const GCodeReader::GCodeLine& line);
// Set allowable instantaneous speed change
void process_M566(const GCodeReader::GCodeLine& line);
// Unload the current filament into the MK3 MMU2 unit at the end of print.
void process_M702(const GCodeReader::GCodeLine& line);
// Processes T line (Select Tool)
void process_T(const GCodeReader::GCodeLine& line);
void process_T(const std::string& command);
void store_move_vertex(EMoveType type);
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
float get_filament_load_time(size_t extruder_id);
float get_filament_unload_time(size_t extruder_id);
void process_custom_gcode_time(CustomGCode::Type code);
// Simulates firmware st_synchronize() call
void simulate_st_synchronize(float additional_time = 0.0f);
void update_estimated_times_stats();
};
} /* namespace Slic3r */
#endif // ENABLE_GCODE_VIEWER
#endif /* slic3r_GCodeProcessor_hpp_ */

View file

@ -5,6 +5,8 @@
#include <boost/format.hpp>
#if !ENABLE_GCODE_VIEWER
//! macro used to mark string used at localization,
#define L(s) (s)
@ -516,3 +518,5 @@ Color operator * (float f, const Color& color)
}
} // namespace Slic3r
#endif // !ENABLE_GCODE_VIEWER

View file

@ -1,6 +1,8 @@
#ifndef slic3r_GCode_PreviewData_hpp_
#define slic3r_GCode_PreviewData_hpp_
#if !ENABLE_GCODE_VIEWER
#include "../libslic3r.h"
#include "../ExtrusionEntity.hpp"
#include "../Point.hpp"
@ -56,8 +58,7 @@ public:
// Color mapping to convert a float into a smooth rainbow of 10 colors.
class RangeBase
{
public:
public:
virtual void reset() = 0;
virtual bool empty() const = 0;
virtual float min() const = 0;
@ -73,7 +74,7 @@ public:
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
class Range : public RangeBase
{
public:
public:
Range();
// RangeBase Overrides
@ -97,8 +98,7 @@ public:
template <typename EnumRangeType>
class MultiRange : public RangeBase
{
public:
public:
void reset() override
{
bounds = decltype(bounds){};
@ -160,8 +160,7 @@ public:
mode.set(static_cast<std::size_t>(range_type_value), enable);
}
private:
private:
// Interval bounds
struct Bounds
{
@ -394,4 +393,6 @@ public:
} // namespace Slic3r
#endif // !ENABLE_GCODE_VIEWER
#endif /* slic3r_GCode_PreviewData_hpp_ */

View file

@ -21,7 +21,11 @@ TODO LIST
#include <vector>
#include <numeric>
#if ENABLE_GCODE_VIEWER
#include "GCodeProcessor.hpp"
#else
#include "Analyzer.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "BoundingBox.hpp"
#if defined(__linux) || defined(__GNUC__ )
@ -47,36 +51,69 @@ public:
m_extrusion_flow(0.f),
m_preview_suppressed(false),
m_elapsed_time(0.f),
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
m_default_analyzer_line_width(line_width),
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
m_gcode_flavor(flavor),
m_filpar(filament_parameters)
{
// adds tag for analyzer:
char buf[64];
#if ENABLE_GCODE_VIEWER
sprintf(buf, ";%s%f\n", GCodeProcessor::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
m_gcode += buf;
sprintf(buf, ";%s%s\n", GCodeProcessor::Extrusion_Role_Tag.c_str(), ExtrusionEntity::role_to_string(erWipeTower).c_str());
m_gcode += buf;
#else
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
m_gcode += buf;
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
m_gcode += buf;
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
change_analyzer_line_width(line_width);
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
}
WipeTowerWriter& change_analyzer_line_width(float line_width) {
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
m_gcode += buf;
return *this;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
WipeTowerWriter& change_analyzer_line_width(float line_width) {
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeProcessor::Width_Tag.c_str(), line_width);
m_gcode += buf;
return *this;
}
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
m_gcode += buf;
return *this;
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
// adds tag for processor:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeProcessor::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
m_gcode += buf;
return *this;
}
#else
#if !ENABLE_GCODE_VIEWER
WipeTowerWriter& change_analyzer_line_width(float line_width) {
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
m_gcode += buf;
return *this;
}
WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) {
static const float area = float(M_PI) * 1.75f * 1.75f / 4.f;
float mm3_per_mm = (len == 0.f ? 0.f : area * e / len);
// adds tag for analyzer:
char buf[64];
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm);
m_gcode += buf;
return *this;
}
#endif // !ENABLE_GCODE_VIEWER
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
m_wipe_tower_width = width;
@ -111,8 +148,13 @@ public:
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions.
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
#else
WipeTowerWriter& suppress_preview() { m_preview_suppressed = true; return *this; }
WipeTowerWriter& resume_preview() { m_preview_suppressed = false; return *this; }
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
WipeTowerWriter& feedrate(float f)
{
@ -149,8 +191,14 @@ public:
Vec2f rot(this->rotate(Vec2f(x,y))); // this is where we want to go
if (! m_preview_suppressed && e > 0.f && len > 0.f) {
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
change_analyzer_mm3_per_mm(len, e);
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
#else
#if !ENABLE_GCODE_VIEWER
change_analyzer_mm3_per_mm(len, e);
#endif // !ENABLE_GCODE_VIEWER
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width = e * m_filpar[0].filament_area / (len * m_layer_height);
// Correct for the roundings of a squished extrusion.
@ -411,7 +459,9 @@ private:
float m_wipe_tower_depth = 0.f;
unsigned m_last_fan_speed = 0;
int current_temp = -1;
#if !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
const float m_default_analyzer_line_width;
#endif // !ENABLE_GCODE_VIEWER || ENABLE_GCODE_VIEWER_DATA_CHECKING
float m_used_filament_length = 0.f;
GCodeFlavor m_gcode_flavor;
const std::vector<WipeTower::FilamentParameters>& m_filpar;
@ -852,8 +902,12 @@ void WipeTower::toolchange_Unload(
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
writer.append("; CP TOOLCHANGE UNLOAD\n")
.change_analyzer_line_width(line_width);
.change_analyzer_line_width(line_width);
#else
writer.append("; CP TOOLCHANGE UNLOAD\n");
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
unsigned i = 0; // iterates through ramming_speed
m_left_to_right = true; // current direction of ramming
@ -930,7 +984,9 @@ void WipeTower::toolchange_Unload(
}
}
Vec2f end_of_ramming(writer.x(),writer.y());
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
// Retraction:
float old_x = writer.x();

View file

@ -115,7 +115,12 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
{
std::ifstream f(file);
std::string line;
#if ENABLE_GCODE_VIEWER
m_parsing_file = true;
while (m_parsing_file && std::getline(f, line))
#else
while (std::getline(f, line))
#endif // ENABLE_GCODE_VIEWER
this->parse_line(line, callback);
}

View file

@ -107,6 +107,9 @@ public:
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
void parse_file(const std::string &file, callback_t callback);
#if ENABLE_GCODE_VIEWER
void quit_parsing_file() { m_parsing_file = false; }
#endif // ENABLE_GCODE_VIEWER
float& x() { return m_position[X]; }
float x() const { return m_position[X]; }
@ -145,6 +148,9 @@ private:
char m_extrusion_axis;
float m_position[NUM_AXES];
bool m_verbose;
#if ENABLE_GCODE_VIEWER
bool m_parsing_file{ false };
#endif // ENABLE_GCODE_VIEWER
};
} /* namespace Slic3r */

View file

@ -9,6 +9,8 @@
#include <boost/nowide/cstdio.hpp>
#include <boost/algorithm/string/predicate.hpp>
#if !ENABLE_GCODE_VIEWER
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float MILLISEC_TO_SEC = 0.001f;
static const float INCHES_TO_MM = 25.4f;
@ -1671,3 +1673,5 @@ namespace Slic3r {
}
#endif // ENABLE_MOVE_STATS
}
#endif // !ENABLE_GCODE_VIEWER

View file

@ -6,6 +6,8 @@
#include "GCodeReader.hpp"
#include "CustomGCode.hpp"
#if !ENABLE_GCODE_VIEWER
#define ENABLE_MOVE_STATS 0
namespace Slic3r {
@ -481,4 +483,6 @@ namespace Slic3r {
} /* namespace Slic3r */
#endif // !ENABLE_GCODE_VIEWER
#endif /* slic3r_GCodeTimeEstimator_hpp_ */

View file

@ -21,7 +21,9 @@
#include "SVG.hpp"
#include <Eigen/Dense>
#include "GCodeWriter.hpp"
#if !ENABLE_GCODE_VIEWER
#include "GCode/PreviewData.hpp"
#endif // !ENABLE_GCODE_VIEWER
namespace Slic3r {

View file

@ -1812,6 +1812,26 @@ namespace PresetUtils {
}
return out;
}
#if ENABLE_GCODE_VIEWER
std::string system_printer_bed_model(const Preset& preset)
{
std::string out;
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && !pm->bed_model.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
return out;
}
std::string system_printer_bed_texture(const Preset& preset)
{
std::string out;
const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && !pm->bed_texture.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
return out;
}
#endif // ENABLE_GCODE_VIEWER
} // namespace PresetUtils
} // namespace Slic3r

View file

@ -527,6 +527,10 @@ public:
namespace PresetUtils {
// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
#if ENABLE_GCODE_VIEWER
std::string system_printer_bed_model(const Preset& preset);
std::string system_printer_bed_texture(const Preset& preset);
#endif // ENABLE_GCODE_VIEWER
} // namespace PresetUtils

View file

@ -1632,13 +1632,21 @@ void Print::process()
// The export_gcode may die for various reasons (fails to process output_filename_format,
// write error into the G-code, cannot execute post-processing scripts).
// It is up to the caller to show an error message.
#if ENABLE_GCODE_VIEWER
std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
#else
std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
#endif // ENABLE_GCODE_VIEWER
{
// output everything to a G-code file
// The following call may die if the output_filename_format template substitution fails.
std::string path = this->output_filepath(path_template);
std::string message;
#if ENABLE_GCODE_VIEWER
if (!path.empty() && result == nullptr) {
#else
if (! path.empty() && preview_data == nullptr) {
#endif // ENABLE_GCODE_VIEWER
// Only show the path if preview_data is not set -> running from command line.
message = L("Exporting G-code");
message += " to ";
@ -1649,7 +1657,11 @@ std::string Print::export_gcode(const std::string& path_template, GCodePreviewDa
// The following line may die for multiple reasons.
GCode gcode;
#if ENABLE_GCODE_VIEWER
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
#else
gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
#endif // ENABLE_GCODE_VIEWER
return path.c_str();
}
@ -2180,16 +2192,16 @@ DynamicConfig PrintStatistics::config() const
DynamicConfig config;
std::string normal_print_time = short_time(this->estimated_normal_print_time);
std::string silent_print_time = short_time(this->estimated_silent_print_time);
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.));
config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume));
config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost));
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.));
config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume));
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight));
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost));
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament));
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
return config;
}

View file

@ -11,6 +11,9 @@
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
#include "GCode/ThumbnailData.hpp"
#if ENABLE_GCODE_VIEWER
#include "GCode/GCodeProcessor.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "libslic3r.h"
@ -20,7 +23,9 @@ class Print;
class PrintObject;
class ModelObject;
class GCode;
#if !ENABLE_GCODE_VIEWER
class GCodePreviewData;
#endif // !ENABLE_GCODE_VIEWER
enum class SlicingMode : uint32_t;
class Layer;
class SupportLayer;
@ -300,8 +305,10 @@ struct PrintStatistics
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
#if !ENABLE_GCODE_VIEWER
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_normal_custom_gcode_print_times;
std::vector<std::pair<CustomGCode::Type, std::string>> estimated_silent_custom_gcode_print_times;
#endif // !ENABLE_GCODE_VIEWER
double total_used_filament;
double total_extruded_volume;
double total_cost;
@ -319,10 +326,12 @@ struct PrintStatistics
std::string finalize_output_path(const std::string &path_in) const;
void clear() {
#if !ENABLE_GCODE_VIEWER
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
estimated_normal_custom_gcode_print_times.clear();
estimated_silent_custom_gcode_print_times.clear();
#endif // !ENABLE_GCODE_VIEWER
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;
@ -362,7 +371,11 @@ public:
void process() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
#if ENABLE_GCODE_VIEWER
std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
#else
std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
#endif // ENABLE_GCODE_VIEWER
// methods for handling state
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
@ -417,6 +430,7 @@ public:
const Polygon& first_layer_convex_hull() const { return m_first_layer_convex_hull; }
const PrintStatistics& print_statistics() const { return m_print_statistics; }
PrintStatistics& print_statistics() { return m_print_statistics; }
// Wipe tower support.
bool has_wipe_tower() const;

View file

@ -15,7 +15,7 @@
#define ENABLE_RENDER_STATISTICS 0
// Shows an imgui dialog with camera related data
#define ENABLE_CAMERA_STATISTICS 0
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
#define ENABLE_RENDER_PICKING_PASS 0
// Enable extracting thumbnails from selected gcode and save them as png files
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
@ -54,5 +54,10 @@
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
// Enable G-Code viewer
#define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1)
#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER)
#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER)
#define ENABLE_GCODE_VIEWER_TASKBAR_ICON (0 && ENABLE_GCODE_VIEWER)
#endif // _prusaslicer_technologies_h_

View file

@ -110,19 +110,20 @@ std::string header_slic3r_generated();
// getpid platform wrapper
extern unsigned get_current_pid();
#if !ENABLE_GCODE_VIEWER
template <typename Real>
Real round_nearest(Real value, unsigned int decimals)
{
Real res = (Real)0;
if (decimals == 0)
res = ::round(value);
else
{
else {
Real power = ::pow((Real)10, (int)decimals);
res = ::round(value * power + (Real)0.5) / power;
}
return res;
}
#endif // !ENABLE_GCODE_VIEWER
// Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html
@ -337,6 +338,25 @@ inline std::string get_time_dhms(float time_in_secs)
return buffer;
}
inline std::string get_time_dhm(float time_in_secs)
{
int days = (int)(time_in_secs / 86400.0f);
time_in_secs -= (float)days * 86400.0f;
int hours = (int)(time_in_secs / 3600.0f);
time_in_secs -= (float)hours * 3600.0f;
int minutes = (int)(time_in_secs / 60.0f);
char buffer[64];
if (days > 0)
::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
else if (hours > 0)
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
else if (minutes > 0)
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
return buffer;
}
} // namespace Slic3r
#if WIN32

View file

@ -21,6 +21,8 @@ set(SLIC3R_GUI_SOURCES
GUI/3DScene.cpp
GUI/3DScene.hpp
GUI/format.hpp
GUI/GLShadersManager.hpp
GUI/GLShadersManager.cpp
GUI/GLShader.cpp
GUI/GLShader.hpp
GUI/GLCanvas3D.hpp
@ -53,10 +55,14 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoHollow.hpp
GUI/GLSelectionRectangle.cpp
GUI/GLSelectionRectangle.hpp
GUI/GLModel.hpp
GUI/GLModel.cpp
GUI/GLTexture.hpp
GUI/GLTexture.cpp
GUI/GLToolbar.hpp
GUI/GLToolbar.cpp
GUI/GCodeViewer.hpp
GUI/GCodeViewer.cpp
GUI/Preferences.cpp
GUI/Preferences.hpp
GUI/PresetHints.cpp

View file

@ -5,15 +5,24 @@
#include "libslic3r/Polygon.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/BoundingBox.hpp"
#if ENABLE_GCODE_VIEWER
#include "libslic3r/Geometry.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "GUI_App.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "GLCanvas3D.hpp"
#if ENABLE_GCODE_VIEWER
#include "3DScene.hpp"
#endif // ENABLE_GCODE_VIEWER
#include <GL/glew.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
#if ENABLE_GCODE_VIEWER
#include <boost/log/trivial.hpp>
#endif // ENABLE_GCODE_VIEWER
static const float GROUND_Z = -0.02f;
@ -36,10 +45,8 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
float max_y = min_y;
unsigned int v_count = 0;
for (const Polygon& t : triangles)
{
for (unsigned int i = 0; i < 3; ++i)
{
for (const Polygon& t : triangles) {
for (unsigned int i = 0; i < 3; ++i) {
Vertex& v = m_vertices[v_count];
const Point& p = t.points[i];
@ -50,8 +57,7 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
v.position[1] = y;
v.position[2] = z;
if (generate_tex_coords)
{
if (generate_tex_coords) {
v.tex_coords[0] = x;
v.tex_coords[1] = y;
@ -65,17 +71,14 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
}
}
if (generate_tex_coords)
{
if (generate_tex_coords) {
float size_x = max_x - min_x;
float size_y = max_y - min_y;
if ((size_x != 0.0f) && (size_y != 0.0f))
{
if ((size_x != 0.0f) && (size_y != 0.0f)) {
float inv_size_x = 1.0f / size_x;
float inv_size_y = -1.0f / size_y;
for (Vertex& v : m_vertices)
{
for (Vertex& v : m_vertices) {
v.tex_coords[0] = (v.tex_coords[0] - min_x) * inv_size_x;
v.tex_coords[1] = (v.tex_coords[1] - min_y) * inv_size_y;
}
@ -96,8 +99,7 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
m_vertices = std::vector<Vertex>(v_size, Vertex());
unsigned int v_count = 0;
for (const Line& l : lines)
{
for (const Line& l : lines) {
Vertex& v1 = m_vertices[v_count];
v1.position[0] = unscale<float>(l.a(0));
v1.position[1] = unscale<float>(l.a(1));
@ -119,10 +121,24 @@ const float* GeometryBuffer::get_vertices_data() const
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
}
#if ENABLE_GCODE_VIEWER
const float Bed3D::Axes::DefaultStemRadius = 0.5f;
const float Bed3D::Axes::DefaultStemLength = 25.0f;
const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius;
const float Bed3D::Axes::DefaultTipLength = 5.0f;
#else
const double Bed3D::Axes::Radius = 0.5;
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
const double Bed3D::Axes::ArrowLength = 5.0;
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void Bed3D::Axes::set_stem_length(float length)
{
m_stem_length = length;
m_arrow.reset();
}
#else
Bed3D::Axes::Axes()
: origin(Vec3d::Zero())
, length(25.0 * Vec3d::Ones())
@ -137,9 +153,47 @@ Bed3D::Axes::~Axes()
if (m_quadric != nullptr)
::gluDeleteQuadric(m_quadric);
}
#endif // ENABLE_GCODE_VIEWER
void Bed3D::Axes::render() const
{
#if ENABLE_GCODE_VIEWER
auto render_axis = [this](const Transform3f& transform) {
glsafe(::glPushMatrix());
glsafe(::glMultMatrixf(transform.data()));
m_arrow.render();
glsafe(::glPopMatrix());
};
m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
glsafe(::glEnable(GL_DEPTH_TEST));
shader->start_using();
// x axis
std::array<float, 4> color = { 0.75f, 0.0f, 0.0f, 1.0f };
shader->set_uniform("uniform_color", color);
render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast<float>());
// y axis
color = { 0.0f, 0.75f, 0.0f, 1.0f };
shader->set_uniform("uniform_color", color);
render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast<float>());
// z axis
color = { 0.0f, 0.0f, 0.75f, 1.0f };
shader->set_uniform("uniform_color", color);
render_axis(Geometry::assemble_transform(m_origin).cast<float>());
shader->stop_using();
glsafe(::glDisable(GL_DEPTH_TEST));
#else
if (m_quadric == nullptr)
return;
@ -171,8 +225,10 @@ void Bed3D::Axes::render() const
glsafe(::glDisable(GL_LIGHTING));
glsafe(::glDisable(GL_DEPTH_TEST));
#endif // !ENABLE_GCODE_VIEWER
}
#if !ENABLE_GCODE_VIEWER
void Bed3D::Axes::render_axis(double length) const
{
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
@ -185,6 +241,7 @@ void Bed3D::Axes::render_axis(double length) const
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
}
#endif // !ENABLE_GCODE_VIEWER
Bed3D::Bed3D()
: m_type(Custom)
@ -193,7 +250,7 @@ Bed3D::Bed3D()
{
}
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
auto check_texture = [](const std::string& texture) {
return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture);
@ -203,30 +260,39 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model);
};
auto [new_type, system_model, system_texture] = detect_type(shape);
EType type;
std::string model;
std::string texture;
if (force_as_custom)
type = Custom;
else {
auto [new_type, system_model, system_texture] = detect_type(shape);
type = new_type;
model = system_model;
texture = system_texture;
}
std::string texture_filename = custom_texture.empty() ? system_texture : custom_texture;
std::string texture_filename = custom_texture.empty() ? texture : custom_texture;
if (!check_texture(texture_filename))
texture_filename.clear();
std::string model_filename = custom_model.empty() ? system_model : custom_model;
std::string model_filename = custom_model.empty() ? model : custom_model;
if (!check_model(model_filename))
model_filename.clear();
if ((m_shape == shape) && (m_type == new_type) && (m_texture_filename == texture_filename) && (m_model_filename == model_filename))
if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
// No change, no need to update the UI.
return false;
m_shape = shape;
m_texture_filename = texture_filename;
m_model_filename = model_filename;
m_type = new_type;
m_type = type;
calc_bounding_boxes();
ExPolygon poly;
for (const Vec2d& p : m_shape)
{
for (const Vec2d& p : m_shape) {
poly.contour.append(Point(scale_(p(0)), scale_(p(1))));
}
@ -242,8 +308,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
m_model.reset();
// Set the origin and size for rendering the coordinate system axes.
#if ENABLE_GCODE_VIEWER
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
m_axes.set_stem_length(0.1f * static_cast<float>(m_bounding_box.max_size()));
#else
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones();
#endif // ENABLE_GCODE_VIEWER
// Let the calee to update the UI.
return true;
@ -282,25 +353,35 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
void Bed3D::calc_bounding_boxes() const
{
m_bounding_box = BoundingBoxf3();
for (const Vec2d& p : m_shape)
{
for (const Vec2d& p : m_shape) {
m_bounding_box.merge(Vec3d(p(0), p(1), 0.0));
}
m_extended_bounding_box = m_bounding_box;
// extend to contain axes
m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones());
#if ENABLE_GCODE_VIEWER
m_extended_bounding_box.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
m_extended_bounding_box.merge(m_extended_bounding_box.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, m_extended_bounding_box.max(2)));
// extend to contain model, if any
BoundingBoxf3 model_bb = m_model.get_bounding_box();
if (model_bb.defined) {
model_bb.translate(m_model_offset);
m_extended_bounding_box.merge(model_bb);
}
#else
m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones());
// extend to contain model, if any
if (!m_model.get_filename().empty())
m_extended_bounding_box.merge(m_model.get_transformed_bounding_box());
#endif // ENABLE_GCODE_VIEWER
}
void Bed3D::calc_triangles(const ExPolygon& poly)
{
Polygons triangles;
poly.triangulate(&triangles);
poly.triangulate_p2t(&triangles);
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true))
printf("Unable to create bed triangles\n");
@ -309,15 +390,13 @@ void Bed3D::calc_triangles(const ExPolygon& poly)
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
{
Polylines axes_lines;
for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0))
{
for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) {
Polyline line;
line.append(Point(x, bed_bbox.min(1)));
line.append(Point(x, bed_bbox.max(1)));
axes_lines.push_back(line);
}
for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0))
{
for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) {
Polyline line;
line.append(Point(bed_bbox.min(0), y));
line.append(Point(bed_bbox.max(0), y));
@ -335,6 +414,7 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
printf("Unable to create bed grid lines\n");
}
#if !ENABLE_GCODE_VIEWER
static std::string system_print_bed_model(const Preset &preset)
{
std::string out;
@ -352,23 +432,25 @@ static std::string system_print_bed_texture(const Preset &preset)
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
return out;
}
#endif // !ENABLE_GCODE_VIEWER
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
{
auto bundle = wxGetApp().preset_bundle;
if (bundle != nullptr)
{
if (bundle != nullptr) {
const Preset* curr = &bundle->printers.get_selected_preset();
while (curr != nullptr)
{
if (curr->config.has("bed_shape"))
{
if (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)
{
while (curr != nullptr) {
if (curr->config.has("bed_shape")) {
if (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values) {
#if ENABLE_GCODE_VIEWER
std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
#else
std::string model_filename = system_print_bed_model(*curr);
std::string texture_filename = system_print_bed_texture(*curr);
#endif // ENABLE_GCODE_VIEWER
if (!model_filename.empty() && !texture_filename.empty())
return std::make_tuple(System, model_filename, texture_filename);
return { System, model_filename, texture_filename };
}
}
@ -376,7 +458,7 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
}
}
return std::make_tuple(Custom, "", "");
return { Custom, "", "" };
}
void Bed3D::render_axes() const
@ -396,26 +478,21 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) co
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
{
if (m_texture_filename.empty())
{
if (m_texture_filename.empty()) {
m_texture.reset();
render_default(bottom);
return;
}
if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename))
{
if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename)) {
m_texture.reset();
if (boost::algorithm::iends_with(m_texture_filename, ".svg"))
{
if (boost::algorithm::iends_with(m_texture_filename, ".svg")) {
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename))
{
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8))
{
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
render_default(bottom);
return;
}
@ -423,19 +500,15 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size))
{
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
render_default(bottom);
return;
}
}
else if (boost::algorithm::iends_with(m_texture_filename, ".png"))
{
}
else if (boost::algorithm::iends_with(m_texture_filename, ".png")) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename))
{
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false))
{
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) {
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) {
render_default(bottom);
return;
}
@ -443,20 +516,17 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true))
{
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
render_default(bottom);
return;
}
}
else
{
else {
render_default(bottom);
return;
}
}
else if (m_texture.unsent_compressed_data_available())
{
else if (m_texture.unsent_compressed_data_available()) {
// sends to gpu the already available compressed levels of the main texture
m_texture.send_compressed_data_to_gpu();
@ -468,19 +538,14 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
}
if (m_triangles.get_vertices_count() > 0)
{
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
if (m_triangles.get_vertices_count() > 0) {
GLShaderProgram* shader = wxGetApp().get_shader("printbed");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("transparent_background", bottom);
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
if (m_shader.is_initialized())
{
m_shader.start_using();
m_shader.set_uniform("transparent_background", bottom);
m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
if (m_vbo_id == 0)
{
if (m_vbo_id == 0) {
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
@ -498,8 +563,8 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
GLint position_id = shader->get_attrib_location("v_position");
GLint tex_coords_id = shader->get_attrib_location("v_tex_coords");
// show the temporary texture while no compressed data is available
GLuint tex_id = (GLuint)m_temp_texture.get_id();
@ -509,13 +574,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
if (position_id != -1)
{
if (position_id != -1) {
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
if (tex_coords_id != -1) {
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
}
@ -537,7 +600,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
m_shader.stop_using();
shader->stop_using();
}
}
}
@ -547,29 +610,41 @@ void Bed3D::render_model() const
if (m_model_filename.empty())
return;
if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename))
{
if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename)) {
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
shift(2) = -0.03;
#if ENABLE_GCODE_VIEWER
m_model_offset = shift;
#else
m_model.set_offset(shift);
#endif // ENABLE_GCODE_VIEWER
// update extended bounding box
calc_bounding_boxes();
}
if (!m_model.get_filename().empty())
{
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
if (!m_model.get_filename().empty()) {
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
#if ENABLE_GCODE_VIEWER
shader->set_uniform("uniform_color", m_model_color);
::glPushMatrix();
::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2));
#endif // ENABLE_GCODE_VIEWER
m_model.render();
#if ENABLE_GCODE_VIEWER
::glPopMatrix();
#endif // ENABLE_GCODE_VIEWER
shader->stop_using();
}
}
}
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const
{
if (m_texture_filename.empty() && m_model_filename.empty())
{
if (m_texture_filename.empty() && m_model_filename.empty()) {
render_default(bottom);
return;
}
@ -586,8 +661,7 @@ void Bed3D::render_default(bool bottom) const
m_texture.reset();
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
if (triangles_vcount > 0) {
bool has_model = !m_model.get_filename().empty();
glsafe(::glEnable(GL_DEPTH_TEST));
@ -596,11 +670,14 @@ void Bed3D::render_default(bool bottom) const
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
if (!has_model && !bottom)
{
if (!has_model && !bottom) {
// draw background
glsafe(::glDepthMask(GL_FALSE));
#if ENABLE_GCODE_VIEWER
glsafe(::glColor4fv(m_model_color.data()));
#else
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
#endif // ENABLE_GCODE_VIEWER
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
@ -608,11 +685,11 @@ void Bed3D::render_default(bool bottom) const
}
// draw grid
glsafe(::glLineWidth(3.0f * m_scale_factor));
glsafe(::glLineWidth(1.5f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f));
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f));
else
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
@ -624,8 +701,7 @@ void Bed3D::render_default(bool bottom) const
void Bed3D::reset()
{
if (m_vbo_id > 0)
{
if (m_vbo_id > 0) {
glsafe(::glDeleteBuffers(1, &m_vbo_id));
m_vbo_id = 0;
}

View file

@ -3,12 +3,19 @@
#include "GLTexture.hpp"
#include "3DScene.hpp"
#include "GLShader.hpp"
#if ENABLE_GCODE_VIEWER
#include "GLModel.hpp"
#endif // ENABLE_GCODE_VIEWER
#include <tuple>
#if ENABLE_GCODE_VIEWER
#include <array>
#endif // ENABLE_GCODE_VIEWER
#if !ENABLE_GCODE_VIEWER
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
#endif // !ENABLE_GCODE_VIEWER
namespace Slic3r {
namespace GUI {
@ -45,22 +52,53 @@ public:
class Bed3D
{
#if ENABLE_GCODE_VIEWER
class Axes
{
public:
static const float DefaultStemRadius;
static const float DefaultStemLength;
static const float DefaultTipRadius;
static const float DefaultTipLength;
private:
#else
struct Axes
{
static const double Radius;
static const double ArrowBaseRadius;
static const double ArrowLength;
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
Vec3d m_origin{ Vec3d::Zero() };
float m_stem_length{ DefaultStemLength };
mutable GLModel m_arrow;
public:
#else
Vec3d origin;
Vec3d length;
GLUquadricObj* m_quadric;
#endif // ENABLE_GCODE_VIEWER
#if !ENABLE_GCODE_VIEWER
Axes();
~Axes();
#endif // !ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
const Vec3d& get_origin() const { return m_origin; }
void set_origin(const Vec3d& origin) { m_origin = origin; }
void set_stem_length(float length);
float get_total_length() const { return m_stem_length + DefaultTipLength; }
#endif // ENABLE_GCODE_VIEWER
void render() const;
#if !ENABLE_GCODE_VIEWER
private:
void render_axis(double length) const;
#endif // !ENABLE_GCODE_VIEWER
};
public:
@ -82,10 +120,15 @@ private:
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
mutable GLTexture m_texture;
#if ENABLE_GCODE_VIEWER
mutable GLModel m_model;
mutable Vec3d m_model_offset{ Vec3d::Zero() };
std::array<float, 4> m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f };
#else
mutable GLBed m_model;
#endif // ENABLE_GCODE_VIEWER
// temporary texture shown until the main texture has still no levels compressed
mutable GLTexture m_temp_texture;
mutable Shader m_shader;
mutable unsigned int m_vbo_id;
Axes m_axes;
@ -101,7 +144,7 @@ public:
const Pointfs& get_shape() const { return m_shape; }
// Return true if the bed shape changed, so the calee will update the UI.
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
const BoundingBoxf3& get_bounding_box(bool extended) const {
return extended ? m_extended_bounding_box : m_bounding_box;

View file

@ -7,19 +7,24 @@
#endif // ENABLE_SMOOTH_NORMALS
#include "3DScene.hpp"
#if ENABLE_ENVIRONMENT_MAP
#include "GLShader.hpp"
#include "GUI_App.hpp"
#if ENABLE_ENVIRONMENT_MAP
#include "Plater.hpp"
#endif // ENABLE_ENVIRONMENT_MAP
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/Geometry.hpp"
#if !ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/PreviewData.hpp"
#endif // !ENABLE_GCODE_VIEWER
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Slicing.hpp"
#if !ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/Analyzer.hpp"
#endif // !ENABLE_GCODE_VIEWER
#include "slic3r/GUI/BitmapCache.hpp"
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Utils.hpp"
@ -442,7 +447,6 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
bounding_box().transformed(trafo);
}
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@ -747,6 +751,10 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
{
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
if (shader == nullptr)
return;
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@ -757,80 +765,32 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
GLint current_program_id;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id));
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1;
GLint clipping_plane_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "clipping_plane") : -1;
GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1;
GLint print_box_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.actived") : -1;
GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
shader->set_uniform("print_box.min", m_print_box_min, 3);
shader->set_uniform("print_box.max", m_print_box_max, 3);
shader->set_uniform("z_range", m_z_range, 2);
shader->set_uniform("clipping_plane", m_clipping_plane, 4);
#if ENABLE_SLOPE_RENDERING
GLint slope_active_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.actived") : -1;
GLint slope_normal_matrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.volume_world_normal_matrix") : -1;
GLint slope_z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "slope.z_range") : -1;
#endif // ENABLE_SLOPE_RENDERING
#if ENABLE_ENVIRONMENT_MAP
GLint use_environment_tex_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "use_environment_tex") : -1;
#endif // ENABLE_ENVIRONMENT_MAP
glcheck();
if (print_box_min_id != -1)
glsafe(::glUniform3fv(print_box_min_id, 1, (const GLfloat*)m_print_box_min));
if (print_box_max_id != -1)
glsafe(::glUniform3fv(print_box_max_id, 1, (const GLfloat*)m_print_box_max));
if (z_range_id != -1)
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)m_z_range));
if (clipping_plane_id != -1)
glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)m_clipping_plane));
#if ENABLE_SLOPE_RENDERING
if (slope_z_range_id != -1)
glsafe(::glUniform2fv(slope_z_range_id, 1, (const GLfloat*)m_slope.z_range.data()));
shader->set_uniform("slope.z_range", m_slope.z_range);
#endif // ENABLE_SLOPE_RENDERING
#if ENABLE_ENVIRONMENT_MAP
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
bool use_environment_texture = current_program_id > 0 && environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
if (use_environment_tex_id != -1)
{
glsafe(::glUniform1i(use_environment_tex_id, use_environment_texture ? 1 : 0));
if (use_environment_texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
}
bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
shader->set_uniform("use_environment_tex", use_environment_texture);
if (use_environment_texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
#endif // ENABLE_ENVIRONMENT_MAP
glcheck();
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
#if ENABLE_SLOPE_RENDERING
if (color_id >= 0)
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)volume.first->render_color));
else
glsafe(::glColor4fv(volume.first->render_color));
if (print_box_active_id != -1)
glsafe(::glUniform1i(print_box_active_id, volume.first->shader_outside_printer_detection_enabled ? 1 : 0));
if (print_box_worldmatrix_id != -1)
glsafe(::glUniformMatrix4fv(print_box_worldmatrix_id, 1, GL_FALSE, (const GLfloat*)volume.first->world_matrix().cast<float>().data()));
if (slope_active_id != -1)
glsafe(::glUniform1i(slope_active_id, m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower ? 1 : 0));
if (slope_normal_matrix_id != -1)
{
Matrix3f normal_matrix = volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>();
glsafe(::glUniformMatrix3fv(slope_normal_matrix_id, 1, GL_FALSE, (const GLfloat*)normal_matrix.data()));
}
shader->set_uniform("uniform_color", volume.first->render_color, 4);
shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
volume.first->render();
#else
@ -839,7 +799,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
}
#if ENABLE_ENVIRONMENT_MAP
if (use_environment_tex_id != -1 && use_environment_texture)
if (use_environment_texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
#endif // ENABLE_ENVIRONMENT_MAP
@ -1057,6 +1017,7 @@ bool GLVolumeCollection::has_toolpaths_to_export() const
return false;
}
#if !ENABLE_GCODE_VIEWER
void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const
{
if (filename == nullptr)
@ -1338,6 +1299,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const
fclose(fp);
}
#endif // !ENABLE_GCODE_VIEWER
// caller is responsible for supplying NO lines with zero length
static void thick_lines_to_indexed_vertex_array(
@ -1985,6 +1947,7 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
thick_point_to_verts(point, width, height, volume);
}
#if !ENABLE_GCODE_VIEWER
GLModel::GLModel()
: m_filename("")
{
@ -2040,6 +2003,10 @@ void GLModel::reset()
void GLModel::render() const
{
GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
if (shader == nullptr)
return;
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@ -2047,17 +2014,8 @@ void GLModel::render() const
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
GLint current_program_id;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id));
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
glcheck();
#if ENABLE_SLOPE_RENDERING
if (color_id >= 0)
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)m_volume.render_color));
else
glsafe(::glColor4fv(m_volume.render_color));
shader->set_uniform("uniform_color", m_volume.render_color, 4);
m_volume.render();
#else
m_volume.render(color_id, -1, -1);
@ -2274,5 +2232,6 @@ bool GLBed::on_init_from_file(const std::string& filename)
return true;
}
#endif // !ENABLE_GCODE_VIEWER
} // namespace Slic3r

View file

@ -26,12 +26,6 @@
#endif // HAS_GLSAFE
namespace Slic3r {
namespace GUI {
class Bed3D;
struct Camera;
class GLToolbar;
} // namespace GUI
class SLAPrintObject;
enum SLAPrintObjectStep : unsigned int;
class DynamicPrintConfig;
@ -603,8 +597,10 @@ public:
std::string log_memory_info() const;
bool has_toolpaths_to_export() const;
#if !ENABLE_GCODE_VIEWER
// Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format
void export_toolpaths_to_obj(const char* filename) const;
#endif // !ENABLE_GCODE_VIEWER
private:
GLVolumeCollection(const GLVolumeCollection &other);
@ -613,6 +609,7 @@ private:
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = nullptr);
#if !ENABLE_GCODE_VIEWER
class GLModel
{
protected:
@ -672,6 +669,7 @@ class GLBed : public GLModel
protected:
bool on_init_from_file(const std::string& filename) override;
};
#endif // !ENABLE_GCODE_VIEWER
struct _3DScene
{

View file

@ -19,7 +19,9 @@
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/GCode/PostProcessor.hpp"
#if !ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/PreviewData.hpp"
#endif // !ENABLE_GCODE_VIEWER
#include "libslic3r/Format/SL1.hpp"
#include "libslic3r/libslic3r.h"
@ -92,7 +94,11 @@ void BackgroundSlicingProcess::process_fff()
wxCommandEvent evt(m_event_slicing_completed_id);
evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psBrim).timestamp));
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
#if ENABLE_GCODE_VIEWER
m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, m_thumbnail_cb);
#else
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
#endif // ENABLE_GCODE_VIEWER
if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
@ -382,6 +388,17 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
assert(m_print != nullptr);
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
Print::ApplyStatus invalidated = m_print->apply(model, config);
#if ENABLE_GCODE_VIEWER
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
!this->m_fff_print->is_step_done(psGCodeExport))
{
// Some FFF status was invalidated, and the G-code was not exported yet.
// Let the G-code preview UI know that the final G-code preview is not valid.
// In addition, this early memory deallocation reduces memory footprint.
if (m_gcode_result != nullptr)
m_gcode_result->reset();
}
#else
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) {
// Some FFF status was invalidated, and the G-code was not exported yet.
@ -389,6 +406,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
// In addition, this early memory deallocation reduces memory footprint.
m_gcode_preview_data->reset();
}
#endif // ENABLE_GCODE_VIEWER
return invalidated;
}

View file

@ -11,6 +11,9 @@
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Format/SL1.hpp"
#include "slic3r/Utils/PrintHost.hpp"
#if ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/GCodeProcessor.hpp"
#endif // ENABLE_GCODE_VIEWER
namespace boost { namespace filesystem { class path; } }
@ -18,7 +21,9 @@ namespace boost { namespace filesystem { class path; } }
namespace Slic3r {
class DynamicPrintConfig;
#if !ENABLE_GCODE_VIEWER
class GCodePreviewData;
#endif // !ENABLE_GCODE_VIEWER
class Model;
class SLAPrint;
@ -50,8 +55,12 @@ public:
void set_fff_print(Print *print) { m_fff_print = print; }
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
#if ENABLE_GCODE_VIEWER
void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
#else
void set_gcode_preview_data(GCodePreviewData* gpd) { m_gcode_preview_data = gpd; }
#endif // ENABLE_GCODE_VIEWER
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
// and the background processing will transition into G-code export.
@ -157,12 +166,17 @@ private:
// Non-owned pointers to Print instances.
Print *m_fff_print = nullptr;
SLAPrint *m_sla_print = nullptr;
#if ENABLE_GCODE_VIEWER
// Data structure, to which the G-code export writes its annotations.
GCodeProcessor::Result *m_gcode_result = nullptr;
#else
// Data structure, to which the G-code export writes its annotations.
GCodePreviewData *m_gcode_preview_data = nullptr;
// Callback function, used to write thumbnails into gcode.
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
SL1Archive m_sla_archive;
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
#endif // ENABLE_GCODE_VIEWER
// Callback function, used to write thumbnails into gcode.
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
SL1Archive m_sla_archive;
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
std::string m_temp_output_path;
// Output path provided by the user. The output path may be set even if the slicing is running,
// but once set, it cannot be re-set.

View file

@ -3,6 +3,9 @@
#include "libslic3r/Utils.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "GUI.hpp"
#if ENABLE_GCODE_VIEWER
#include "GUI_Utils.hpp"
#endif // ENABLE_GCODE_VIEWER
#include <boost/filesystem.hpp>
@ -355,6 +358,7 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
}
#if !ENABLE_GCODE_VIEWER
static inline int hex_digit_to_int(const char c)
{
return
@ -362,6 +366,7 @@ static inline int hex_digit_to_int(const char c)
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
#endif // !ENABLE_GCODE_VIEWER
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
{

View file

@ -5,6 +5,7 @@
#include "GUI_App.hpp"
#if ENABLE_CAMERA_STATISTICS
#include "Mouse3DController.hpp"
#include "Plater.hpp"
#endif // ENABLE_CAMERA_STATISTICS
#include <GL/glew.h>

View file

@ -84,6 +84,7 @@ public:
double get_near_z() const { return m_frustrum_zs.first; }
double get_far_z() const { return m_frustrum_zs.second; }
const std::pair<double, double>& get_z_range() const { return m_frustrum_zs; }
double get_fov() const;

View file

@ -1,5 +1,11 @@
#include "libslic3r/libslic3r.h"
#if ENABLE_GCODE_VIEWER
#include "DoubleSlider.hpp"
#include "libslic3r/GCode.hpp"
#else
#include "wxExtensions.hpp"
#include "libslic3r/GCode/PreviewData.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
@ -15,7 +21,9 @@
#include <wx/bmpcbox.h>
#include <wx/statline.h>
#include <wx/dcclient.h>
#if !ENABLE_GCODE_VIEWER
#include <wx/numformatter.h>
#endif // !ENABLE_GCODE_VIEWER
#include <wx/colordlg.h>
#include <cmath>
@ -72,8 +80,13 @@ Control::Control( wxWindow *parent,
if (!is_osx)
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
#if ENABLE_GCODE_VIEWER
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up"));
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down"));
#else
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "thumb_up"));
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "thumb_down"));
#endif // ENABLE_GCODE_VIEWER
m_thumb_size = m_bmp_thumb_lower.GetBmpSize();
m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add");
@ -275,14 +288,14 @@ wxCoord Control::get_position_from_value(const int value)
return wxCoord(SLIDER_MARGIN + int(val*step + 0.5));
}
wxSize Control::get_size()
wxSize Control::get_size() const
{
int w, h;
get_size(&w, &h);
return wxSize(w, h);
}
void Control::get_size(int *w, int *h)
void Control::get_size(int* w, int* h) const
{
GetSize(w, h);
is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim;
@ -302,14 +315,22 @@ double Control::get_double_value(const SelectedSlider& selection)
Info Control::GetTicksValues() const
{
Info custom_gcode_per_print_z;
#if ENABLE_GCODE_VIEWER
std::vector<CustomGCode::Item>& values = custom_gcode_per_print_z.gcodes;
#else
std::vector<Item>& values = custom_gcode_per_print_z.gcodes;
#endif // ENABLE_GCODE_VIEWER
const int val_size = m_values.size();
if (!m_values.empty())
for (const TickCode& tick : m_ticks.ticks) {
if (tick.tick > val_size)
break;
#if ENABLE_GCODE_VIEWER
values.emplace_back(CustomGCode::Item{ m_values[tick.tick], tick.type, tick.extruder, tick.color, tick.extra });
#else
values.emplace_back(Item{m_values[tick.tick], tick.type, tick.extruder, tick.color, tick.extra});
#endif // ENABLE_GCODE_VIEWER
}
if (m_force_mode_apply)
@ -329,7 +350,11 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z)
const bool was_empty = m_ticks.empty();
m_ticks.ticks.clear();
#if ENABLE_GCODE_VIEWER
const std::vector<CustomGCode::Item>& heights = custom_gcode_per_print_z.gcodes;
#else
const std::vector<Item>& heights = custom_gcode_per_print_z.gcodes;
#endif // ENABLE_GCODE_VIEWER
for (auto h : heights) {
auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon());
@ -401,7 +426,15 @@ void Control::draw_focus_rect()
void Control::render()
{
#if ENABLE_GCODE_VIEWER
#ifdef _WIN32
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#else
SetBackgroundColour(GetParent()->GetBackgroundColour());
#endif // _WIN32
#else
SetBackgroundColour(GetParent()->GetBackgroundColour());
#endif // ENABLE_GCODE_VIEWER
draw_focus_rect();
wxPaintDC dc(this);
@ -417,22 +450,22 @@ void Control::render()
// draw line
draw_scroll_line(dc, lower_pos, higher_pos);
//draw color print ticks
// draw color print ticks
draw_ticks(dc);
// draw both sliders
draw_thumbs(dc, lower_pos, higher_pos);
//draw lock/unlock
// draw lock/unlock
draw_one_layer_icon(dc);
//draw revert bitmap (if it's shown)
// draw revert bitmap (if it's shown)
draw_revert_icon(dc);
//draw cog bitmap (if it's shown)
// draw cog bitmap (if it's shown)
draw_cog_icon(dc);
//draw mouse position
// draw mouse position
draw_tick_on_mouse_position(dc);
}
@ -544,10 +577,21 @@ wxString Control::get_label(int tick) const
if (value >= m_values.size())
return "ErrVal";
const wxString str = m_values.empty() ?
wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) :
wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value+1);
#if ENABLE_GCODE_VIEWER
if (m_draw_mode == dmSequentialGCodeView)
return wxString::Format("%d", static_cast<unsigned int>(m_values[value]));
else {
const wxString str = m_values.empty() ?
wxString::Format("%.*f", 2, m_label_koef * value) :
wxString::Format("%.*f", 2, m_values[value]);
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
}
#else
const wxString str = m_values.empty() ?
wxNumberFormatter::ToString(m_label_koef * value, 2, wxNumberFormatter::Style_None) :
wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
return format_wxstr("%1%\n(%2%)", str, m_values.empty() ? value : value + 1);
#endif // ENABLE_GCODE_VIEWER
}
void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const
@ -556,13 +600,36 @@ void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_
const wxString label = get_label(tick);
dc.GetMultiLineTextExtent(label, &text_width, &text_height);
wxPoint text_pos;
if (right_side)
text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) :
wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1);
else
text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) :
wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1);
dc.DrawText(label, text_pos);
if (right_side) {
if (is_horizontal()) {
int width;
int height;
get_size(&width, &height);
int x_right = pos.x + 1 + text_width;
int xx = (x_right < width) ? pos.x + 1 : pos.x - text_width - 1;
text_pos = wxPoint(xx, pos.y + m_thumb_size.x / 2 + 1);
}
else
text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1);
// update text rectangle
m_rect_lower_thumb_text = wxRect(text_pos, wxSize(text_width, text_height));
}
else {
if (is_horizontal()) {
int x = pos.x - text_width - 1;
int xx = (x > 0) ? x : pos.x + 1;
text_pos = wxPoint(xx, pos.y - m_thumb_size.x / 2 - text_height - 1);
}
else
text_pos = wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5 * text_height + 1);
// update text rectangle
m_rect_higher_thumb_text = wxRect(text_pos, wxSize(text_width, text_height));
}
dc.DrawText(label, text_pos);
}
void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
@ -572,6 +639,10 @@ void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider
void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection)
{
#if ENABLE_GCODE_VIEWER
wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x);
wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y);
#else
wxCoord x_draw, y_draw;
if (selection == ssLower) {
if (is_horizontal()) {
@ -583,7 +654,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider
y_draw = pos.y - int(0.5*m_thumb_size.y);
}
}
else{
else {
if (is_horizontal()) {
x_draw = pos.x;
y_draw = pos.y - int(0.5*m_thumb_size.y);
@ -593,6 +664,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider
y_draw = pos.y - int(0.5*m_thumb_size.y);
}
}
#endif // ENABLE_GCODE_VIEWER
dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw);
// Update thumb rect
@ -756,7 +828,15 @@ void Control::draw_colored_band(wxDC& dc)
// don't color a band for MultiExtruder mode
if (m_ticks.empty() || m_mode == MultiExtruder)
{
#if ENABLE_GCODE_VIEWER
#ifdef _WIN32
draw_band(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW), main_band);
#else
draw_band(dc, GetParent()->GetBackgroundColour(), main_band);
#endif // _WIN32
#else
draw_band(dc, GetParent()->GetBackgroundColour(), main_band);
#endif // ENABLE_GCODE_VIEWER
return;
}
@ -788,6 +868,11 @@ void Control::draw_colored_band(wxDC& dc)
void Control::draw_one_layer_icon(wxDC& dc)
{
#if ENABLE_GCODE_VIEWER
if (m_draw_mode == dmSequentialGCodeView)
return;
#endif // ENABLE_GCODE_VIEWER
const wxBitmap& icon = m_is_one_layer ?
m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() :
m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp();
@ -829,8 +914,20 @@ void Control::draw_cog_icon(wxDC& dc)
get_size(&width, &height);
wxCoord x_draw, y_draw;
is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2;
is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2;
#if ENABLE_GCODE_VIEWER
if (m_draw_mode == dmSequentialGCodeView)
{
is_horizontal() ? x_draw = width - 2 : x_draw = 0.5 * width - 0.5 * m_cog_icon_dim;
is_horizontal() ? y_draw = 0.5 * height - 0.5 * m_cog_icon_dim : y_draw = height - 2;
}
else
{
#endif // ENABLE_GCODE_VIEWER
is_horizontal() ? x_draw = width - 2 : x_draw = width - m_cog_icon_dim - 2;
is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2;
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw);
@ -838,9 +935,12 @@ void Control::draw_cog_icon(wxDC& dc)
m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim);
}
void Control::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection)
void Control::update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection)
{
const wxRect& rect = wxRect(begin_x, begin_y + (selection == ssLower ? int(m_thumb_size.y * 0.5) : 0), m_thumb_size.x, int(m_thumb_size.y*0.5));
const wxRect rect = is_horizontal() ?
wxRect(begin_x + (selection == ssHigher ? m_thumb_size.x / 2 : 0), begin_y, m_thumb_size.x / 2, m_thumb_size.y) :
wxRect(begin_x, begin_y + (selection == ssLower ? m_thumb_size.y / 2 : 0), m_thumb_size.x, m_thumb_size.y / 2);
if (selection == ssLower)
m_rect_lower_thumb = rect;
else
@ -968,10 +1068,19 @@ wxString Control::get_tooltip(int tick/*=-1*/)
if (m_focus == fiRevertIcon)
return _L("Discard all custom changes");
if (m_focus == fiCogIcon)
return m_mode == MultiAsSingle ?
#if ENABLE_GCODE_VIEWER
{
if (m_draw_mode == dmSequentialGCodeView)
return _L("Jump to move") + " (Shift + G)";
else
#endif // ENABLE_GCODE_VIEWER
return m_mode == MultiAsSingle ?
GUI::from_u8((boost::format(_u8L("Jump to height %s or "
"Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) :
_L("Jump to height") + " (Shift + G)";
"Set extruder sequence for the entire print")) % " (Shift + G)\n").str()) :
_L("Jump to height") + " (Shift + G)";
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
if (m_focus == fiColorBand)
return m_mode != SingleExtruder ? "" :
_L("Edit current color - Right click the colored slider segment");
@ -1099,6 +1208,14 @@ void Control::OnMotion(wxMouseEvent& event)
else if (m_mode == SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) &&
get_edited_tick_for_position(pos) >= 0 )
m_focus = fiColorBand;
else if (is_point_in_rect(pos, m_rect_lower_thumb))
m_focus = fiLowerThumb;
else if (is_point_in_rect(pos, m_rect_higher_thumb))
m_focus = fiHigherThumb;
else if (is_point_in_rect(pos, m_rect_lower_thumb_text))
m_focus = fiLowerThumbText;
else if (is_point_in_rect(pos, m_rect_higher_thumb_text))
m_focus = fiHigherThumbText;
else {
m_focus = fiTick;
tick = get_tick_near_point(pos);
@ -1223,7 +1340,11 @@ void Control::OnLeftUp(wxMouseEvent& event)
if (m_mode == MultiAsSingle && m_draw_mode == dmRegular)
show_cog_icon_context_menu();
else
#if ENABLE_GCODE_VIEWER
jump_to_value();
#else
jump_to_print_z();
#endif // ENABLE_GCODE_VIEWER
break;
case maOneLayerIconClick:
switch_one_layer_mode();
@ -1262,6 +1383,15 @@ void Control::move_current_thumb(const bool condition)
if (is_horizontal())
delta *= -1;
// accelerators
int accelerator = 0;
if (wxGetKeyState(WXK_SHIFT))
accelerator += 5;
if (wxGetKeyState(WXK_CONTROL))
accelerator += 5;
if (accelerator > 0)
delta *= accelerator;
if (m_selection == ssLower) {
m_lower_value -= delta;
correct_lower_value();
@ -1295,12 +1425,32 @@ void Control::OnWheel(wxMouseEvent& event)
ssLower : ssHigher;
}
#if ENABLE_GCODE_VIEWER
move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0);
#else
move_current_thumb(event.GetWheelRotation() > 0);
#endif // ENABLE_GCODE_VIEWER
}
void Control::OnKeyDown(wxKeyEvent &event)
{
const int key = event.GetKeyCode();
#if ENABLE_GCODE_VIEWER
if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) {
// OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice.
// To avoid this case we should suppress second add_tick() call.
m_ticks.suppress_plus(true);
add_current_tick(true);
}
else if (m_draw_mode != dmSequentialGCodeView && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK)) {
// OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice.
// To avoid this case we should suppress second delete_tick() call.
m_ticks.suppress_minus(true);
delete_current_tick();
}
else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT)
UseDefaultColors(false);
#else
if (key == WXK_NUMPAD_ADD) {
// OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice.
// To avoid this case we should suppress second add_tick() call.
@ -1315,22 +1465,37 @@ void Control::OnKeyDown(wxKeyEvent &event)
}
else if (event.GetKeyCode() == WXK_SHIFT)
UseDefaultColors(false);
#endif // ENABLE_GCODE_VIEWER
else if (is_horizontal())
{
if (key == WXK_LEFT || key == WXK_RIGHT)
move_current_thumb(key == WXK_LEFT);
else if (key == WXK_UP || key == WXK_DOWN) {
m_selection = key == WXK_UP ? ssHigher : ssLower;
Refresh();
#if ENABLE_GCODE_VIEWER
if (m_is_focused)
{
#endif // ENABLE_GCODE_VIEWER
if (key == WXK_LEFT || key == WXK_RIGHT)
move_current_thumb(key == WXK_LEFT);
else if (key == WXK_UP || key == WXK_DOWN) {
m_selection = key == WXK_UP ? ssHigher : ssLower;
Refresh();
}
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
}
}
else {
if (key == WXK_LEFT || key == WXK_RIGHT) {
m_selection = key == WXK_LEFT ? ssHigher : ssLower;
Refresh();
#if ENABLE_GCODE_VIEWER
if (m_is_focused)
{
#endif // ENABLE_GCODE_VIEWER
if (key == WXK_LEFT || key == WXK_RIGHT) {
m_selection = key == WXK_LEFT ? ssHigher : ssLower;
Refresh();
}
else if (key == WXK_UP || key == WXK_DOWN)
move_current_thumb(key == WXK_UP);
#if ENABLE_GCODE_VIEWER
}
else if (key == WXK_UP || key == WXK_DOWN)
move_current_thumb(key == WXK_UP);
#endif // ENABLE_GCODE_VIEWER
}
event.Skip(); // !Needed to have EVT_CHAR generated as well
@ -1351,16 +1516,27 @@ void Control::OnKeyUp(wxKeyEvent &event)
void Control::OnChar(wxKeyEvent& event)
{
const int key = event.GetKeyCode();
if (key == '+' && !m_ticks.suppressed_plus()) {
add_current_tick(true);
m_ticks.suppress_plus(false);
}
else if (key == '-' && !m_ticks.suppressed_minus()) {
delete_current_tick();
m_ticks.suppress_minus(false);
#if ENABLE_GCODE_VIEWER
if (m_draw_mode != dmSequentialGCodeView)
{
#endif // ENABLE_GCODE_VIEWER
if (key == '+' && !m_ticks.suppressed_plus()) {
add_current_tick(true);
m_ticks.suppress_plus(false);
}
else if (key == '-' && !m_ticks.suppressed_minus()) {
delete_current_tick();
m_ticks.suppress_minus(false);
}
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
if (key == 'G')
#if ENABLE_GCODE_VIEWER
jump_to_value();
#else
jump_to_print_z();
#endif // ENABLE_GCODE_VIEWER
}
void Control::OnRightDown(wxMouseEvent& event)
@ -1550,7 +1726,11 @@ void Control::show_cog_icon_context_menu()
wxMenu menu;
append_menu_item(&menu, wxID_ANY, _(L("Jump to height")) + " (Shift+G)", "",
[this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu);
#if ENABLE_GCODE_VIEWER
[this](wxCommandEvent&) { jump_to_value(); }, "", & menu);
#else
[this](wxCommandEvent&) { jump_to_print_z(); }, "", &menu);
#endif // ENABLE_GCODE_VIEWER
append_menu_item(&menu, wxID_ANY, _(L("Set extruder sequence for the entire print")), "",
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
@ -1670,11 +1850,21 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height)
return into_u8(dlg.GetValue());
}
#if ENABLE_GCODE_VIEWER
static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode)
#else
static double get_print_z_to_jump(double active_print_z, double min_z, double max_z)
#endif // ENABLE_GCODE_VIEWER
{
#if ENABLE_GCODE_VIEWER
wxString msg_text = (mode == dmSequentialGCodeView) ? _L("Enter the move you want to jump to") + ":" : _L("Enter the height you want to jump to") + ":";
wxString msg_header = (mode == dmSequentialGCodeView) ? _L("Jump to move") : _L("Jump to height");
wxString msg_in = GUI::double_to_string(active_value);
#else
wxString msg_text = _(L("Enter the height you want to jump to")) + ":";
wxString msg_header = _(L("Jump to height"));
wxString msg_in = GUI::double_to_string(active_print_z);
#endif // ENABLE_GCODE_VIEWER
// get custom gcode
wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle);
@ -1883,6 +2073,23 @@ void Control::edit_extruder_sequence()
post_ticks_changed_event(ToolChange);
}
#if ENABLE_GCODE_VIEWER
void Control::jump_to_value()
{
double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value],
m_values[m_min_value], m_values[m_max_value], m_draw_mode);
if (value < 0.0)
return;
auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon());
int tick_value = it - m_values.begin();
if (m_selection == ssLower)
SetLowerValue(tick_value);
else
SetHigherValue(tick_value);
}
#else
void Control::jump_to_print_z()
{
double print_z = get_print_z_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value],
@ -1898,6 +2105,7 @@ void Control::jump_to_print_z()
else
SetHigherValue(tick_value);
}
#endif // ENABLE_GCODE_VIEWER
void Control::post_ticks_changed_event(Type type /*= Custom*/)
{
@ -1968,7 +2176,11 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int
{
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors)
{
#if ENABLE_GCODE_VIEWER
const std::vector<std::string>& colors = ColorPrintColors::get();
#else
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
#endif // ENABLE_GCODE_VIEWER
if (ticks.empty())
return colors[0];
m_default_color_idx++;

View file

@ -4,7 +4,9 @@
#include "libslic3r/CustomGCode.hpp"
#include "wxExtensions.hpp"
#if !ENABLE_GCODE_VIEWER
#include <wx/wx.h>
#endif // !ENABLE_GCODE_VIEWER
#include <wx/window.h>
#include <wx/control.h>
#include <wx/dc.h>
@ -42,6 +44,10 @@ enum FocusedItem {
fiCogIcon,
fiColorBand,
fiActionIcon,
fiLowerThumb,
fiHigherThumb,
fiLowerThumbText,
fiHigherThumbText,
fiTick
};
@ -73,6 +79,9 @@ enum DrawMode
dmRegular,
dmSlaPrint,
dmSequentialFffPrint,
#if ENABLE_GCODE_VIEWER
dmSequentialGCodeView,
#endif // ENABLE_GCODE_VIEWER
};
struct TickCode
@ -210,6 +219,9 @@ public:
void SetTicksValues(const Info &custom_gcode_per_print_z);
void SetDrawMode(bool is_sla_print, bool is_sequential_print);
#if ENABLE_GCODE_VIEWER
void SetDrawMode(DrawMode mode) { m_draw_mode = mode; }
#endif // ENABLE_GCODE_VIEWER
void SetManipulationMode(Mode mode) { m_mode = mode; }
Mode GetManipulationMode() const { return m_mode; }
@ -222,7 +234,7 @@ public:
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 OnPaint(wxPaintEvent& ) { render(); }
void OnLeftDown(wxMouseEvent& event);
void OnMotion(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
@ -246,7 +258,11 @@ public:
void discard_all_thicks();
void move_current_thumb_to_pos(wxPoint pos);
void edit_extruder_sequence();
#if ENABLE_GCODE_VIEWER
void jump_to_value();
#else
void jump_to_print_z();
#endif // ENABLE_GCODE_VIEWER
void show_add_context_menu();
void show_edit_context_menu();
void show_cog_icon_context_menu();
@ -272,7 +288,7 @@ protected:
void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side = true) const;
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection);
void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection);
bool detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
@ -290,8 +306,8 @@ private:
int get_value_from_position(const wxCoord x, const wxCoord y);
int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); }
wxCoord get_position_from_value(const int value);
wxSize get_size();
void get_size(int *w, int *h);
wxSize get_size() const;
void get_size(int* w, int* h) const;
double get_double_value(const SelectedSlider& selection);
wxString get_tooltip(int tick = -1);
int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange);
@ -348,6 +364,8 @@ private:
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
mutable wxRect m_rect_lower_thumb_text;
mutable wxRect m_rect_higher_thumb_text;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxRect m_rect_revert_icon;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,459 @@
#ifndef slic3r_GCodeViewer_hpp_
#define slic3r_GCodeViewer_hpp_
#if ENABLE_GCODE_VIEWER
#include "3DScene.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GLModel.hpp"
#include <float.h>
namespace Slic3r {
class Print;
class TriangleMesh;
namespace GUI {
class GCodeViewer
{
using Color = std::array<float, 3>;
static const std::vector<Color> Extrusion_Role_Colors;
static const std::vector<Color> Options_Colors;
static const std::vector<Color> Travel_Colors;
static const std::vector<Color> Range_Colors;
enum class EOptionsColors : unsigned char
{
Retractions,
Unretractions,
ToolChanges,
ColorChanges,
PausePrints,
CustomGCodes
};
// vbo buffer containing vertices data used to rendder a specific toolpath type
struct VBuffer
{
enum class EFormat : unsigned char
{
// vertex format: 3 floats -> position.x|position.y|position.z
Position,
// vertex format: 4 floats -> position.x|position.y|position.z|normal.x
PositionNormal1,
// vertex format: 6 floats -> position.x|position.y|position.z|normal.x|normal.y|normal.z
PositionNormal3
};
EFormat format{ EFormat::Position };
// vbo id
unsigned int id{ 0 };
// count of vertices, updated after data are sent to gpu
size_t count{ 0 };
size_t data_size_bytes() const { return count * vertex_size_bytes(); }
size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
size_t position_offset_floats() const { return 0; }
size_t position_offset_size() const { return position_offset_floats() * sizeof(float); }
size_t position_size_floats() const
{
switch (format)
{
case EFormat::Position:
case EFormat::PositionNormal3: { return 3; }
case EFormat::PositionNormal1: { return 4; }
default: { return 0; }
}
}
size_t position_size_bytes() const { return position_size_floats() * sizeof(float); }
size_t normal_offset_floats() const
{
switch (format)
{
case EFormat::Position:
case EFormat::PositionNormal1: { return 0; }
case EFormat::PositionNormal3: { return 3; }
default: { return 0; }
}
}
size_t normal_offset_size() const { return normal_offset_floats() * sizeof(float); }
size_t normal_size_floats() const {
switch (format)
{
default:
case EFormat::Position:
case EFormat::PositionNormal1: { return 0; }
case EFormat::PositionNormal3: { return 3; }
}
}
size_t normal_size_bytes() const { return normal_size_floats() * sizeof(float); }
void reset();
};
// ibo buffer containing indices data (lines/triangles) used to render a specific toolpath type
struct IBuffer
{
// ibo id
unsigned int id{ 0 };
// count of indices, updated after data are sent to gpu
size_t count{ 0 };
void reset();
};
// Used to identify different toolpath sub-types inside a IBuffer
struct Path
{
struct Endpoint
{
// index into the indices buffer
unsigned int i_id{ 0u };
// sequential id
unsigned int s_id{ 0u };
Vec3f position{ Vec3f::Zero() };
};
EMoveType type{ EMoveType::Noop };
ExtrusionRole role{ erNone };
Endpoint first;
Endpoint last;
float delta_extruder{ 0.0f };
float height{ 0.0f };
float width{ 0.0f };
float feedrate{ 0.0f };
float fan_speed{ 0.0f };
float volumetric_rate{ 0.0f };
unsigned char extruder_id{ 0 };
unsigned char cp_color_id{ 0 };
bool matches(const GCodeProcessor::MoveVertex& move) const;
size_t vertices_count() const { return last.s_id - first.s_id + 1; }
bool contains(unsigned int id) const { return first.s_id <= id && id <= last.s_id; }
};
// Used to batch the indices needed to render paths
struct RenderPath
{
Color color;
size_t path_id;
std::vector<unsigned int> sizes;
std::vector<size_t> offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements())
};
// buffer containing data for rendering a specific toolpath type
struct TBuffer
{
enum class ERenderPrimitiveType : unsigned char
{
Point,
Line,
Triangle
};
ERenderPrimitiveType render_primitive_type;
VBuffer vertices;
IBuffer indices;
std::string shader;
std::vector<Path> paths;
std::vector<RenderPath> render_paths;
bool visible{ false };
void reset();
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int i_id, unsigned int s_id);
unsigned int indices_per_segment() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 42; } // 3 indices x 14 triangles
default: { return 0; }
}
}
};
// helper to render shells
struct Shells
{
GLVolumeCollection volumes;
bool visible{ false };
};
// helper to render extrusion paths
struct Extrusions
{
struct Range
{
float min;
float max;
unsigned int count;
Range() { reset(); }
void update_from(const float value) {
if (value != max && value != min)
++count;
min = std::min(min, value);
max = std::max(max, value);
}
void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; }
float step_size() const { return (max - min) / (static_cast<float>(Range_Colors.size()) - 1.0f); }
Color get_color_at(float value) const;
};
struct Ranges
{
// Color mapping by layer height.
Range height;
// Color mapping by extrusion width.
Range width;
// Color mapping by feedrate.
Range feedrate;
// Color mapping by fan speed.
Range fan_speed;
// Color mapping by volumetric extrusion rate.
Range volumetric_rate;
void reset() {
height.reset();
width.reset();
feedrate.reset();
fan_speed.reset();
volumetric_rate.reset();
}
};
unsigned int role_visibility_flags{ 0 };
Ranges ranges;
void reset_role_visibility_flags() {
role_visibility_flags = 0;
for (unsigned int i = 0; i < erCount; ++i) {
role_visibility_flags |= 1 << i;
}
}
void reset_ranges() { ranges.reset(); }
};
#if ENABLE_GCODE_VIEWER_STATISTICS
struct Statistics
{
// times
long long results_time{ 0 };
long long load_time{ 0 };
long long refresh_time{ 0 };
long long refresh_paths_time{ 0 };
// opengl calls
long long gl_multi_points_calls_count{ 0 };
long long gl_multi_lines_calls_count{ 0 };
long long gl_multi_triangles_calls_count{ 0 };
// memory
long long results_size{ 0 };
long long vertices_gpu_size{ 0 };
long long indices_gpu_size{ 0 };
long long paths_size{ 0 };
long long render_paths_size{ 0 };
// others
long long travel_segments_count{ 0 };
long long extrude_segments_count{ 0 };
void reset_all() {
reset_times();
reset_opengl();
reset_sizes();
reset_counters();
}
void reset_times() {
results_time = 0;
load_time = 0;
refresh_time = 0;
refresh_paths_time = 0;
}
void reset_opengl() {
gl_multi_points_calls_count = 0;
gl_multi_lines_calls_count = 0;
gl_multi_triangles_calls_count = 0;
}
void reset_sizes() {
results_size = 0;
vertices_gpu_size = 0;
indices_gpu_size = 0;
paths_size = 0;
render_paths_size = 0;
}
void reset_counters() {
travel_segments_count = 0;
extrude_segments_count = 0;
}
};
#endif // ENABLE_GCODE_VIEWER_STATISTICS
public:
struct SequentialView
{
class Marker
{
GLModel m_model;
Vec3f m_world_position;
Transform3f m_world_transform;
float m_z_offset{ 0.5f };
std::array<float, 4> m_color{ 1.0f, 1.0f, 1.0f, 1.0f };
bool m_visible{ false };
public:
void init();
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
void set_world_position(const Vec3f& position);
void set_color(const std::array<float, 4>& color) { m_color = color; }
bool is_visible() const { return m_visible; }
void set_visible(bool visible) { m_visible = visible; }
void render() const;
};
struct Endpoints
{
unsigned int first{ 0 };
unsigned int last{ 0 };
};
Endpoints endpoints;
Endpoints current;
Vec3f current_position{ Vec3f::Zero() };
Marker marker;
};
enum class EViewType : unsigned char
{
FeatureType,
Height,
Width,
Feedrate,
FanSpeed,
VolumetricRate,
Tool,
ColorPrint,
Count
};
private:
unsigned int m_last_result_id{ 0 };
size_t m_vertices_count{ 0 };
mutable std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) };
// bounding box of toolpaths
BoundingBoxf3 m_paths_bounding_box;
// bounding box of toolpaths + marker tools
BoundingBoxf3 m_max_bounding_box;
std::vector<Color> m_tool_colors;
std::vector<double> m_layers_zs;
std::array<double, 2> m_layers_z_range;
std::vector<ExtrusionRole> m_roles;
std::vector<unsigned char> m_extruder_ids;
mutable Extrusions m_extrusions;
mutable SequentialView m_sequential_view;
Shells m_shells;
EViewType m_view_type{ EViewType::FeatureType };
bool m_legend_enabled{ true };
PrintEstimatedTimeStatistics m_time_statistics;
mutable PrintEstimatedTimeStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedTimeStatistics::ETimeMode::Normal };
#if ENABLE_GCODE_VIEWER_STATISTICS
mutable Statistics m_statistics;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
public:
GCodeViewer() = default;
~GCodeViewer() { reset(); }
bool init();
// extract rendering data from the given parameters
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
// recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
void reset();
void render() const;
bool has_data() const { return !m_roles.empty(); }
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
const std::vector<double>& get_layers_zs() const { return m_layers_zs; };
const SequentialView& get_sequential_view() const { return m_sequential_view; }
void update_sequential_view_current(unsigned int first, unsigned int last)
{
m_sequential_view.current.first = first;
m_sequential_view.current.last = last;
refresh_render_paths(true, true);
}
EViewType get_view_type() const { return m_view_type; }
void set_view_type(EViewType type) {
if (type == EViewType::Count)
type = EViewType::FeatureType;
m_view_type = type;
}
bool is_toolpath_move_type_visible(EMoveType type) const;
void set_toolpath_move_type_visible(EMoveType type, bool visible);
unsigned int get_toolpath_role_visibility_flags() const { return m_extrusions.role_visibility_flags; }
void set_toolpath_role_visibility_flags(unsigned int flags) { m_extrusions.role_visibility_flags = flags; }
unsigned int get_options_visibility_flags() const;
void set_options_visibility_from_flags(unsigned int flags);
void set_layers_z_range(const std::array<double, 2>& layers_z_range);
bool is_legend_enabled() const { return m_legend_enabled; }
void enable_legend(bool enable) { m_legend_enabled = enable; }
void export_toolpaths_to_obj(const char* filename) const;
private:
void init_shaders();
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
void load_shells(const Print& print, bool initialized);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths() const;
void render_shells() const;
void render_legend() const;
#if ENABLE_GCODE_VIEWER_STATISTICS
void render_statistics() const;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
bool is_visible(ExtrusionRole role) const {
return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0;
}
bool is_visible(const Path& path) const { return is_visible(path.role); }
bool is_in_z_range(const Path& path) const {
auto in_z_range = [this](double z) {
return z > m_layers_z_range[0] - EPSILON && z < m_layers_z_range[1] + EPSILON;
};
return in_z_range(path.first.position[2]) || in_z_range(path.last.position[2]);
}
bool is_travel_in_z_range(size_t id) const;
};
} // namespace GUI
} // namespace Slic3r
#endif // ENABLE_GCODE_VIEWER
#endif // slic3r_GCodeViewer_hpp_

View file

@ -5,7 +5,9 @@
#include "polypartition.h"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#if !ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/PreviewData.hpp"
#endif // !ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
@ -158,11 +160,8 @@ GLCanvas3D::LayersEditing::~LayersEditing()
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
void GLCanvas3D::LayersEditing::init()
{
if (!m_shader.init(vertex_shader_filename, fragment_shader_filename))
return false;
glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
@ -171,8 +170,6 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename,
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
@ -205,7 +202,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
bool GLCanvas3D::LayersEditing::is_allowed() const
{
return m_shader.is_initialized() && m_shader.get_shader()->shader_program_id > 0 && m_z_texture_id > 0;
return wxGetApp().get_shader("variable_layer_height") != nullptr && m_z_texture_id > 0;
}
bool GLCanvas3D::LayersEditing::is_enabled() const
@ -223,8 +220,6 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
if (!m_enabled)
return;
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
const Size& cnv_size = canvas.get_canvas_size();
ImGuiWrapper& imgui = *wxGetApp().imgui();
@ -233,23 +228,23 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
imgui.begin(_L("Variable layer height"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
imgui.text_colored(ORANGE, _L("Left mouse button:"));
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Left mouse button:"));
ImGui::SameLine();
imgui.text(_L("Add detail"));
imgui.text_colored(ORANGE, _L("Right mouse button:"));
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Right mouse button:"));
ImGui::SameLine();
imgui.text(_L("Remove detail"));
imgui.text_colored(ORANGE, _L("Shift + Left mouse button:"));
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Shift + Left mouse button:"));
ImGui::SameLine();
imgui.text(_L("Reset to base"));
imgui.text_colored(ORANGE, _L("Shift + Right mouse button:"));
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Shift + Right mouse button:"));
ImGui::SameLine();
imgui.text(_L("Smoothing"));
imgui.text_colored(ORANGE, _L("Mouse wheel:"));
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mouse wheel:"));
ImGui::SameLine();
imgui.text(_L("Increase/decrease edit area"));
@ -351,7 +346,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
bool GLCanvas3D::LayersEditing::is_initialized() const
{
return m_shader.is_initialized();
return wxGetApp().get_shader("variable_layer_height") != nullptr;
}
std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const
@ -385,13 +380,17 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con
void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
{
m_shader.start_using();
GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height");
if (shader == nullptr)
return;
m_shader.set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z));
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height);
m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas));
m_shader.set_uniform("z_cursor_band_width", band_width);
m_shader.set_uniform("object_max_z", m_object_max_z);
shader->start_using();
shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z));
shader->set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height);
shader->set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas));
shader->set_uniform("z_cursor_band_width", band_width);
shader->set_uniform("object_max_z", m_object_max_z);
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
@ -411,7 +410,7 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3
glsafe(::glEnd());
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_shader.stop_using();
shader->stop_using();
}
void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const
@ -445,73 +444,50 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
{
assert(this->is_allowed());
assert(this->last_object_id != -1);
GLint shader_id = m_shader.get_shader()->shader_program_id;
assert(shader_id > 0);
GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height");
if (shader == nullptr)
return;
GLint current_program_id;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id));
if (shader_id > 0 && shader_id != current_program_id)
GLShaderProgram* current_shader = wxGetApp().get_current_shader();
if (shader->get_id() != current_shader->get_id())
// The layer editing shader is not yet active. Activate it.
glsafe(::glUseProgram(shader_id));
shader->start_using();
else
// The layer editing shader was already active.
current_program_id = -1;
current_shader = nullptr;
GLint z_to_texture_row_id = ::glGetUniformLocation(shader_id, "z_to_texture_row");
GLint z_texture_row_to_normalized_id = ::glGetUniformLocation(shader_id, "z_texture_row_to_normalized");
GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor");
GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width");
GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix");
GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z");
glcheck();
const_cast<LayersEditing*>(this)->generate_layer_height_texture();
if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1)
{
const_cast<LayersEditing*>(this)->generate_layer_height_texture();
// Uniforms were resolved, go ahead using the layer editing shader.
shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * float(m_object_max_z)));
shader->set_uniform("z_texture_row_to_normalized", 1.0f / float(m_layers_texture.height));
shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas)));
shader->set_uniform("z_cursor_band_width", float(this->band_width));
// Uniforms were resolved, go ahead using the layer editing shader.
glsafe(::glUniform1f(z_to_texture_row_id, GLfloat(m_layers_texture.cells - 1) / (GLfloat(m_layers_texture.width) * GLfloat(m_object_max_z))));
glsafe(::glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height)));
glsafe(::glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas))));
glsafe(::glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width)));
// Initialize the layer height texture mapping.
GLsizei w = (GLsizei)m_layers_texture.width;
GLsizei h = (GLsizei)m_layers_texture.height;
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
for (const GLVolume* glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
if (world_matrix_id != -1)
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
if (object_max_z_id != -1)
glsafe(::glUniform1f(object_max_z_id, GLfloat(0)));
glvolume->render();
}
// Revert back to the previous shader.
glBindTexture(GL_TEXTURE_2D, 0);
if (current_program_id > 0)
glsafe(::glUseProgram(current_program_id));
}
else
{
// Something went wrong. Just render the object.
assert(false);
for (const GLVolume* glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
glvolume->render();
}
}
// Initialize the layer height texture mapping.
GLsizei w = (GLsizei)m_layers_texture.width;
GLsizei h = (GLsizei)m_layers_texture.height;
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
for (const GLVolume* glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
shader->set_uniform("volume_world_matrix", glvolume->world_matrix());
shader->set_uniform("object_max_z", GLfloat(0));
glvolume->render();
}
// Revert back to the previous shader.
glBindTexture(GL_TEXTURE_2D, 0);
if (current_shader != nullptr)
current_shader->start_using();
}
void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
@ -899,6 +875,7 @@ void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas)
generate(m_msg_text, canvas, true, m_is_colored_red);
}
#if !ENABLE_GCODE_VIEWER
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
const unsigned char GLCanvas3D::LegendTexture::Default_Background_Color[3] = { (unsigned char)(DEFAULT_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[2] * 255.0f) };
const unsigned char GLCanvas3D::LegendTexture::Error_Background_Color[3] = { (unsigned char)(ERROR_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[2] * 255.0f) };
@ -1257,6 +1234,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
}
}
#endif // !ENABLE_GCODE_VIEWER
void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_instances) const
{
@ -1528,7 +1506,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
#if ENABLE_GCODE_VIEWER
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, wxKeyEvent);
#else
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
#endif // ENABLE_GCODE_VIEWER
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
@ -1559,7 +1541,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
, m_dirty(true)
, m_initialized(false)
, m_apply_zoom_to_volumes_filter(false)
#if !ENABLE_GCODE_VIEWER
, m_legend_texture_enabled(false)
#endif // !ENABLE_GCODE_VIEWER
, m_picking_enabled(false)
, m_moving_enabled(false)
, m_dynamic_background_enabled(false)
@ -1650,17 +1634,16 @@ bool GLCanvas3D::init()
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
if (!m_shader.init("gouraud.vs", "gouraud.fs"))
{
std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl;
return false;
}
if (m_main_toolbar.is_enabled())
m_layers_editing.init();
if (m_main_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs"))
#if ENABLE_GCODE_VIEWER
if (!m_main_toolbar.is_enabled())
{
std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl;
return false;
if (!m_gcode_viewer.init())
return false;
}
#endif // ENABLE_GCODE_VIEWER
// on linux the gl context is not valid until the canvas is not shown on screen
// we defer the geometry finalization of volumes until the first call to render()
@ -1892,7 +1875,11 @@ void GLCanvas3D::enable_layers_editing(bool enable)
void GLCanvas3D::enable_legend_texture(bool enable)
{
#if ENABLE_GCODE_VIEWER
m_gcode_viewer.enable_legend(enable);
#else
m_legend_texture_enabled = enable;
#endif // ENABLE_GCODE_VIEWER
}
void GLCanvas3D::enable_picking(bool enable)
@ -1954,6 +1941,13 @@ void GLCanvas3D::zoom_to_selection()
_zoom_to_box(m_selection.get_bounding_box());
}
#if ENABLE_GCODE_VIEWER
void GLCanvas3D::zoom_to_gcode()
{
_zoom_to_box(m_gcode_viewer.get_paths_bounding_box(), 1.05);
}
#endif // ENABLE_GCODE_VIEWER
void GLCanvas3D::select_view(const std::string& direction)
{
wxGetApp().plater()->get_camera().select_view(direction);
@ -2049,6 +2043,10 @@ void GLCanvas3D::render()
_render_background();
_render_objects();
#if ENABLE_GCODE_VIEWER
if (!m_main_toolbar.is_enabled())
_render_gcode();
#endif // ENABLE_GCODE_VIEWER
_render_sla_slices();
_render_selection();
_render_bed(!camera.is_looking_downward(), true);
@ -2060,6 +2058,9 @@ void GLCanvas3D::render()
// we need to set the mouse's scene position here because the depth buffer
// could be invalidated by the following gizmo render methods
// this position is used later into on_mouse() to drag the objects
#if ENABLE_GCODE_VIEWER
if (m_picking_enabled)
#endif // ENABLE_GCODE_VIEWER
m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<coord_t>());
_render_current_gizmo();
@ -2097,7 +2098,7 @@ void GLCanvas3D::render()
#endif // ENABLE_RENDER_STATISTICS
#if ENABLE_CAMERA_STATISTICS
m_camera.debug_render();
camera.debug_render();
#endif // ENABLE_CAMERA_STATISTICS
std::string tooltip;
@ -2204,6 +2205,40 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx)
}
}
#if ENABLE_GCODE_VIEWER
const std::vector<double>& GLCanvas3D::get_gcode_layers_zs() const
{
return m_gcode_viewer.get_layers_zs();
}
std::vector<double> GLCanvas3D::get_volumes_print_zs(bool active_only) const
{
return m_volumes.get_current_print_zs(active_only);
}
void GLCanvas3D::set_gcode_options_visibility_from_flags(unsigned int flags)
{
m_gcode_viewer.set_options_visibility_from_flags(flags);
}
void GLCanvas3D::set_toolpath_role_visibility_flags(unsigned int flags)
{
m_gcode_viewer.set_toolpath_role_visibility_flags(flags);
}
void GLCanvas3D::set_toolpath_view_type(GCodeViewer::EViewType type)
{
m_gcode_viewer.set_view_type(type);
}
void GLCanvas3D::set_toolpaths_z_range(const std::array<double, 2>& range)
{
m_volumes.set_range(range[0] - 1e-6, range[1] + 1e-6);
if (m_gcode_viewer.has_data())
m_gcode_viewer.set_layers_z_range(range);
}
#else
std::vector<double> GLCanvas3D::get_current_print_zs(bool active_only) const
{
return m_volumes.get_current_print_zs(active_only);
@ -2213,6 +2248,7 @@ void GLCanvas3D::set_toolpaths_range(double low, double high)
{
m_volumes.set_range(low, high);
}
#endif // ENABLE_GCODE_VIEWER
std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs)
{
@ -2655,6 +2691,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
vol_old.finalize_geometry(gl_initialized);
}
#if !ENABLE_GCODE_VIEWER
static void load_gcode_retractions(const GCodePreviewData::Retraction& retractions, GLCanvas3D::GCodePreviewVolumeIndex::EType extrusion_type, GLVolumeCollection &volumes, GLCanvas3D::GCodePreviewVolumeIndex &volume_index, bool gl_initialized)
{
// nothing to render, return
@ -2685,7 +2722,23 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio
}
volume->indexed_vertex_array.finalize_geometry(gl_initialized);
}
#endif // !ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
{
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer)
_show_warning_texture_if_needed(WarningTexture::ToolpathOutside);
}
void GLCanvas3D::refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
{
m_gcode_viewer.refresh(gcode_result, str_tool_colors);
set_as_dirty();
request_extra_frame();
}
#else
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
{
const Print *print = this->fff_print();
@ -2754,6 +2807,7 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
_generate_legend_texture(preview_data, tool_colors);
}
}
#endif // ENABLE_GCODE_VIEWER
void GLCanvas3D::load_sla_preview()
{
@ -2787,6 +2841,7 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
_update_toolpath_volumes_outside_state();
_show_warning_texture_if_needed(WarningTexture::ToolpathOutside);
#if !ENABLE_GCODE_VIEWER
if (color_print_values.empty())
reset_legend_texture();
else {
@ -2795,6 +2850,7 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
const std::vector<float> tool_colors = _parse_colors(str_tool_colors);
_generate_legend_texture(preview_data, tool_colors);
}
#endif // !ENABLE_GCODE_VIEWER
}
void GLCanvas3D::bind_event_handlers()
@ -3032,18 +3088,46 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'i': { _update_camera_zoom(1.0); break; }
case 'K':
case 'k': { wxGetApp().plater()->get_camera().select_next_type(); m_dirty = true; break; }
#if ENABLE_GCODE_VIEWER
case 'L':
case 'l': {
if (!m_main_toolbar.is_enabled()) {
m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled());
m_dirty = true;
wxGetApp().plater()->update_preview_bottom_toolbar();
}
break;
}
#endif // ENABLE_GCODE_VIEWER
case 'O':
case 'o': { _update_camera_zoom(-1.0); break; }
#if ENABLE_RENDER_PICKING_PASS
case 'T':
case 't': {
case 'P':
case 'p': {
m_show_picking_texture = !m_show_picking_texture;
m_dirty = true;
m_dirty = true;
break;
}
#endif // ENABLE_RENDER_PICKING_PASS
case 'Z':
#if ENABLE_GCODE_VIEWER
case 'z':
{
if (!m_selection.is_empty())
zoom_to_selection();
else
{
if (!m_volumes.empty())
zoom_to_volumes();
else
_zoom_to_box(m_gcode_viewer.get_paths_bounding_box());
}
break;
}
#else
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
#endif // ENABLE_GCODE_VIEWER
default: { evt.Skip(); break; }
}
}
@ -3286,7 +3370,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
keyCode == WXK_DOWN)
{
if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
#if ENABLE_GCODE_VIEWER
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, evt));
#else
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt));
#endif // ENABLE_GCODE_VIEWER
}
}
}
@ -3850,9 +3938,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_selection.is_empty())
m_gizmos.reset_all_states();
#if ENABLE_GCODE_VIEWER
m_dirty = true;
#else
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
//if (m_picking_enabled)
m_dirty = true;
#endif // ENABLE_GCODE_VIEWER
}
else
evt.Skip();
@ -3913,6 +4005,7 @@ Vec2d GLCanvas3D::get_local_mouse_position() const
return Vec2d(factor * mouse_pos.x, factor * mouse_pos.y);
}
#if !ENABLE_GCODE_VIEWER
void GLCanvas3D::reset_legend_texture()
{
if (m_legend_texture.get_id() != 0)
@ -3921,6 +4014,7 @@ void GLCanvas3D::reset_legend_texture()
m_legend_texture.reset();
}
}
#endif // !ENABLE_GCODE_VIEWER
void GLCanvas3D::set_tooltip(const std::string& tooltip) const
{
@ -4210,12 +4304,15 @@ void GLCanvas3D::update_ui_from_settings()
}
#endif // ENABLE_RETINA_GL
#if ENABLE_GCODE_VIEWER
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer)
wxGetApp().plater()->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1");
#else
bool enable_collapse = wxGetApp().app_config->get("show_collapse_button") == "1";
wxGetApp().plater()->get_collapse_toolbar().set_enabled(enable_collapse);
#endif // ENABLE_GCODE_VIEWER
}
GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const
{
WipeTowerInfo wti;
@ -4277,12 +4374,20 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()
bool GLCanvas3D::has_toolpaths_to_export() const
{
#if ENABLE_GCODE_VIEWER
return m_gcode_viewer.has_data();
#else
return m_volumes.has_toolpaths_to_export();
#endif // ENABLE_GCODE_VIEWER
}
void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const
{
#if ENABLE_GCODE_VIEWER
m_gcode_viewer.export_toolpaths_to_obj(filename);
#else
m_volumes.export_toolpaths_to_obj(filename);
#endif // ENABLE_GCODE_VIEWER
}
void GLCanvas3D::mouse_up_cleanup()
@ -4432,8 +4537,8 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
return ret;
};
static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f };
static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f };
static const std::array<float, 4> orange = { 0.923f, 0.504f, 0.264f, 1.0f };
static const std::array<float, 4> gray = { 0.64f, 0.64f, 0.64f, 1.0f };
GLVolumePtrs visible_volumes;
@ -4477,39 +4582,33 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
camera.apply_projection(box, near_z, far_z);
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
if (shader == nullptr)
return;
if (transparent_background)
glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
m_shader.start_using();
GLint shader_id = m_shader.get_shader_program_id();
GLint color_id = ::glGetUniformLocation(shader_id, "uniform_color");
GLint print_box_detection_id = ::glGetUniformLocation(shader_id, "print_box.volume_detection");
glcheck();
if (print_box_detection_id != -1)
glsafe(::glUniform1i(print_box_detection_id, 0));
shader->start_using();
shader->set_uniform("print_box.volume_detection", 0);
for (const GLVolume* vol : visible_volumes)
{
if (color_id >= 0)
glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray));
else
glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray));
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
vol->render();
}
m_shader.stop_using();
shader->stop_using();
glsafe(::glDisable(GL_DEPTH_TEST));
if (show_bed)
_render_bed(!camera.is_looking_downward(), false);
// restore background color
if (transparent_background)
glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
}
@ -5120,6 +5219,12 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
}
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(include_bed_model));
#if ENABLE_GCODE_VIEWER
if (!m_main_toolbar.is_enabled())
bb.merge(m_gcode_viewer.get_max_bounding_box());
#endif // ENABLE_GCODE_VIEWER
return bb;
}
@ -5280,8 +5385,40 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
_update_volumes_hover_state();
}
#if ENABLE_GCODE_VIEWER
static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
{
// tolerance to avoid false detection at bed edges
const double tolerance_x = 0.05;
const double tolerance_y = 0.05;
BoundingBoxf3 ret;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
if (opt != nullptr) {
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
ret = BoundingBoxf3(Vec3d(unscale<double>(bed_box_2D.min(0)) - tolerance_x, unscale<double>(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)) + tolerance_x, unscale<double>(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
ret.min(2) = -1e10;
}
return ret;
}
#endif // ENABLE_GCODE_VIEWER
void GLCanvas3D::_render_background() const
{
#if ENABLE_GCODE_VIEWER
bool use_error_color = false;
if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) {
use_error_color = m_dynamic_background_enabled;
if (!m_volumes.empty())
use_error_color &= _is_any_volume_outside();
else {
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_paths_bounding_box()) : false;
}
}
#endif // ENABLE_GCODE_VIEWER
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
glsafe(::glMatrixMode(GL_PROJECTION));
@ -5292,7 +5429,11 @@ void GLCanvas3D::_render_background() const
glsafe(::glDisable(GL_DEPTH_TEST));
::glBegin(GL_QUADS);
#if ENABLE_GCODE_VIEWER
if (use_error_color)
#else
if (m_dynamic_background_enabled && _is_any_volume_outside())
#endif // ENABLE_GCODE_VIEWER
::glColor3fv(ERROR_BG_DARK_COLOR);
else
::glColor3fv(DEFAULT_BG_DARK_COLOR);
@ -5300,8 +5441,12 @@ void GLCanvas3D::_render_background() const
::glVertex2f(-1.0f, -1.0f);
::glVertex2f(1.0f, -1.0f);
#if ENABLE_GCODE_VIEWER
if (use_error_color)
#else
if (m_dynamic_background_enabled && _is_any_volume_outside())
::glColor3fv(ERROR_BG_LIGHT_COLOR);
#endif // ENABLE_GCODE_VIEWER
::glColor3fv(ERROR_BG_LIGHT_COLOR);
else
::glColor3fv(DEFAULT_BG_LIGHT_COLOR);
@ -5339,13 +5484,11 @@ void GLCanvas3D::_render_objects() const
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
if (m_picking_enabled)
{
if (m_picking_enabled) {
// Update the layer editing selection to the first object selected, update the current object maximum Z.
const_cast<LayersEditing&>(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
if (m_config != nullptr)
{
if (m_config != nullptr) {
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
m_volumes.check_outside_state(m_config, nullptr);
@ -5359,28 +5502,39 @@ void GLCanvas3D::_render_objects() const
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
m_shader.start_using();
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
m_layers_editing.render_volumes(*this, this->m_volumes);
} else {
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
if (shader != nullptr) {
shader->start_using();
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
m_layers_editing.render_volumes(*this, this->m_volumes);
} else {
// do not cull backfaces to show broken geometry, if any
m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
}
}
m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
m_shader.stop_using();
m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
shader->stop_using();
}
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
}
#if ENABLE_GCODE_VIEWER
void GLCanvas3D::_render_gcode() const
{
m_gcode_viewer.render();
}
#endif // ENABLE_GCODE_VIEWER
void GLCanvas3D::_render_selection() const
{
float scale_factor = 1.0;
@ -5457,7 +5611,9 @@ void GLCanvas3D::_render_overlays() const
_render_gizmos_overlay();
_render_warning_texture();
#if !ENABLE_GCODE_VIEWER
_render_legend_texture();
#endif // !ENABLE_GCODE_VIEWER
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
// to correctly place them
@ -5504,6 +5660,7 @@ void GLCanvas3D::_render_warning_texture() const
m_warning_texture.render(*this);
}
#if !ENABLE_GCODE_VIEWER
void GLCanvas3D::_render_legend_texture() const
{
if (!m_legend_texture_enabled)
@ -5511,6 +5668,7 @@ void GLCanvas3D::_render_legend_texture() const
m_legend_texture.render(*this);
}
#endif // !ENABLE_GCODE_VIEWER
void GLCanvas3D::_render_volumes_for_picking() const
{
@ -5788,7 +5946,7 @@ void GLCanvas3D::_render_sla_slices() const
void GLCanvas3D::_render_selection_sidebar_hints() const
{
m_selection.render_sidebar_hints(m_sidebar_field, m_shader);
m_selection.render_sidebar_hints(m_sidebar_field);
}
void GLCanvas3D::_update_volumes_hover_state() const
@ -6458,6 +6616,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
}
#if !ENABLE_GCODE_VIEWER
static inline int hex_digit_to_int(const char c)
{
return
@ -6785,6 +6944,7 @@ void GLCanvas3D::_load_fff_shells()
}
}
}
#endif // !ENABLE_GCODE_VIEWER
// While it looks like we can call
// this->reload_scene(true, true)
@ -6842,6 +7002,7 @@ void GLCanvas3D::_load_sla_shells()
update_volumes_colors_by_extruder();
}
#if !ENABLE_GCODE_VIEWER
void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data)
{
unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size();
@ -6899,9 +7060,13 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
}
}
}
#endif // !ENABLE_GCODE_VIEWER
void GLCanvas3D::_update_toolpath_volumes_outside_state()
{
#if ENABLE_GCODE_VIEWER
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
#else
// tolerance to avoid false detection at bed edges
static const double tolerance_x = 0.05;
static const double tolerance_y = 0.05;
@ -6918,15 +7083,23 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state()
print_volume.min(2) = -1e10;
}
}
#endif // ENABLE_GCODE_VIEWER
for (GLVolume* volume : m_volumes.volumes)
{
#if ENABLE_GCODE_VIEWER
volume->is_outside = ((test_volume.radius() > 0.0) && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false;
#else
volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box()) : false;
#endif // ENABLE_GCODE_VIEWER
}
}
void GLCanvas3D::_update_sla_shells_outside_state()
{
#if ENABLE_GCODE_VIEWER
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
#else
// tolerance to avoid false detection at bed edges
static const double tolerance_x = 0.05;
static const double tolerance_y = 0.05;
@ -6943,17 +7116,37 @@ void GLCanvas3D::_update_sla_shells_outside_state()
print_volume.min(2) = -1e10;
}
}
#endif // ENABLE_GCODE_VIEWER
for (GLVolume* volume : m_volumes.volumes)
{
#if ENABLE_GCODE_VIEWER
volume->is_outside = ((test_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
#else
volume->is_outside = ((print_volume.radius() > 0.0) && volume->shader_outside_printer_detection_enabled) ? !print_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
#endif // ENABLE_GCODE_VIEWER
}
}
void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning)
{
_set_current();
#if ENABLE_GCODE_VIEWER
bool show = false;
if (!m_volumes.empty())
show = _is_any_volume_outside();
else {
if (wxGetApp().mainframe->get_mode() != MainFrame::EMode::GCodeViewer) {
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0)
show = !test_volume.contains(paths_volume);
}
}
_set_warning_texture(warning, show);
#else
_set_warning_texture(warning, _is_any_volume_outside());
#endif // ENABLE_GCODE_VIEWER
}
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
@ -6981,10 +7174,12 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
return output;
}
#if !ENABLE_GCODE_VIEWER
void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
m_legend_texture.generate(preview_data, tool_colors, *this, true);
}
#endif // !ENABLE_GCODE_VIEWER
void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state)
{

View file

@ -6,13 +6,16 @@
#include <chrono>
#include "GLToolbar.hpp"
#include "GLShader.hpp"
#include "Event.hpp"
#include "Selection.hpp"
#include "Gizmos/GLGizmosManager.hpp"
#include "GUI_ObjectLayers.hpp"
#include "GLSelectionRectangle.hpp"
#include "MeshUtils.hpp"
#if ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GCodeViewer.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "libslic3r/Slicing.hpp"
@ -36,7 +39,9 @@ namespace Slic3r {
struct Camera;
class BackgroundSlicingProcess;
#if !ENABLE_GCODE_VIEWER
class GCodePreviewData;
#endif // !ENABLE_GCODE_VIEWER
struct ThumbnailData;
class ModelObject;
class ModelInstance;
@ -103,7 +108,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
#if ENABLE_GCODE_VIEWER
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, wxKeyEvent);
#else
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
#endif // ENABLE_GCODE_VIEWER
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
@ -118,6 +127,7 @@ class GLCanvas3D
static const double DefaultCameraZoomToBoxMarginFactor;
public:
#if !ENABLE_GCODE_VIEWER
struct GCodePreviewVolumeIndex
{
enum EType
@ -144,6 +154,7 @@ public:
void reset() { first_volumes.clear(); }
};
#endif // !ENABLE_GCODE_VIEWER
private:
class LayersEditing
@ -161,7 +172,6 @@ private:
private:
bool m_enabled;
Shader m_shader;
unsigned int m_z_texture_id;
// Not owned by LayersEditing.
const DynamicPrintConfig *m_config;
@ -208,8 +218,9 @@ private:
LayersEditing();
~LayersEditing();
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
void set_config(const DynamicPrintConfig* config);
void init();
void set_config(const DynamicPrintConfig* config);
void select_object(const Model &model, int object_id);
bool is_allowed() const;
@ -339,6 +350,7 @@ private:
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
};
#if !ENABLE_GCODE_VIEWER
class LegendTexture : public GUI::GLTexture
{
static const int Px_Title_Offset = 5;
@ -365,6 +377,7 @@ private:
void render(const GLCanvas3D& canvas) const;
};
#endif // !ENABLE_GCODE_VIEWER
#if ENABLE_RENDER_STATISTICS
struct RenderStats
@ -443,11 +456,12 @@ private:
std::unique_ptr<RetinaHelper> m_retina_helper;
#endif
bool m_in_render;
#if !ENABLE_GCODE_VIEWER
LegendTexture m_legend_texture;
#endif // !ENABLE_GCODE_VIEWER
WarningTexture m_warning_texture;
wxTimer m_timer;
LayersEditing m_layers_editing;
Shader m_shader;
Mouse m_mouse;
mutable GLGizmosManager m_gizmos;
mutable GLToolbar m_main_toolbar;
@ -462,6 +476,10 @@ private:
bool m_extra_frame_requested;
mutable GLVolumeCollection m_volumes;
#if ENABLE_GCODE_VIEWER
GCodeViewer m_gcode_viewer;
#endif // ENABLE_GCODE_VIEWER
Selection m_selection;
const DynamicPrintConfig* m_config;
Model* m_model;
@ -472,7 +490,9 @@ private:
bool m_initialized;
bool m_apply_zoom_to_volumes_filter;
mutable std::vector<int> m_hover_volume_idxs;
#if !ENABLE_GCODE_VIEWER
bool m_legend_texture_enabled;
#endif // !ENABLE_GCODE_VIEWER
bool m_picking_enabled;
bool m_moving_enabled;
bool m_dynamic_background_enabled;
@ -490,7 +510,9 @@ private:
bool m_reload_delayed;
#if !ENABLE_GCODE_VIEWER
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
#endif // !ENABLE_GCODE_VIEWER
#if ENABLE_RENDER_PICKING_PASS
bool m_show_picking_texture;
@ -532,6 +554,12 @@ public:
void reset_volumes();
int check_volumes_outside_state() const;
#if ENABLE_GCODE_VIEWER
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
#endif // ENABLE_GCODE_VIEWER
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void update_instance_printable_state_for_object(size_t obj_idx);
@ -564,7 +592,6 @@ public:
void set_color_by(const std::string& value);
void refresh_camera_scene_box();
const Shader& get_shader() const { return m_shader; }
BoundingBoxf3 volumes_bounding_box() const;
BoundingBoxf3 scene_bounding_box() const;
@ -597,6 +624,9 @@ public:
void zoom_to_bed();
void zoom_to_volumes();
void zoom_to_selection();
#if ENABLE_GCODE_VIEWER
void zoom_to_gcode();
#endif // ENABLE_GCODE_VIEWER
void select_view(const std::string& direction);
void update_volumes_colors_by_extruder();
@ -613,7 +643,20 @@ public:
void delete_selected();
void ensure_on_bed(unsigned int object_idx);
#if ENABLE_GCODE_VIEWER
bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); }
GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); }
const std::vector<double>& get_gcode_layers_zs() const;
std::vector<double> get_volumes_print_zs(bool active_only) const;
unsigned int get_gcode_options_visibility_flags() const { return m_gcode_viewer.get_options_visibility_flags(); }
void set_gcode_options_visibility_from_flags(unsigned int flags);
unsigned int get_toolpath_role_visibility_flags() const { return m_gcode_viewer.get_toolpath_role_visibility_flags(); }
void set_toolpath_role_visibility_flags(unsigned int flags);
void set_toolpath_view_type(GCodeViewer::EViewType type);
void set_toolpaths_z_range(const std::array<double, 2>& range);
#else
std::vector<double> get_current_print_zs(bool active_only) const;
#endif // ENABLE_GCODE_VIEWER
void set_toolpaths_range(double low, double high);
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
@ -623,7 +666,14 @@ public:
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
#if ENABLE_GCODE_VIEWER
void load_gcode_preview(const GCodeProcessor::Result& gcode_result);
void refresh_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
#else
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
#endif // ENABLE_GCODE_VIEWER
void load_sla_preview();
void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
void bind_event_handlers();
@ -642,7 +692,9 @@ public:
Size get_canvas_size() const;
Vec2d get_local_mouse_position() const;
#if !ENABLE_GCODE_VIEWER
void reset_legend_texture();
#endif // !ENABLE_GCODE_VIEWER
void set_tooltip(const std::string& tooltip) const;
@ -744,6 +796,9 @@ private:
void _render_background() const;
void _render_bed(bool bottom, bool show_axes) const;
void _render_objects() const;
#if ENABLE_GCODE_VIEWER
void _render_gcode() const;
#endif // ENABLE_GCODE_VIEWER
void _render_selection() const;
#if ENABLE_RENDER_SELECTION_CENTER
void _render_selection_center() const;
@ -751,7 +806,9 @@ private:
void _check_and_update_toolbar_icon_scale() const;
void _render_overlays() const;
void _render_warning_texture() const;
#if !ENABLE_GCODE_VIEWER
void _render_legend_texture() const;
#endif // !ENABLE_GCODE_VIEWER
void _render_volumes_for_picking() const;
void _render_current_gizmo() const;
void _render_gizmos_overlay() const;
@ -799,22 +856,28 @@ private:
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
#if !ENABLE_GCODE_VIEWER
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// generates gcode travel paths geometry
void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// generates objects and wipe tower geometry
void _load_fff_shells();
#endif // !ENABLE_GCODE_VIEWER
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
void _load_sla_shells();
#if !ENABLE_GCODE_VIEWER
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
#endif // !ENABLE_GCODE_VIEWER
void _update_toolpath_volumes_outside_state();
void _update_sla_shells_outside_state();
void _show_warning_texture_if_needed(WarningTexture::Warning warning);
#if !ENABLE_GCODE_VIEWER
// generates the legend texture in dependence of the current shown view type
void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
#endif // !ENABLE_GCODE_VIEWER
// generates a warning texture containing the given message
void _set_warning_texture(WarningTexture::Warning warning, bool state);

531
src/slic3r/GUI/GLModel.cpp Normal file
View file

@ -0,0 +1,531 @@
#include "libslic3r/libslic3r.h"
#include "GLModel.hpp"
#include "3DScene.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
void GLModel::init_from(const GLModelInitializationData& data)
{
assert(!data.positions.empty() && !data.triangles.empty());
assert(data.positions.size() == data.normals.size());
if (m_vbo_id > 0) // call reset() if you want to reuse this model
return;
// vertices/normals data
std::vector<float> vertices(6 * data.positions.size());
for (size_t i = 0; i < data.positions.size(); ++i) {
size_t offset = i * 6;
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(data.positions[i].data()), 3 * sizeof(float));
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(data.normals[i].data()), 3 * sizeof(float));
}
// indices data
std::vector<unsigned int> indices(3 * data.triangles.size());
for (size_t i = 0; i < data.triangles.size(); ++i) {
for (size_t j = 0; j < 3; ++j) {
indices[i * 3 + j] = static_cast<unsigned int>(data.triangles[i][j]);
}
}
m_indices_count = static_cast<unsigned int>(indices.size());
m_bounding_box = BoundingBoxf3();
for (size_t i = 0; i < data.positions.size(); ++i) {
m_bounding_box.merge(data.positions[i].cast<double>());
}
send_to_gpu(vertices, indices);
}
void GLModel::init_from(const TriangleMesh& mesh)
{
if (m_vbo_id > 0) // call reset() if you want to reuse this model
return;
std::vector<float> vertices = std::vector<float>(18 * mesh.stl.stats.number_of_facets);
std::vector<unsigned int> indices = std::vector<unsigned int>(3 * mesh.stl.stats.number_of_facets);
unsigned int vertices_count = 0;
for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
const stl_facet& facet = mesh.stl.facet_start[i];
for (uint32_t j = 0; j < 3; ++j) {
uint32_t offset = i * 18 + j * 6;
::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(facet.vertex[j].data()), 3 * sizeof(float));
::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(facet.normal.data()), 3 * sizeof(float));
}
for (uint32_t j = 0; j < 3; ++j) {
indices[i * 3 + j] = vertices_count + j;
}
vertices_count += 3;
}
m_indices_count = static_cast<unsigned int>(indices.size());
m_bounding_box = mesh.bounding_box();
send_to_gpu(vertices, indices);
}
bool GLModel::init_from_file(const std::string& filename)
{
if (!boost::filesystem::exists(filename))
return false;
if (!boost::algorithm::iends_with(filename, ".stl"))
return false;
Model model;
try
{
model = Model::read_from_file(filename);
}
catch (std::exception&)
{
return false;
}
init_from(model.mesh());
m_filename = filename;
return true;
}
void GLModel::reset()
{
// release gpu memory
if (m_ibo_id > 0) {
glsafe(::glDeleteBuffers(1, &m_ibo_id));
m_ibo_id = 0;
}
if (m_vbo_id > 0) {
glsafe(::glDeleteBuffers(1, &m_vbo_id));
m_vbo_id = 0;
}
m_indices_count = 0;
m_bounding_box = BoundingBoxf3();
m_filename = std::string();
}
void GLModel::render() const
{
if (m_vbo_id == 0 || m_ibo_id == 0)
return;
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id));
glsafe(::glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_indices_count), GL_UNSIGNED_INT, (const void*)0));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
void GLModel::send_to_gpu(const std::vector<float>& vertices, const std::vector<unsigned int>& indices)
{
// vertex data -> send to gpu
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
// indices data -> send to gpu
glsafe(::glGenBuffers(1, &m_ibo_id));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height)
{
auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) {
data.positions.emplace_back(position);
data.normals.emplace_back(normal);
};
resolution = std::max(4, resolution);
GLModelInitializationData data;
const float angle_step = 2.0f * M_PI / static_cast<float>(resolution);
std::vector<float> cosines(resolution);
std::vector<float> sines(resolution);
for (int i = 0; i < resolution; ++i)
{
float angle = angle_step * static_cast<float>(i);
cosines[i] = ::cos(angle);
sines[i] = -::sin(angle);
}
const float total_height = tip_height + stem_height;
// tip vertices/normals
append_vertex(data, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ());
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f });
}
// tip triangles
for (int i = 0; i < resolution; ++i)
{
int v3 = (i < resolution - 1) ? i + 2 : 1;
data.triangles.emplace_back(0, i + 1, v3);
}
// tip cap outer perimeter vertices
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ());
}
// tip cap inner perimeter vertices
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ());
}
// tip cap triangles
for (int i = 0; i < resolution; ++i)
{
int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1;
int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1;
data.triangles.emplace_back(i + resolution + 1, v3, v2);
data.triangles.emplace_back(i + resolution + 1, i + 2 * resolution + 1, v3);
}
// stem bottom vertices
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f });
}
// stem top vertices
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f });
}
// stem triangles
for (int i = 0; i < resolution; ++i)
{
int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1;
int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1;
data.triangles.emplace_back(i + 3 * resolution + 1, v3, v2);
data.triangles.emplace_back(i + 3 * resolution + 1, i + 4 * resolution + 1, v3);
}
// stem cap vertices
append_vertex(data, Vec3f::Zero(), -Vec3f::UnitZ());
for (int i = 0; i < resolution; ++i)
{
append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ());
}
// stem cap triangles
for (int i = 0; i < resolution; ++i)
{
int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2;
data.triangles.emplace_back(5 * resolution + 1, v3, i + 5 * resolution + 2);
}
return data;
}
GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness)
{
auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) {
data.positions.emplace_back(position);
data.normals.emplace_back(normal);
};
resolution = std::max(2, resolution);
GLModelInitializationData data;
const float half_thickness = 0.5f * thickness;
const float half_stem_width = 0.5f * stem_width;
const float half_tip_width = 0.5f * tip_width;
const float outer_radius = radius + half_stem_width;
const float inner_radius = radius - half_stem_width;
const float step_angle = 0.5f * PI / static_cast<float>(resolution);
// tip
// top face vertices
append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { -tip_height, radius, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ());
// top face triangles
data.triangles.emplace_back(0, 1, 2);
data.triangles.emplace_back(0, 2, 4);
data.triangles.emplace_back(4, 2, 3);
// bottom face vertices
append_vertex(data, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ());
// bottom face triangles
data.triangles.emplace_back(5, 7, 6);
data.triangles.emplace_back(5, 9, 7);
data.triangles.emplace_back(9, 8, 7);
// side faces vertices
append_vertex(data, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX());
Vec3f normal(-half_tip_width, tip_height, 0.0f);
normal.normalize();
append_vertex(data, { 0.0f, radius + half_tip_width, -half_thickness }, normal);
append_vertex(data, { -tip_height, radius, -half_thickness }, normal);
append_vertex(data, { 0.0f, radius + half_tip_width, half_thickness }, normal);
append_vertex(data, { -tip_height, radius, half_thickness }, normal);
normal = Vec3f(-half_tip_width, -tip_height, 0.0f);
normal.normalize();
append_vertex(data, { -tip_height, radius, -half_thickness }, normal);
append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, normal);
append_vertex(data, { -tip_height, radius, half_thickness }, normal);
append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, normal);
append_vertex(data, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX());
append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX());
// side face triangles
for (int i = 0; i < 4; ++i)
{
int ii = i * 4;
data.triangles.emplace_back(10 + ii, 11 + ii, 13 + ii);
data.triangles.emplace_back(10 + ii, 13 + ii, 12 + ii);
}
// stem
// top face vertices
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ());
}
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ());
}
// top face triangles
for (int i = 0; i < resolution; ++i)
{
data.triangles.emplace_back(26 + i, 27 + i, 27 + resolution + i);
data.triangles.emplace_back(27 + i, 28 + resolution + i, 27 + resolution + i);
}
// bottom face vertices
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ());
}
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ());
}
// bottom face triangles
for (int i = 0; i < resolution; ++i)
{
data.triangles.emplace_back(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i);
data.triangles.emplace_back(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i);
}
// side faces vertices and triangles
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
float c = ::cos(angle);
float s = ::sin(angle);
append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f });
}
for (int i = 0; i <= resolution; ++i)
{
float angle = static_cast<float>(i) * step_angle;
float c = ::cos(angle);
float s = ::sin(angle);
append_vertex(data, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f });
}
int first_id = 26 + 4 * (resolution + 1);
for (int i = 0; i < resolution; ++i)
{
int ii = first_id + i;
data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2);
data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1);
}
append_vertex(data, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY());
append_vertex(data, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY());
first_id = 26 + 6 * (resolution + 1);
data.triangles.emplace_back(first_id, first_id + 1, first_id + 3);
data.triangles.emplace_back(first_id, first_id + 3, first_id + 2);
for (int i = resolution; i >= 0; --i)
{
float angle = static_cast<float>(i) * step_angle;
float c = ::cos(angle);
float s = ::sin(angle);
append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f });
}
for (int i = resolution; i >= 0; --i)
{
float angle = static_cast<float>(i) * step_angle;
float c = ::cos(angle);
float s = ::sin(angle);
append_vertex(data, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f });
}
first_id = 30 + 6 * (resolution + 1);
for (int i = 0; i < resolution; ++i)
{
int ii = first_id + i;
data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2);
data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1);
}
return data;
}
GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness)
{
auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) {
data.positions.emplace_back(position);
data.normals.emplace_back(normal);
};
GLModelInitializationData data;
const float half_thickness = 0.5f * thickness;
const float half_stem_width = 0.5f * stem_width;
const float half_tip_width = 0.5f * tip_width;
const float total_height = tip_height + stem_height;
// top face vertices
append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { 0.0, total_height, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ());
append_vertex(data, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ());
// top face triangles
data.triangles.emplace_back(0, 1, 6);
data.triangles.emplace_back(6, 1, 5);
data.triangles.emplace_back(4, 5, 3);
data.triangles.emplace_back(5, 1, 3);
data.triangles.emplace_back(1, 2, 3);
// bottom face vertices
append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ());
append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ());
// bottom face triangles
data.triangles.emplace_back(7, 13, 8);
data.triangles.emplace_back(13, 12, 8);
data.triangles.emplace_back(12, 11, 10);
data.triangles.emplace_back(8, 12, 10);
data.triangles.emplace_back(9, 8, 10);
// side faces vertices
append_vertex(data, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX());
append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX());
append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX());
append_vertex(data, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY());
append_vertex(data, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY());
Vec3f normal(tip_height, half_tip_width, 0.0f);
normal.normalize();
append_vertex(data, { half_tip_width, stem_height, -half_thickness }, normal);
append_vertex(data, { 0.0, total_height, -half_thickness }, normal);
append_vertex(data, { half_tip_width, stem_height, half_thickness }, normal);
append_vertex(data, { 0.0, total_height, half_thickness }, normal);
normal = Vec3f(-tip_height, half_tip_width, 0.0f);
normal.normalize();
append_vertex(data, { 0.0, total_height, -half_thickness }, normal);
append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, normal);
append_vertex(data, { 0.0, total_height, half_thickness }, normal);
append_vertex(data, { -half_tip_width, stem_height, half_thickness }, normal);
append_vertex(data, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY());
append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY());
append_vertex(data, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX());
append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX());
append_vertex(data, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX());
append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX());
append_vertex(data, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY());
append_vertex(data, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY());
append_vertex(data, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY());
// side face triangles
for (int i = 0; i < 7; ++i)
{
int ii = i * 4;
data.triangles.emplace_back(14 + ii, 15 + ii, 17 + ii);
data.triangles.emplace_back(14 + ii, 17 + ii, 16 + ii);
}
return data;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,68 @@
#ifndef slic3r_GLModel_hpp_
#define slic3r_GLModel_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <vector>
#include <string>
namespace Slic3r {
class TriangleMesh;
namespace GUI {
struct GLModelInitializationData
{
std::vector<Vec3f> positions;
std::vector<Vec3f> normals;
std::vector<Vec3i> triangles;
};
class GLModel
{
unsigned int m_vbo_id{ 0 };
unsigned int m_ibo_id{ 0 };
size_t m_indices_count{ 0 };
BoundingBoxf3 m_bounding_box;
std::string m_filename;
public:
virtual ~GLModel() { reset(); }
void init_from(const GLModelInitializationData& data);
void init_from(const TriangleMesh& mesh);
bool init_from_file(const std::string& filename);
void reset();
void render() const;
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
const std::string& get_filename() const { return m_filename; }
private:
void send_to_gpu(const std::vector<float>& vertices, const std::vector<unsigned int>& indices);
};
// create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
// the origin of the arrow is in the center of the stem cap
// the arrow has its axis of symmetry along the Z axis and is pointing upward
GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height);
// create an arrow whose stem is a quarter of circle, with the given dimensions and resolution
// the origin of the arrow is in the center of the circle
// the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise
GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness);
// create an arrow with the given dimensions
// the origin of the arrow is in the center of the stem cap
// the arrow is contained in XY plane and has its main axis along the Y axis
GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLModel_hpp_

View file

@ -1,366 +1,348 @@
#include <GL/glew.h>
#include "libslic3r/libslic3r.h"
#include "GLShader.hpp"
#include "libslic3r/Utils.hpp"
#include "3DScene.hpp"
#include <boost/nowide/fstream.hpp>
#include "libslic3r/Utils.hpp"
#include <string>
#include <utility>
#include <assert.h>
#include <boost/nowide/fstream.hpp>
#include <GL/glew.h>
#include <cassert>
#include <boost/log/trivial.hpp>
namespace Slic3r {
GLShader::~GLShader()
GLShaderProgram::~GLShaderProgram()
{
assert(fragment_program_id == 0);
assert(vertex_program_id == 0);
assert(shader_program_id == 0);
if (m_id > 0)
glsafe(::glDeleteProgram(m_id));
}
// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr.
inline std::string gl_get_string_safe(GLenum param)
bool GLShaderProgram::init_from_files(const std::string& name, const ShaderFilenames& filenames)
{
const char *value = (const char*)glGetString(param);
return std::string(value ? value : "N/A");
auto load_from_file = [](const std::string& filename) {
std::string path = resources_dir() + "/shaders/" + filename;
boost::nowide::ifstream s(path, boost::nowide::ifstream::binary);
if (!s.good()) {
BOOST_LOG_TRIVIAL(error) << "Couldn't open file: '" << path << "'";
return std::string();
}
s.seekg(0, s.end);
int file_length = static_cast<int>(s.tellg());
s.seekg(0, s.beg);
std::string source(file_length, '\0');
s.read(source.data(), file_length);
if (!s.good()) {
BOOST_LOG_TRIVIAL(error) << "Error while loading file: '" << path << "'";
return std::string();
}
s.close();
return source;
};
ShaderSources sources = {};
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
sources[i] = filenames[i].empty() ? std::string() : load_from_file(filenames[i]);
}
bool valid = !sources[static_cast<size_t>(EShaderType::Vertex)].empty() && !sources[static_cast<size_t>(EShaderType::Fragment)].empty() && sources[static_cast<size_t>(EShaderType::Compute)].empty();
valid |= !sources[static_cast<size_t>(EShaderType::Compute)].empty() && sources[static_cast<size_t>(EShaderType::Vertex)].empty() && sources[static_cast<size_t>(EShaderType::Fragment)].empty() &&
sources[static_cast<size_t>(EShaderType::Geometry)].empty() && sources[static_cast<size_t>(EShaderType::TessEvaluation)].empty() && sources[static_cast<size_t>(EShaderType::TessControl)].empty();
return valid ? init_from_texts(name, sources) : false;
}
bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader)
bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSources& sources)
{
std::string gl_version = gl_get_string_safe(GL_VERSION);
int major = atoi(gl_version.c_str());
//int minor = atoi(gl_version.c_str() + gl_version.find('.') + 1);
if (major < 2) {
// Cannot create a shader object on OpenGL 1.x.
// Form an error message.
std::string gl_vendor = gl_get_string_safe(GL_VENDOR);
std::string gl_renderer = gl_get_string_safe(GL_RENDERER);
std::string glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION);
last_error = "Your computer does not support OpenGL shaders.\n";
#ifdef _WIN32
if (gl_vendor == "Microsoft Corporation" && gl_renderer == "GDI Generic") {
last_error = "Windows is using a software OpenGL renderer.\n"
"You are either connected over remote desktop,\n"
"or a hardware acceleration is not available.\n";
auto shader_type_as_string = [](EShaderType type) {
switch (type)
{
case EShaderType::Vertex: { return "vertex"; }
case EShaderType::Fragment: { return "fragment"; }
case EShaderType::Geometry: { return "geometry"; }
case EShaderType::TessEvaluation: { return "tesselation evaluation"; }
case EShaderType::TessControl: { return "tesselation control"; }
case EShaderType::Compute: { return "compute"; }
default: { return "unknown"; }
}
#endif
last_error += "GL version: " + gl_version + "\n";
last_error += "vendor: " + gl_vendor + "\n";
last_error += "renderer: " + gl_renderer + "\n";
last_error += "GLSL version: " + glsl_version + "\n";
return false;
}
};
if (fragment_shader != nullptr) {
this->fragment_program_id = ::glCreateShader(GL_FRAGMENT_SHADER);
glcheck();
if (this->fragment_program_id == 0) {
last_error = "glCreateShader(GL_FRAGMENT_SHADER) failed.";
return false;
auto create_shader = [](EShaderType type) {
GLuint id = 0;
switch (type)
{
case EShaderType::Vertex: { id = ::glCreateShader(GL_VERTEX_SHADER); glcheck(); break; }
case EShaderType::Fragment: { id = ::glCreateShader(GL_FRAGMENT_SHADER); glcheck(); break; }
case EShaderType::Geometry: { id = ::glCreateShader(GL_GEOMETRY_SHADER); glcheck(); break; }
case EShaderType::TessEvaluation: { id = ::glCreateShader(GL_TESS_EVALUATION_SHADER); glcheck(); break; }
case EShaderType::TessControl: { id = ::glCreateShader(GL_TESS_CONTROL_SHADER); glcheck(); break; }
case EShaderType::Compute: { id = ::glCreateShader(GL_COMPUTE_SHADER); glcheck(); break; }
default: { break; }
}
GLint len = (GLint)strlen(fragment_shader);
glsafe(::glShaderSource(this->fragment_program_id, 1, &fragment_shader, &len));
glsafe(::glCompileShader(this->fragment_program_id));
GLint params;
glsafe(::glGetShaderiv(this->fragment_program_id, GL_COMPILE_STATUS, &params));
if (params == GL_FALSE) {
// Compilation failed. Get the log.
glsafe(::glGetShaderiv(this->fragment_program_id, GL_INFO_LOG_LENGTH, &params));
std::vector<char> msg(params);
glsafe(::glGetShaderInfoLog(this->fragment_program_id, params, &params, msg.data()));
this->last_error = std::string("Fragment shader compilation failed:\n") + msg.data();
this->release();
return false;
return (id == 0) ? std::make_pair(false, GLuint(0)) : std::make_pair(true, id);
};
auto release_shaders = [](const std::array<GLuint, static_cast<size_t>(EShaderType::Count)>& shader_ids) {
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
if (shader_ids[i] > 0)
glsafe(::glDeleteShader(shader_ids[i]));
}
};
assert(m_id == 0);
m_name = name;
std::array<GLuint, static_cast<size_t>(EShaderType::Count)> shader_ids = { 0 };
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
const std::string& source = sources[i];
if (!source.empty())
{
EShaderType type = static_cast<EShaderType>(i);
auto [result, id] = create_shader(type);
if (result)
shader_ids[i] = id;
else {
BOOST_LOG_TRIVIAL(error) << "glCreateShader() failed for " << shader_type_as_string(type) << " shader of shader program '" << name << "'";
// release shaders
release_shaders(shader_ids);
return false;
}
const char* source_ptr = source.c_str();
glsafe(::glShaderSource(id, 1, &source_ptr, nullptr));
glsafe(::glCompileShader(id));
GLint params;
glsafe(::glGetShaderiv(id, GL_COMPILE_STATUS, &params));
if (params == GL_FALSE) {
// Compilation failed.
glsafe(::glGetShaderiv(id, GL_INFO_LOG_LENGTH, &params));
std::vector<char> msg(params);
glsafe(::glGetShaderInfoLog(id, params, &params, msg.data()));
BOOST_LOG_TRIVIAL(error) << "Unable to compile " << shader_type_as_string(type) << " shader of shader program '" << name << "':\n" << msg.data();
// release shaders
release_shaders(shader_ids);
return false;
}
}
}
if (vertex_shader != nullptr) {
this->vertex_program_id = ::glCreateShader(GL_VERTEX_SHADER);
glcheck();
if (this->vertex_program_id == 0) {
last_error = "glCreateShader(GL_VERTEX_SHADER) failed.";
this->release();
return false;
}
GLint len = (GLint)strlen(vertex_shader);
glsafe(::glShaderSource(this->vertex_program_id, 1, &vertex_shader, &len));
glsafe(::glCompileShader(this->vertex_program_id));
GLint params;
glsafe(::glGetShaderiv(this->vertex_program_id, GL_COMPILE_STATUS, &params));
if (params == GL_FALSE) {
// Compilation failed. Get the log.
glsafe(::glGetShaderiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, &params));
std::vector<char> msg(params);
glsafe(::glGetShaderInfoLog(this->vertex_program_id, params, &params, msg.data()));
this->last_error = std::string("Vertex shader compilation failed:\n") + msg.data();
this->release();
return false;
}
}
// Link shaders
this->shader_program_id = ::glCreateProgram();
m_id = ::glCreateProgram();
glcheck();
if (this->shader_program_id == 0) {
last_error = "glCreateProgram() failed.";
this->release();
if (m_id == 0) {
BOOST_LOG_TRIVIAL(error) << "glCreateProgram() failed for shader program '" << name << "'";
// release shaders
release_shaders(shader_ids);
return false;
}
if (this->fragment_program_id)
glsafe(::glAttachShader(this->shader_program_id, this->fragment_program_id));
if (this->vertex_program_id)
glsafe(::glAttachShader(this->shader_program_id, this->vertex_program_id));
glsafe(::glLinkProgram(this->shader_program_id));
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
if (shader_ids[i] > 0)
glsafe(::glAttachShader(m_id, shader_ids[i]));
}
glsafe(::glLinkProgram(m_id));
GLint params;
glsafe(::glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, &params));
glsafe(::glGetProgramiv(m_id, GL_LINK_STATUS, &params));
if (params == GL_FALSE) {
// Linking failed. Get the log.
glsafe(::glGetProgramiv(this->shader_program_id, GL_INFO_LOG_LENGTH, &params));
// Linking failed.
glsafe(::glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &params));
std::vector<char> msg(params);
glsafe(::glGetProgramInfoLog(this->shader_program_id, params, &params, msg.data()));
this->last_error = std::string("Shader linking failed:\n") + msg.data();
this->release();
glsafe(::glGetProgramInfoLog(m_id, params, &params, msg.data()));
BOOST_LOG_TRIVIAL(error) << "Unable to link shader program '" << name << "':\n" << msg.data();
// release shaders
release_shaders(shader_ids);
// release shader program
glsafe(::glDeleteProgram(m_id));
m_id = 0;
return false;
}
last_error.clear();
// release shaders, they are no more needed
release_shaders(shader_ids);
return true;
}
bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename)
void GLShaderProgram::start_using() const
{
const std::string& path = resources_dir() + "/shaders/";
boost::nowide::ifstream vs(path + std::string(vertex_shader_filename), boost::nowide::ifstream::binary);
if (!vs.good())
return false;
vs.seekg(0, vs.end);
int file_length = (int)vs.tellg();
vs.seekg(0, vs.beg);
std::string vertex_shader(file_length, '\0');
vs.read(vertex_shader.data(), file_length);
if (!vs.good())
return false;
vs.close();
boost::nowide::ifstream fs(path + std::string(fragment_shader_filename), boost::nowide::ifstream::binary);
if (!fs.good())
return false;
fs.seekg(0, fs.end);
file_length = (int)fs.tellg();
fs.seekg(0, fs.beg);
std::string fragment_shader(file_length, '\0');
fs.read(fragment_shader.data(), file_length);
if (!fs.good())
return false;
fs.close();
return load_from_text(fragment_shader.c_str(), vertex_shader.c_str());
assert(m_id > 0);
glsafe(::glUseProgram(m_id));
}
void GLShader::release()
{
if (this->shader_program_id) {
if (this->vertex_program_id)
glsafe(::glDetachShader(this->shader_program_id, this->vertex_program_id));
if (this->fragment_program_id)
glsafe(::glDetachShader(this->shader_program_id, this->fragment_program_id));
glsafe(::glDeleteProgram(this->shader_program_id));
this->shader_program_id = 0;
}
if (this->vertex_program_id) {
glsafe(::glDeleteShader(this->vertex_program_id));
this->vertex_program_id = 0;
}
if (this->fragment_program_id) {
glsafe(::glDeleteShader(this->fragment_program_id));
this->fragment_program_id = 0;
}
}
void GLShader::enable() const
{
glsafe(::glUseProgram(this->shader_program_id));
}
void GLShader::disable() const
void GLShaderProgram::stop_using() const
{
glsafe(::glUseProgram(0));
}
// Return shader vertex attribute ID
int GLShader::get_attrib_location(const char *name) const
{
return this->shader_program_id ? glGetAttribLocation(this->shader_program_id, name) : -1;
}
// Return shader uniform variable ID
int GLShader::get_uniform_location(const char *name) const
{
return this->shader_program_id ? glGetUniformLocation(this->shader_program_id, name) : -1;
}
bool GLShader::set_uniform(const char *name, float value) const
{
int id = this->get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform1fARB(id, value));
return true;
}
return false;
}
bool GLShader::set_uniform(const char* name, const float* matrix) const
bool GLShaderProgram::set_uniform(const char* name, int value) const
{
int id = get_uniform_location(name);
if (id >= 0)
{
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix));
if (id >= 0) {
glsafe(::glUniform1i(id, static_cast<GLint>(value)));
return true;
}
return false;
}
bool GLShader::set_uniform(const char* name, int value) const
bool GLShaderProgram::set_uniform(const char* name, bool value) const
{
return set_uniform(name, value ? 1 : 0);
}
bool GLShaderProgram::set_uniform(const char* name, float value) const
{
int id = get_uniform_location(name);
if (id >= 0)
{
glsafe(::glUniform1i(id, value));
if (id >= 0) {
glsafe(::glUniform1f(id, static_cast<GLfloat>(value)));
return true;
}
return false;
}
/*
# Set shader vector
sub SetVector
bool GLShaderProgram::set_uniform(const char* name, double value) const
{
my($self,$var,@values) = @_;
my $id = $self->Map($var);
return 'Unable to map $var' if (!defined($id));
my $count = scalar(@values);
eval('glUniform'.$count.'fARB($id,@values)');
return '';
return set_uniform(name, static_cast<float>(value));
}
# Set shader 4x4 matrix
sub SetMatrix
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 2>& value) const
{
my($self,$var,$oga) = @_;
my $id = $self->Map($var);
return 'Unable to map $var' if (!defined($id));
glUniformMatrix4fvARB_c($id,1,0,$oga->ptr());
return '';
}
*/
Shader::Shader()
: m_shader(nullptr)
{
}
Shader::~Shader()
{
reset();
}
bool Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
{
if (is_initialized())
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform2iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
m_shader = new GLShader();
if (m_shader != nullptr)
{
if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str()))
{
std::cout << "Compilaton of shader failed:" << std::endl;
std::cout << m_shader->last_error << std::endl;
reset();
return false;
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 3>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform3iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 4>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform4iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 3>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 4>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const
{
if (size == 1)
return set_uniform(name, value[0]);
else if (size < 5) {
int id = get_uniform_location(name);
if (id >= 0) {
if (size == 2)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
else if (size == 3)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
else
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
return true;
}
}
return true;
return false;
}
bool Shader::is_initialized() const
bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const
{
return (m_shader != nullptr);
}
bool Shader::start_using() const
{
if (is_initialized())
{
m_shader->enable();
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.matrix().data())));
return true;
}
else
return false;
return false;
}
void Shader::stop_using() const
bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const
{
if (m_shader != nullptr)
m_shader->disable();
return set_uniform(name, value.cast<float>());
}
int Shader::get_attrib_location(const std::string& name) const
bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const
{
return (m_shader != nullptr) ? m_shader->get_attrib_location(name.c_str()) : -1;
}
int Shader::get_uniform_location(const std::string& name) const
{
return (m_shader != nullptr) ? m_shader->get_uniform_location(name.c_str()) : -1;
}
void Shader::set_uniform(const std::string& name, float value) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), value);
}
void Shader::set_uniform(const std::string& name, const float* matrix) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), matrix);
}
void Shader::set_uniform(const std::string& name, bool value) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), value ? 1 : 0);
}
unsigned int Shader::get_shader_program_id() const
{
return (m_shader != nullptr) ? m_shader->shader_program_id : 0;
}
void Shader::reset()
{
if (m_shader != nullptr)
{
m_shader->release();
delete m_shader;
m_shader = nullptr;
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const
{
return set_uniform(name, static_cast<Vec3f>(value.cast<float>()));
}
int GLShaderProgram::get_attrib_location(const char* name) const
{
return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1;
}
int GLShaderProgram::get_uniform_location(const char* name) const
{
return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1;
}
} // namespace Slic3r

View file

@ -1,71 +1,67 @@
#ifndef slic3r_GLShader_hpp_
#define slic3r_GLShader_hpp_
#include "libslic3r/libslic3r.h"
#include "libslic3r/Point.hpp"
#include <array>
#include <string>
namespace Slic3r {
class GLShader
class GLShaderProgram
{
public:
GLShader() :
fragment_program_id(0),
vertex_program_id(0),
shader_program_id(0)
{}
~GLShader();
enum class EShaderType
{
Vertex,
Fragment,
Geometry,
TessEvaluation,
TessControl,
Compute,
Count
};
bool load_from_text(const char *fragment_shader, const char *vertex_shader);
bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename);
void release();
int get_attrib_location(const char *name) const;
int get_uniform_location(const char *name) const;
bool set_uniform(const char *name, float value) const;
bool set_uniform(const char* name, const float* matrix) const;
bool set_uniform(const char* name, int value) const;
void enable() const;
void disable() const;
unsigned int fragment_program_id;
unsigned int vertex_program_id;
unsigned int shader_program_id;
std::string last_error;
};
class Shader
{
GLShader* m_shader;
public:
Shader();
~Shader();
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
bool is_initialized() const;
bool start_using() const;
void stop_using() const;
int get_attrib_location(const std::string& name) const;
int get_uniform_location(const std::string& name) const;
void set_uniform(const std::string& name, float value) const;
void set_uniform(const std::string& name, const float* matrix) const;
void set_uniform(const std::string& name, bool value) const;
const GLShader* get_shader() const { return m_shader; }
unsigned int get_shader_program_id() const;
typedef std::array<std::string, static_cast<size_t>(EShaderType::Count)> ShaderFilenames;
typedef std::array<std::string, static_cast<size_t>(EShaderType::Count)> ShaderSources;
private:
void reset();
std::string m_name;
unsigned int m_id{ 0 };
public:
~GLShaderProgram();
bool init_from_files(const std::string& name, const ShaderFilenames& filenames);
bool init_from_texts(const std::string& name, const ShaderSources& sources);
const std::string& get_name() const { return m_name; }
unsigned int get_id() const { return m_id; }
void start_using() const;
void stop_using() const;
bool set_uniform(const char* name, int value) const;
bool set_uniform(const char* name, bool value) const;
bool set_uniform(const char* name, float value) const;
bool set_uniform(const char* name, double value) const;
bool set_uniform(const char* name, const std::array<int, 2>& value) const;
bool set_uniform(const char* name, const std::array<int, 3>& value) const;
bool set_uniform(const char* name, const std::array<int, 4>& value) const;
bool set_uniform(const char* name, const std::array<float, 2>& value) const;
bool set_uniform(const char* name, const std::array<float, 3>& value) const;
bool set_uniform(const char* name, const std::array<float, 4>& value) const;
bool set_uniform(const char* name, const float* value, size_t size) const;
bool set_uniform(const char* name, const Transform3f& value) const;
bool set_uniform(const char* name, const Transform3d& value) const;
bool set_uniform(const char* name, const Matrix3f& value) const;
bool set_uniform(const char* name, const Vec3f& value) const;
bool set_uniform(const char* name, const Vec3d& value) const;
// returns -1 if not found
int get_attrib_location(const char* name) const;
// returns -1 if not found
int get_uniform_location(const char* name) const;
};
}
} // namespace Slic3r
#endif /* slic3r_GLShader_hpp_ */

View file

@ -0,0 +1,75 @@
#include "libslic3r/libslic3r.h"
#include "GLShadersManager.hpp"
#include "3DScene.hpp"
#include "GUI_App.hpp"
#include <cassert>
#include <algorithm>
#include <GL/glew.h>
namespace Slic3r {
std::pair<bool, std::string> GLShadersManager::init()
{
std::string error;
auto append_shader = [this, &error](const std::string& name, const GLShaderProgram::ShaderFilenames& filenames) {
m_shaders.push_back(std::make_unique<GLShaderProgram>());
if (!m_shaders.back()->init_from_files(name, filenames)) {
error += name + "\n";
// if any error happens while initializating the shader, we remove it from the list
m_shaders.pop_back();
return false;
}
return true;
};
assert(m_shaders.empty());
bool valid = true;
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
// used to render printbed
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
// used to render options in gcode preview
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
// used to render extrusion and travel paths as lines in gcode preview
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
// used to render objects in 3d editor
valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" });
// used to render variable layers heights in 3d editor
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
return { valid, error };
}
void GLShadersManager::shutdown()
{
for (std::unique_ptr<GLShaderProgram>& shader : m_shaders) {
shader.reset();
}
}
GLShaderProgram* GLShadersManager::get_shader(const std::string& shader_name)
{
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [shader_name](std::unique_ptr<GLShaderProgram>& p) { return p->get_name() == shader_name; });
return (it != m_shaders.end()) ? it->get() : nullptr;
}
GLShaderProgram* GLShadersManager::get_current_shader()
{
GLint id = 0;
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &id));
if (id == 0)
return nullptr;
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [id](std::unique_ptr<GLShaderProgram>& p) { return static_cast<GLint>(p->get_id()) == id; });
return (it != m_shaders.end()) ? it->get() : nullptr;
}
} // namespace Slic3r

View file

@ -0,0 +1,30 @@
#ifndef slic3r_GLShadersManager_hpp_
#define slic3r_GLShadersManager_hpp_
#include "GLShader.hpp"
#include <vector>
#include <string>
#include <memory>
namespace Slic3r {
class GLShadersManager
{
std::vector<std::unique_ptr<GLShaderProgram>> m_shaders;
public:
std::pair<bool, std::string> init();
// call this method before to release the OpenGL context
void shutdown();
// returns nullptr if not found
GLShaderProgram* get_shader(const std::string& shader_name);
// returns currently active shader, nullptr if none
GLShaderProgram* get_current_shader();
};
} // namespace Slic3r
#endif // slic3r_GLShadersManager_hpp_

View file

@ -230,24 +230,13 @@ void GLToolbar::set_icons_size(float size)
void GLToolbar::set_scale(float scale)
{
if (m_layout.scale != scale)
{
if (m_layout.scale != scale) {
m_layout.scale = scale;
m_layout.dirty = true;
m_icons_texture_dirty = true;
}
}
bool GLToolbar::is_enabled() const
{
return m_enabled;
}
void GLToolbar::set_enabled(bool enable)
{
m_enabled = enable;//true; etFIXME
}
bool GLToolbar::add_item(const GLToolbarItem::Data& data)
{
GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data);

View file

@ -276,8 +276,8 @@ public:
void set_icons_size(float size);
void set_scale(float scale);
bool is_enabled() const;
void set_enabled(bool enable);
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
bool add_item(const GLToolbarItem::Data& data);
bool add_separator();

View file

@ -255,7 +255,7 @@ void warning_catcher(wxWindow* parent, const wxString& message)
msg.ShowModal();
}
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items)
{
if (comboCtrl == nullptr)
return;
@ -266,41 +266,59 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string
// On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
comboCtrl->UseAltPopupWindow();
int max_width = 0;
// the following line messes up the popup size the first time it is shown on wxWidgets 3.1.3
// comboCtrl->EnablePopupAnimation(false);
comboCtrl->SetPopupControl(popup);
popup->SetStringValue(from_u8(text));
popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); });
popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); });
wxString title = from_u8(text);
max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x);
popup->SetStringValue(title);
popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); });
popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); });
popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
std::vector<std::string> items_str;
boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off);
for (const std::string& item : items_str) {
popup->Append(from_u8(item));
}
// each item must be composed by 2 parts
assert(items_str.size() %2 == 0);
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
popup->Check(i, initial_value);
}
}
for (size_t i = 0; i < items_str.size(); i += 2) {
wxString label = from_u8(items_str[i]);
max_width = std::max(max_width, 60 + popup->GetTextExtent(label).x);
popup->Append(label);
popup->Check(i / 2, items_str[i + 1] == "1");
}
comboCtrl->SetMinClientSize(wxSize(max_width, -1));
}
}
int combochecklist_get_flags(wxComboCtrl* comboCtrl)
unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl)
{
int flags = 0;
unsigned int flags = 0;
wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup);
if (popup != nullptr) {
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
if (popup->IsChecked(i))
flags |= 1 << i;
}
}
wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup);
if (popup != nullptr) {
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
if (popup->IsChecked(i))
flags |= 1 << i;
}
}
return flags;
return flags;
}
void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags)
{
wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup);
if (popup != nullptr) {
for (unsigned int i = 0; i < popup->GetCount(); ++i) {
popup->Check(i, (flags & (1 << i)) != 0);
}
}
}
AppConfig* get_app_config()

View file

@ -49,13 +49,17 @@ inline void show_info(wxWindow* parent, const std::string& message,const std::st
void warning_catcher(wxWindow* parent, const wxString& message);
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
// Items are all initialized to the given value.
// Items must be separated by '|', for example "Item1|Item2|Item3", and so on.
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value);
// Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true).
// For example "Item1|0|Item2|1|Item3|0", and so on.
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items);
// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl,
// encoded inside an int.
int combochecklist_get_flags(wxComboCtrl* comboCtrl);
// encoded inside an unsigned int.
unsigned int combochecklist_get_flags(wxComboCtrl* comboCtrl);
// Sets the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl,
// with the flags encoded in the given unsigned int.
void combochecklist_set_flags(wxComboCtrl* comboCtrl, unsigned int flags);
// wxString conversions:

View file

@ -789,6 +789,20 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const
dialog.GetPaths(input_files);
}
#if ENABLE_GCODE_VIEWER
void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const
{
input_file.Clear();
wxFileDialog dialog(parent ? parent : GetTopWindow(),
_(L("Choose one file (GCODE/.GCO/.G/.ngc/NGC):")),
app_config->get_last_dir(), "",
file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() == wxID_OK)
input_file = dialog.GetPath();
}
#endif // ENABLE_GCODE_VIEWER
bool GUI_App::switch_language()
{
if (select_language()) {

View file

@ -157,6 +157,10 @@ public:
void keyboard_shortcuts();
void load_project(wxWindow *parent, wxString& input_file) const;
void import_model(wxWindow *parent, wxArrayString& input_files) const;
#if ENABLE_GCODE_VIEWER
void load_gcode(wxWindow* parent, wxString& input_file) const;
#endif // ENABLE_GCODE_VIEWER
static bool catch_error(std::function<void()> cb, const std::string& err);
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
@ -231,6 +235,12 @@ public:
void gcode_thumbnails_debug();
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
GLShaderProgram* get_shader(const std::string& shader_name) { return m_opengl_mgr.get_shader(shader_name); }
GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); }
bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); }
bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); }
private:
bool on_init_inner();
void init_app_config();
@ -249,6 +259,6 @@ private:
DECLARE_APP(GUI_App)
} // GUI
} //Slic3r
} // Slic3r
#endif // slic3r_GUI_App_hpp_

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,9 @@
#include "libslic3r/CustomGCode.hpp"
#include <string>
#if ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/GCodeProcessor.hpp"
#endif // ENABLE_GCODE_VIEWER
class wxNotebook;
class wxGLCanvas;
@ -23,7 +25,9 @@ namespace Slic3r {
class DynamicPrintConfig;
class Print;
class BackgroundSlicingProcess;
#if !ENABLE_GCODE_VIEWER
class GCodePreviewData;
#endif // !ENABLE_GCODE_VIEWER
class Model;
namespace DoubleSlider {
@ -79,20 +83,35 @@ class Preview : public wxPanel
{
wxGLCanvas* m_canvas_widget;
GLCanvas3D* m_canvas;
#if ENABLE_GCODE_VIEWER
wxBoxSizer* m_left_sizer;
wxBoxSizer* m_layers_slider_sizer;
wxPanel* m_bottom_toolbar_panel;
#else
wxBoxSizer* m_double_slider_sizer;
#endif // ENABLE_GCODE_VIEWER
wxStaticText* m_label_view_type;
wxChoice* m_choice_view_type;
wxStaticText* m_label_show_features;
wxStaticText* m_label_show;
wxComboCtrl* m_combochecklist_features;
#if ENABLE_GCODE_VIEWER
size_t m_combochecklist_features_pos;
wxComboCtrl* m_combochecklist_options;
#else
wxCheckBox* m_checkbox_travel;
wxCheckBox* m_checkbox_retractions;
wxCheckBox* m_checkbox_unretractions;
wxCheckBox* m_checkbox_shells;
wxCheckBox* m_checkbox_legend;
#endif // ENABLE_GCODE_VIEWER
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
#if ENABLE_GCODE_VIEWER
GCodeProcessor::Result* m_gcode_result;
#else
GCodePreviewData* m_gcode_preview_data;
#endif // ENABLE_GCODE_VIEWER
#ifdef __linux__
// We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955.
@ -107,13 +126,39 @@ class Preview : public wxPanel
std::string m_preferred_color_mode;
bool m_loaded;
#if !ENABLE_GCODE_VIEWER
bool m_enabled;
#endif // !ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
DoubleSlider::Control* m_layers_slider{ nullptr };
DoubleSlider::Control* m_moves_slider{ nullptr };
#else
DoubleSlider::Control* m_slider {nullptr};
#endif // ENABLE_GCODE_VIEWER
public:
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config,
#if ENABLE_GCODE_VIEWER
enum class OptionType : unsigned int
{
Travel,
Retractions,
Unretractions,
ToolChanges,
ColorChanges,
PausePrints,
CustomGCodes,
Shells,
ToolMarker,
Legend
};
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process = []() {});
#else
Preview(wxWindow* parent, Model* model, DynamicPrintConfig* config,
BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process = []() {});
#endif // ENABLE_GCODE_VIEWER
virtual ~Preview();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
@ -122,7 +167,9 @@ public:
void set_as_dirty();
void set_number_extruders(unsigned int number_extruders);
#if !ENABLE_GCODE_VIEWER
void set_enabled(bool enabled);
#endif // !ENABLE_GCODE_VIEWER
void bed_shape_changed();
void select_view(const std::string& direction);
void set_drop_target(wxDropTarget* target);
@ -132,47 +179,83 @@ public:
void refresh_print();
void msw_rescale();
#if ENABLE_GCODE_VIEWER
void move_layers_slider(wxKeyEvent& evt);
void edit_layers_slider(wxKeyEvent& evt);
#else
void move_double_slider(wxKeyEvent& evt);
void edit_double_slider(wxKeyEvent& evt);
#endif // ENABLE_GCODE_VIEWER
void update_view_type(bool slice_completed);
void update_view_type(bool keep_volumes);
bool is_loaded() const { return m_loaded; }
#if ENABLE_GCODE_VIEWER
void update_bottom_toolbar();
void update_moves_slider();
#endif // ENABLE_GCODE_VIEWER
private:
bool init(wxWindow* parent, Model* model);
void bind_event_handlers();
void unbind_event_handlers();
#if ENABLE_GCODE_VIEWER
void hide_layers_slider();
#else
void show_hide_ui_elements(const std::string& what);
void reset_sliders(bool reset_all);
void update_sliders(const std::vector<double>& layers_z, bool keep_z_range = false);
#endif // ENABLE_GCODE_VIEWER
void on_size(wxSizeEvent& evt);
void on_choice_view_type(wxCommandEvent& evt);
void on_combochecklist_features(wxCommandEvent& evt);
#if ENABLE_GCODE_VIEWER
void on_combochecklist_options(wxCommandEvent& evt);
#else
void on_checkbox_travel(wxCommandEvent& evt);
void on_checkbox_retractions(wxCommandEvent& evt);
void on_checkbox_unretractions(wxCommandEvent& evt);
void on_checkbox_shells(wxCommandEvent& evt);
void on_checkbox_legend(wxCommandEvent& evt);
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
// Create/Update/Reset double slider on 3dPreview
wxBoxSizer* create_layers_slider_sizer();
void check_layers_slider_values(std::vector<CustomGCode::Item>& ticks_from_model,
const std::vector<double>& layers_z);
void reset_layers_slider();
void update_layers_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void update_layers_slider_mode();
// update vertical DoubleSlider after keyDown in canvas
void update_layers_slider_from_canvas(wxKeyEvent& event);
#else
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();
void check_slider_values(std::vector<CustomGCode::Item> &ticks_from_model,
const std::vector<double> &layers_z);
void check_slider_values(std::vector<CustomGCode::Item>& ticks_from_model,
const std::vector<double>& layers_z);
void reset_double_slider();
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void update_double_slider_mode();
// update DoubleSlider after keyDown in canvas
void update_double_slider_from_canvas(wxKeyEvent& event);
#endif // ENABLE_GCODE_VIEWER
void load_print_as_fff(bool keep_z_range = false);
void load_print_as_sla();
#if ENABLE_GCODE_VIEWER
void on_layers_slider_scroll_changed(wxCommandEvent& event);
void on_moves_slider_scroll_changed(wxCommandEvent& event);
wxString get_option_type_string(OptionType type) const;
#else
void on_sliders_scroll_changed(wxCommandEvent& event);
#endif // ENABLE_GCODE_VIEWER
};
} // namespace GUI

View file

@ -387,6 +387,15 @@ public:
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics);
#if ENABLE_GCODE_VIEWER
inline int hex_digit_to_int(const char c)
{
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
#endif // ENABLE_GCODE_VIEWER
}}

View file

@ -535,8 +535,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
window_width = std::max(window_width, button_width);
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
m_imgui->text_colored(ORANGE, caption);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, caption);
ImGui::SameLine(caption_max);
m_imgui->text(text);
};

View file

@ -50,6 +50,15 @@ static const std::map<const char, std::string> font_icons = {
{ImGui::ErrorMarker , "flag_red" }
};
const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };
const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f };
const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f };
const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f };
const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.133f, 0.133f, 0.133f, 0.8f };
const ImVec4 ImGuiWrapper::COL_BUTTON_BACKGROUND = { 0.233f, 0.233f, 0.233f, 1.0f };
const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = { 0.433f, 0.433f, 0.433f, 1.8f };
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED;
ImGuiWrapper::ImGuiWrapper()
: m_glyph_ranges(nullptr)
, m_font_cjk(false)
@ -792,6 +801,12 @@ void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, co
check_box(_L("Search in English"), view_params.english);
}
void ImGuiWrapper::title(const std::string& str)
{
text(str);
ImGui::Separator();
}
void ImGuiWrapper::disabled_begin(bool disabled)
{
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
@ -1011,23 +1026,13 @@ void ImGuiWrapper::init_style()
{
ImGuiStyle &style = ImGui::GetStyle();
auto set_color = [&](ImGuiCol_ col, unsigned hex_color) {
style.Colors[col] = ImVec4(
((hex_color >> 24) & 0xff) / 255.0f,
((hex_color >> 16) & 0xff) / 255.0f,
((hex_color >> 8) & 0xff) / 255.0f,
(hex_color & 0xff) / 255.0f);
auto set_color = [&](ImGuiCol_ entity, ImVec4 color) {
style.Colors[entity] = color;
};
static const unsigned COL_WINDOW_BACKGROND = 0x222222cc;
static const unsigned COL_GREY_DARK = 0x555555ff;
static const unsigned COL_GREY_LIGHT = 0x666666ff;
static const unsigned COL_ORANGE_DARK = 0xc16737ff;
static const unsigned COL_ORANGE_LIGHT = 0xff7d38ff;
// Window
style.WindowRounding = 4.0f;
set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROND);
set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROUND);
set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);
// Generics
@ -1039,9 +1044,9 @@ void ImGuiWrapper::init_style()
set_color(ImGuiCol_TextSelectedBg, COL_ORANGE_DARK);
// Buttons
set_color(ImGuiCol_Button, COL_ORANGE_DARK);
set_color(ImGuiCol_ButtonHovered, COL_ORANGE_LIGHT);
set_color(ImGuiCol_ButtonActive, COL_ORANGE_LIGHT);
set_color(ImGuiCol_Button, COL_BUTTON_BACKGROUND);
set_color(ImGuiCol_ButtonHovered, COL_BUTTON_HOVERED);
set_color(ImGuiCol_ButtonActive, COL_BUTTON_ACTIVE);
// Checkbox
set_color(ImGuiCol_CheckMark, COL_ORANGE_LIGHT);
@ -1057,6 +1062,13 @@ void ImGuiWrapper::init_style()
// Separator
set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT);
// Tabs
set_color(ImGuiCol_Tab, COL_ORANGE_DARK);
set_color(ImGuiCol_TabHovered, COL_ORANGE_LIGHT);
set_color(ImGuiCol_TabActive, COL_ORANGE_LIGHT);
set_color(ImGuiCol_TabUnfocused, COL_GREY_DARK);
set_color(ImGuiCol_TabUnfocusedActive, COL_GREY_LIGHT);
}
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)

View file

@ -86,6 +86,7 @@ public:
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized);
void title(const std::string& str);
void disabled_begin(bool disabled);
void disabled_end();
@ -95,6 +96,15 @@ public:
bool want_text_input() const;
bool want_any_input() const;
static const ImVec4 COL_GREY_DARK;
static const ImVec4 COL_GREY_LIGHT;
static const ImVec4 COL_ORANGE_DARK;
static const ImVec4 COL_ORANGE_LIGHT;
static const ImVec4 COL_WINDOW_BACKGROUND;
static const ImVec4 COL_BUTTON_BACKGROUND;
static const ImVec4 COL_BUTTON_HOVERED;
static const ImVec4 COL_BUTTON_ACTIVE;
private:
void init_font(bool compress);
void init_input();

View file

@ -1,3 +1,4 @@
#include "libslic3r/libslic3r.h"
#include "KBShortcutsDialog.hpp"
#include "I18N.hpp"
#include "libslic3r/Utils.hpp"
@ -6,6 +7,9 @@
#include <wx/display.h>
#include "GUI_App.hpp"
#include "wxExtensions.hpp"
#if ENABLE_GCODE_VIEWER
#include "MainFrame.hpp"
#endif // ENABLE_GCODE_VIEWER
#define NOTEBOOK_TOP 1
#define NOTEBOOK_LEFT 2
@ -29,12 +33,8 @@ namespace Slic3r {
namespace GUI {
KBShortcutsDialog::KBShortcutsDialog()
: DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")),
#if ENABLE_SCROLLABLE
: DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Keyboard Shortcuts"),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
#else
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
#endif // ENABLE_SCROLLABLE
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@ -65,13 +65,9 @@ main_sizer->Add(book, 1, wxEXPAND | wxALL, 10);
fill_shortcuts();
for (size_t i = 0; i < m_full_shortcuts.size(); ++i)
{
#if ENABLE_SCROLLABLE
wxPanel* page = create_page(book, m_full_shortcuts[i], font, bold_font);
m_pages.push_back(page);
book->AddPage(page, m_full_shortcuts[i].first, i == 0);
#else
book->AddPage(create_page(book, m_full_shortcuts[i], font, bold_font), m_full_shortcuts[i].first, i == 0);
#endif // ENABLE_SCROLLABLE
}
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK);
@ -98,114 +94,122 @@ void KBShortcutsDialog::fill_shortcuts()
const std::string& ctrl = GUI::shortkey_ctrl_prefix();
const std::string& alt = GUI::shortkey_alt_prefix();
Shortcuts commands_shortcuts = {
// File
{ ctrl + "N", L("New project, clear plater") },
{ ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") },
{ ctrl + "S", L("Save project (3mf)") },
{ ctrl + alt + "S", L("Save project as (3mf)") },
{ ctrl + "R", L("(Re)slice") },
// File>Import
{ ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") },
{ ctrl + "L", L("Import Config from ini/amf/3mf/gcode") },
{ ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") },
// File>Export
{ ctrl + "G", L("Export G-code") },
{ ctrl + "Shift+" + "G", L("Send G-code") },
{ ctrl + "E", L("Export config") },
{ ctrl + "U", L("Export to SD card / Flash drive") },
{ ctrl + "T", L("Eject SD card / Flash drive") },
// Edit
{ ctrl + "A", L("Select all objects") },
{ "Esc", L("Deselect all") },
{ "Del", L("Delete selected") },
{ ctrl + "Del", L("Delete all") },
{ ctrl + "Z", L("Undo") },
{ ctrl + "Y", L("Redo") },
{ ctrl + "C", L("Copy to clipboard") },
{ ctrl + "V", L("Paste from clipboard") },
{ "F5", L("Reload plater from disk") },
{ ctrl + "F", L("Search") },
// Window
{ ctrl + "1", L("Select Plater Tab") },
{ ctrl + "2", L("Select Print Settings Tab") },
{ ctrl + "3", L("Select Filament Settings Tab") },
{ ctrl + "4", L("Select Printer Settings Tab") },
{ ctrl + "5", L("Switch to 3D") },
{ ctrl + "6", L("Switch to Preview") },
{ ctrl + "J", L("Print host upload queue") },
// View
{ "0-6", L("Camera view") },
{ "E", L("Show/Hide object/instance labels") },
#if ENABLE_GCODE_VIEWER
bool is_gcode_viewer = wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer;
if (!is_gcode_viewer) {
#endif // ENABLE_GCODE_VIEWER
Shortcuts commands_shortcuts = {
// File
{ ctrl + "N", L("New project, clear plater") },
{ ctrl + "O", L("Open project STL/OBJ/AMF/3MF with config, clear plater") },
{ ctrl + "S", L("Save project (3mf)") },
{ ctrl + alt + "S", L("Save project as (3mf)") },
{ ctrl + "R", L("(Re)slice") },
// File>Import
{ ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") },
{ ctrl + "L", L("Import Config from ini/amf/3mf/gcode") },
{ ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") },
// File>Export
{ ctrl + "G", L("Export G-code") },
{ ctrl + "Shift+" + "G", L("Send G-code") },
{ ctrl + "E", L("Export config") },
{ ctrl + "U", L("Export to SD card / Flash drive") },
{ ctrl + "T", L("Eject SD card / Flash drive") },
// Edit
{ ctrl + "A", L("Select all objects") },
{ "Esc", L("Deselect all") },
{ "Del", L("Delete selected") },
{ ctrl + "Del", L("Delete all") },
{ ctrl + "Z", L("Undo") },
{ ctrl + "Y", L("Redo") },
{ ctrl + "C", L("Copy to clipboard") },
{ ctrl + "V", L("Paste from clipboard") },
{ "F5", L("Reload plater from disk") },
{ ctrl + "F", L("Search") },
// Window
{ ctrl + "1", L("Select Plater Tab") },
{ ctrl + "2", L("Select Print Settings Tab") },
{ ctrl + "3", L("Select Filament Settings Tab") },
{ ctrl + "4", L("Select Printer Settings Tab") },
{ ctrl + "5", L("Switch to 3D") },
{ ctrl + "6", L("Switch to Preview") },
{ ctrl + "J", L("Print host upload queue") },
// View
{ "0-6", L("Camera view") },
{ "E", L("Show/Hide object/instance labels") },
#if ENABLE_SLOPE_RENDERING
{ "D", L("Turn On/Off facets' slope rendering") },
{ "D", L("Turn On/Off facets' slope rendering") },
#endif // ENABLE_SLOPE_RENDERING
// Configuration
{ ctrl + "P", L("Preferences") },
// Help
{ "?", L("Show keyboard shortcuts list") }
};
// Configuration
{ ctrl + "P", L("Preferences") },
// Help
{ "?", L("Show keyboard shortcuts list") }
};
m_full_shortcuts.push_back(std::make_pair(_(L("Commands")), commands_shortcuts));
m_full_shortcuts.push_back(std::make_pair(_L("Commands"), commands_shortcuts));
Shortcuts plater_shortcuts = {
{ "A", L("Arrange") },
{ "Shift+A", L("Arrange selection") },
{ "+", L("Add Instance of the selected object") },
{ "-", L("Remove Instance of the selected object") },
{ ctrl, L("Press to select multiple objects\nor move multiple objects with mouse") },
{ "Shift+", L("Press to activate selection rectangle") },
{ alt, L("Press to activate deselection rectangle") },
{ L("Arrow Up"), L("Move selection 10 mm in positive Y direction") },
{ L("Arrow Down"), L("Move selection 10 mm in negative Y direction") },
{ L("Arrow Left"), L("Move selection 10 mm in negative X direction") },
{ L("Arrow Right"), L("Move selection 10 mm in positive X direction") },
{ std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") },
{ ctrl + L("Any arrow"), L("Movement in camera space") },
{ L("Page Up"), L("Rotate selection 45 degrees CCW") },
{ L("Page Down"), L("Rotate selection 45 degrees CW") },
{ "M", L("Gizmo move") },
{ "S", L("Gizmo scale") },
{ "R", L("Gizmo rotate") },
{ "C", L("Gizmo cut") },
{ "F", L("Gizmo Place face on bed") },
{ "H", L("Gizmo SLA hollow") },
{ "L", L("Gizmo SLA support points") },
{ "Esc", L("Unselect gizmo or clear selection") },
{ "K", L("Change camera type (perspective, orthographic)") },
{ "B", L("Zoom to Bed") },
{ "Z", L("Zoom to selected object\nor all objects in scene, if none selected") },
{ "I", L("Zoom in") },
{ "O", L("Zoom out") },
Shortcuts plater_shortcuts = {
{ "A", L("Arrange") },
{ "Shift+A", L("Arrange selection") },
{ "+", L("Add Instance of the selected object") },
{ "-", L("Remove Instance of the selected object") },
{ ctrl, L("Press to select multiple objects\nor move multiple objects with mouse") },
{ "Shift+", L("Press to activate selection rectangle") },
{ alt, L("Press to activate deselection rectangle") },
{ L("Arrow Up"), L("Move selection 10 mm in positive Y direction") },
{ L("Arrow Down"), L("Move selection 10 mm in negative Y direction") },
{ L("Arrow Left"), L("Move selection 10 mm in negative X direction") },
{ L("Arrow Right"), L("Move selection 10 mm in positive X direction") },
{ std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") },
{ ctrl + L("Any arrow"), L("Movement in camera space") },
{ L("Page Up"), L("Rotate selection 45 degrees CCW") },
{ L("Page Down"), L("Rotate selection 45 degrees CW") },
{ "M", L("Gizmo move") },
{ "S", L("Gizmo scale") },
{ "R", L("Gizmo rotate") },
{ "C", L("Gizmo cut") },
{ "F", L("Gizmo Place face on bed") },
{ "H", L("Gizmo SLA hollow") },
{ "L", L("Gizmo SLA support points") },
{ "Esc", L("Unselect gizmo or clear selection") },
{ "K", L("Change camera type (perspective, orthographic)") },
{ "B", L("Zoom to Bed") },
{ "Z", L("Zoom to selected object\nor all objects in scene, if none selected") },
{ "I", L("Zoom in") },
{ "O", L("Zoom out") },
#ifdef __linux__
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
#endif // __linux__
#if ENABLE_RENDER_PICKING_PASS
// Don't localize debugging texts.
{ "T", "Toggle picking pass texture rendering on/off" },
// Don't localize debugging texts.
{ "P", "Toggle picking pass texture rendering on/off" },
#endif // ENABLE_RENDER_PICKING_PASS
};
};
m_full_shortcuts.push_back(std::make_pair(_(L("Plater")), plater_shortcuts));
m_full_shortcuts.push_back(std::make_pair(_L("Plater"), plater_shortcuts));
Shortcuts gizmos_shortcuts = {
{ "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") },
{ "F", L("Scale selection to fit print volume\nin Gizmo scale") },
{ ctrl, L("Press to activate one direction scaling in Gizmo scale") },
{ alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") },
};
Shortcuts gizmos_shortcuts = {
{ "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") },
{ "F", L("Scale selection to fit print volume\nin Gizmo scale") },
{ ctrl, L("Press to activate one direction scaling in Gizmo scale") },
{ alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") },
};
m_full_shortcuts.push_back(std::make_pair(_(L("Gizmos")), gizmos_shortcuts));
m_full_shortcuts.push_back(std::make_pair(_L("Gizmos"), gizmos_shortcuts));
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
Shortcuts preview_shortcuts = {
{ L("Arrow Up"), L("Upper Layer") },
{ L("Arrow Down"), L("Lower Layer") },
{ "U", L("Upper Layer") },
{ "D", L("Lower Layer") },
{ "L", L("Show/Hide Legend") }
{ "L", L("Show/Hide Legend/Estimated printing time") },
};
m_full_shortcuts.push_back(std::make_pair(_(L("Preview")), preview_shortcuts));
m_full_shortcuts.push_back(std::make_pair(_L("Preview"), preview_shortcuts));
Shortcuts layers_slider_shortcuts = {
{ L("Arrow Up"), L("Move current slider thumb Up") },
@ -213,10 +217,23 @@ void KBShortcutsDialog::fill_shortcuts()
{ L("Arrow Left"), L("Set upper thumb to current slider thumb") },
{ L("Arrow Right"), L("Set lower thumb to current slider thumb") },
{ "+", L("Add color change marker for current layer") },
{ "-", L("Delete color change marker for current layer") }
{ "-", L("Delete color change marker for current layer") },
{ "Shift+", L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") },
{ ctrl, L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") },
};
m_full_shortcuts.push_back(std::make_pair(_(L("Layers Slider")), layers_slider_shortcuts));
m_full_shortcuts.push_back(std::make_pair(_L("Layers Slider"), layers_slider_shortcuts));
#if ENABLE_GCODE_VIEWER
Shortcuts sequential_slider_shortcuts = {
{ L("Arrow Left"), L("Move current slider thumb Left") },
{ L("Arrow Right"), L("Move current slider thumb Right") },
{ "Shift+", L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") },
{ ctrl, L("Press to speed up 5 times while moving thumb\nwith arrow keys or mouse wheel") },
};
m_full_shortcuts.push_back(std::make_pair(_L("Sequential Slider"), sequential_slider_shortcuts));
#endif // ENABLE_GCODE_VIEWER
}
wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_font)
@ -239,7 +256,7 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f
sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
// text
wxStaticText* text = new wxStaticText(panel, wxID_ANY, _(L("Keyboard shortcuts")));
wxStaticText* text = new wxStaticText(panel, wxID_ANY, _L("Keyboard shortcuts"));
text->SetFont(header_font);
sizer->Add(text, 0, wxALIGN_CENTER_VERTICAL);
@ -254,13 +271,9 @@ wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const std::pair<wxStri
static const int max_items_per_column = 20;
int columns_count = 1 + (int)shortcuts.second.size() / max_items_per_column;
#if ENABLE_SCROLLABLE
wxScrolledWindow* page = new wxScrolledWindow(parent);
page->SetScrollbars(20, 20, 50, 50);
page->SetInitialSize(wxSize(850, 450));
#else
wxPanel* page = new wxPanel(parent);
#endif // ENABLE_SCROLLABLE
#if (BOOK_TYPE == LISTBOOK_TOP) || (BOOK_TYPE == LISTBOOK_LEFT)
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, page, " " + shortcuts.first + " ");

View file

@ -8,8 +8,6 @@
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#define ENABLE_SCROLLABLE 1
namespace Slic3r {
namespace GUI {
@ -22,9 +20,7 @@ class KBShortcutsDialog : public DPIDialog
ShortcutsVec m_full_shortcuts;
ScalableBitmap m_logo_bmp;
wxStaticBitmap* m_header_bitmap;
#if ENABLE_SCROLLABLE
std::vector<wxPanel*> m_pages;
#endif // ENABLE_SCROLLABLE
public:
KBShortcutsDialog();

View file

@ -92,16 +92,41 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
#endif
// Font is already set in DPIFrame constructor
*/
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
TCHAR szExeFileName[MAX_PATH];
GetModuleFileName(nullptr, szExeFileName, MAX_PATH);
SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO));
}
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
if (wxTaskBarIcon::IsAvailable()) {
#if defined(__WXOSX__) && wxOSX_USE_COCOA
m_taskbar_icon = new wxTaskBarIcon(wxTBI_DOCK);
#else
m_taskbar_icon = new wxTaskBarIcon();
#endif
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
m_taskbar_icon->Bind(wxEVT_TASKBAR_CLICK, [this](wxTaskBarIconEvent& evt) {
wxString msg = _L("You pressed the icon in taskbar for ") + "\n";
if (m_mode == EMode::Editor)
msg += wxString(SLIC3R_APP_NAME);
else
msg += wxString(SLIC3R_APP_NAME) + "-GCode viewer";
wxMessageDialog dialog(nullptr, msg, _("Taskbar icon clicked"), wxOK);
dialog.ShowModal();
});
}
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
#endif // _WIN32
// // Load the icon either from the exe, or from the ico file.
//#if _WIN32
// {
//
// TCHAR szExeFileName[MAX_PATH];
// GetModuleFileName(nullptr, szExeFileName, MAX_PATH);
// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO));
// }
//#else
// SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
//#endif // _WIN32
// initialize status bar
m_statusbar = std::make_shared<ProgressStatusBar>(this);
@ -113,7 +138,25 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// initialize tabpanel and menubar
init_tabpanel();
#if ENABLE_GCODE_VIEWER
init_editor_menubar();
init_gcodeviewer_menubar();
#if _WIN32
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
wxAcceleratorEntry entries[6];
entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1);
entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2);
entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3);
entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4);
entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5);
entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6);
wxAcceleratorTable accel(6, entries);
SetAcceleratorTable(accel);
#endif // _WIN32
#else
init_menubar();
#endif // ENABLE_GCODE_VIEWER
// set default tooltip timer in msec
// SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
@ -226,10 +269,21 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
update_ui_from_settings(); // FIXME (?)
if (m_plater != nullptr)
if (m_plater != nullptr) {
#if ENABLE_GCODE_VIEWER
m_plater->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1");
#endif // ENABLE_GCODE_VIEWER
m_plater->show_action_buttons(true);
}
}
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
MainFrame::~MainFrame()
{
delete m_taskbar_icon;
}
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
void MainFrame::update_layout()
{
auto restore_to_creation = [this]() {
@ -271,9 +325,16 @@ void MainFrame::update_layout()
Layout();
};
#if ENABLE_GCODE_VIEWER
ESettingsLayout layout = (m_mode == EMode::GCodeViewer) ? ESettingsLayout::GCodeViewer :
(wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old :
wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New :
wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old);
#else
ESettingsLayout layout = wxGetApp().app_config->get("old_settings_layout_mode") == "1" ? ESettingsLayout::Old :
wxGetApp().app_config->get("new_settings_layout_mode") == "1" ? ESettingsLayout::New :
wxGetApp().app_config->get("dlg_settings_layout_mode") == "1" ? ESettingsLayout::Dlg : ESettingsLayout::Old;
#endif // ENABLE_GCODE_VIEWER
if (m_layout == layout)
return;
@ -294,6 +355,10 @@ void MainFrame::update_layout()
// Set new settings
switch (m_layout)
{
case ESettingsLayout::Unknown:
{
break;
}
case ESettingsLayout::Old:
{
m_plater->Reparent(m_tabpanel);
@ -325,6 +390,14 @@ void MainFrame::update_layout()
m_plater->Show();
break;
}
#if ENABLE_GCODE_VIEWER
case ESettingsLayout::GCodeViewer:
{
m_main_sizer->Add(m_plater, 1, wxEXPAND);
m_plater->Show();
break;
}
#endif // ENABLE_GCODE_VIEWER
}
//#ifdef __APPLE__
@ -359,6 +432,20 @@ void MainFrame::shutdown()
}
#endif // _WIN32
#if ENABLE_GCODE_VIEWER
if (m_plater != nullptr) {
m_plater->stop_jobs();
// Unbinding of wxWidgets event handling in canvases needs to be done here because on MAC,
// when closing the application using Command+Q, a mouse event is triggered after this lambda is completed,
// causing a crash
m_plater->unbind_canvas_event_handlers();
// Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours
// see: https://github.com/prusa3d/PrusaSlicer/issues/3964
m_plater->reset_canvas_volumes();
}
#else
if (m_plater)
m_plater->stop_jobs();
@ -370,6 +457,7 @@ void MainFrame::shutdown()
// Cleanup of canvases' volumes needs to be done here or a crash may happen on some Linux Debian flavours
// see: https://github.com/prusa3d/PrusaSlicer/issues/3964
if (m_plater) m_plater->reset_canvas_volumes();
#endif // ENABLE_GCODE_VIEWER
// Weird things happen as the Paint messages are floating around the windows being destructed.
// Avoid the Paint messages by hiding the main window.
@ -381,11 +469,22 @@ void MainFrame::shutdown()
// call Close() to trigger call to lambda defined into GUI_App::persist_window_geometry()
m_settings_dialog.Close();
// Stop the background thread (Windows and Linux).
// Disconnect from a 3DConnextion driver (OSX).
m_plater->get_mouse3d_controller().shutdown();
// Store the device parameter database back to appconfig.
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
if (m_plater != nullptr) {
#if ENABLE_GCODE_VIEWER
// restore sidebar if it was hidden when switching to gcode viewer mode
if (m_restore_from_gcode_viewer.collapsed_sidebar)
m_plater->collapse_sidebar(false);
// restore sla printer if it was deselected when switching to gcode viewer mode
if (m_restore_from_gcode_viewer.sla_technology)
m_plater->set_printer_technology(ptSLA);
#endif // ENABLE_GCODE_VIEWER
// Stop the background thread (Windows and Linux).
// Disconnect from a 3DConnextion driver (OSX).
m_plater->get_mouse3d_controller().shutdown();
// Store the device parameter database back to appconfig.
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
}
// Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater.
wxGetApp().removable_drive_manager()->shutdown();
@ -713,7 +812,81 @@ void MainFrame::on_sys_color_changed()
msw_rescale_menu(menu_bar->GetMenu(id));
}
#if ENABLE_GCODE_VIEWER
#ifdef _MSC_VER
// \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators,
// as the simple numeric accelerators spoil all numeric data entry.
static const wxString sep = "\t\xA0";
static const wxString sep_space = "\xA0";
#else
static const wxString sep = " - ";
static const wxString sep_space = "";
#endif
static wxMenu* generate_help_menu()
{
wxMenu* helpMenu = new wxMenu();
append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); });
append_menu_item(helpMenu, wxID_ANY, _(L("Software &Releases")), _(L("Open the software releases page in your browser")),
[](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); });
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
//# wxTheApp->check_version(1);
//# });
//# $versioncheck->Enable(wxTheApp->have_version_check);
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Website"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); });
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("%s &Manual")), SLIC3R_APP_NAME),
// wxString::Format(_(L("Open the %s manual in your browser")), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _L("System &Info"), _L("Show system information"),
[](wxCommandEvent&) { wxGetApp().system_info(); });
append_menu_item(helpMenu, wxID_ANY, _L("Show &Configuration Folder"), _L("Show user configuration folder (datadir)"),
[](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
append_menu_item(helpMenu, wxID_ANY, _(L"Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); });
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"),
[](wxCommandEvent&) { Slic3r::GUI::about(); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"),
[](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); });
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, "DEBUG gcode thumbnails", "DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails",
[](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); });
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
return helpMenu;
}
static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, std::function<bool(void)> can_change_view)
{
// The camera control accelerators are captured by GLCanvas3D::on_char().
append_menu_item(view_menu, wxID_ANY, _L("Iso") + sep + "&0", _L("Iso View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("iso"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
view_menu->AppendSeparator();
//TRN To be shown in the main menu View->Top
append_menu_item(view_menu, wxID_ANY, _L("Top") + sep + "&1", _L("Top View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("top"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
//TRN To be shown in the main menu View->Bottom
append_menu_item(view_menu, wxID_ANY, _L("Bottom") + sep + "&2", _L("Bottom View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("bottom"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Front") + sep + "&3", _L("Front View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("front"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Rear") + sep + "&4", _L("Rear View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("rear"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Left") + sep + "&5", _L("Left View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("left"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
append_menu_item(view_menu, wxID_ANY, _L("Right") + sep + "&6", _L("Right View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("right"); },
"", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame);
}
void MainFrame::init_editor_menubar()
#else
void MainFrame::init_menubar()
#endif // ENABLE_GCODE_VIEWER
{
#ifdef __APPLE__
wxMenuBar::SetAutoWindowMenu(false);
@ -722,15 +895,15 @@ void MainFrame::init_menubar()
// File menu
wxMenu* fileMenu = new wxMenu;
{
append_menu_item(fileMenu, wxID_ANY, _(L("&New Project")) + "\tCtrl+N", _(L("Start a new project")),
append_menu_item(fileMenu, wxID_ANY, _L("&New Project") + "\tCtrl+N", _L("Start a new project"),
[this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr,
[this](){return m_plater != nullptr && can_start_new_project(); }, this);
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
append_menu_item(fileMenu, wxID_ANY, _L("&Open Project") + dots + "\tCtrl+O", _L("Open a project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "open", nullptr,
[this](){return m_plater != nullptr; }, this);
wxMenu* recent_projects_menu = new wxMenu();
wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), "");
wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _L("Recent projects"), "");
m_recent_projects.UseMenu(recent_projects_menu);
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
size_t file_id = evt.GetId() - wxID_FILE1;
@ -739,7 +912,7 @@ void MainFrame::init_menubar()
m_plater->load_project(filename);
else
{
wxMessageDialog msg(this, _(L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?")), _(L("Error")), wxYES_NO | wxYES_DEFAULT);
wxMessageDialog msg(this, _L("The selected project is no longer available.\nDo you want to remove it from the recent projects list?"), _L("Error"), wxYES_NO | wxYES_DEFAULT);
if (msg.ShowModal() == wxID_YES)
{
m_recent_projects.RemoveFileFromHistory(file_id);
@ -764,13 +937,13 @@ void MainFrame::init_menubar()
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#ifdef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Shift+S", _(L("Save current project file as")),
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
#else
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
#endif // __APPLE__
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
@ -778,7 +951,7 @@ void MainFrame::init_menubar()
fileMenu->AppendSeparator();
wxMenu* import_menu = new wxMenu();
append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")),
append_menu_item(import_menu, wxID_ANY, _L("Import STL/OBJ/AM&F/3MF") + dots + "\tCtrl+I", _L("Load a model"),
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
@ -786,59 +959,59 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(true); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
append_menu_item(import_menu, wxID_ANY, _(L("Import SL1 archive")) + dots, _(L("Load an SL1 output archive")),
append_menu_item(import_menu, wxID_ANY, _L("Import SL1 archive") + dots, _L("Load an SL1 output archive"),
[this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")),
append_menu_item(import_menu, wxID_ANY, _L("Import &Config") + dots + "\tCtrl+L", _L("Load exported configuration file"),
[this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr,
[this]() {return true; }, this);
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")),
append_menu_item(import_menu, wxID_ANY, _L("Import Config from &project") + dots +"\tCtrl+Alt+L", _L("Load configuration from project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config", nullptr,
[this]() {return true; }, this);
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")),
append_menu_item(import_menu, wxID_ANY, _L("Import Config &Bundle") + dots, _L("Load presets from a bundle"),
[this](wxCommandEvent&) { load_configbundle(); }, "import_config_bundle", nullptr,
[this]() {return true; }, this);
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), "");
append_submenu(fileMenu, import_menu, wxID_ANY, _L("&Import"), "");
wxMenu* export_menu = new wxMenu();
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")),
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _L("Export &G-code") + dots +"\tCtrl+G", _L("Export current plate as G-code"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(false); }, "export_gcode", nullptr,
[this](){return can_export_gcode(); }, this);
m_changeable_menu_items.push_back(item_export_gcode);
wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")),
wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _L("S&end G-code") + dots +"\tCtrl+Shift+G", _L("Send to print current plate as G-code"),
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
[this](){return can_send_gcode(); }, this);
m_changeable_menu_items.push_back(item_send_gcode);
append_menu_item(export_menu, wxID_ANY, _(L("Export G-code to SD card / Flash drive")) + dots + "\tCtrl+U", _(L("Export current plate as G-code to SD card / Flash drive")),
append_menu_item(export_menu, wxID_ANY, _L("Export G-code to SD card / Flash drive") + dots + "\tCtrl+U", _L("Export current plate as G-code to SD card / Flash drive"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(true); }, "export_to_sd", nullptr,
[this]() {return can_export_gcode_sd(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
append_menu_item(export_menu, wxID_ANY, _L("Export plate as &STL") + dots, _L("Export current plate as STL"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
[this](){return can_export_model(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL &including supports")) + dots, _(L("Export current plate as STL including supports")),
append_menu_item(export_menu, wxID_ANY, _L("Export plate as STL &including supports") + dots, _L("Export current plate as STL including supports"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr,
[this](){return can_export_supports(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
append_menu_item(export_menu, wxID_ANY, _L("Export plate as &AMF") + dots, _L("Export current plate as AMF"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr,
[this](){return can_export_model(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &toolpaths as OBJ")) + dots, _(L("Export toolpaths as OBJ")),
append_menu_item(export_menu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
append_menu_item(export_menu, wxID_ANY, _L("Export &Config") + dots +"\tCtrl+E", _L("Export current configuration to file"),
[this](wxCommandEvent&) { export_config(); }, "export_config", nullptr,
[this]() {return true; }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
append_menu_item(export_menu, wxID_ANY, _L("Export Config &Bundle") + dots, _L("Export all presets to file"),
[this](wxCommandEvent&) { export_configbundle(); }, "export_config_bundle", nullptr,
[this]() {return true; }, this);
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), "");
append_menu_item(fileMenu, wxID_ANY, _(L("Ejec&t SD card / Flash drive")) + dots + "\tCtrl+T", _(L("Eject SD card / Flash drive after the G-code was exported to it.")),
append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD card / Flash drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."),
[this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr,
[this]() {return can_eject(); }, this);
@ -846,19 +1019,19 @@ void MainFrame::init_menubar()
#if 0
m_menu_item_repeat = nullptr;
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice")) +dots+ "\tCtrl+U", _(L("Slice a file into a G-code")),
append_menu_item(fileMenu, wxID_ANY, _L("Quick Slice") +dots+ "\tCtrl+U", _L("Slice a file into a G-code"),
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
quick_slice();
m_menu_item_repeat->Enable(is_last_input_file());
}); }, "cog_go.png");
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice and Save As")) +dots +"\tCtrl+Alt+U", _(L("Slice a file into a G-code, save as")),
append_menu_item(fileMenu, wxID_ANY, _L("Quick Slice and Save As") +dots +"\tCtrl+Alt+U", _L("Slice a file into a G-code, save as"),
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
quick_slice(qsSaveAs);
m_menu_item_repeat->Enable(is_last_input_file());
}); }, "cog_go.png");
m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _(L("Repeat Last Quick Slice")) +"\tCtrl+Shift+U", _(L("Repeat last quick slice")),
m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _L("Repeat Last Quick Slice") +"\tCtrl+Shift+U", _L("Repeat last quick slice"),
[this](wxCommandEvent&) {
wxTheApp->CallAfter([this]() {
quick_slice(qsReslice);
@ -866,18 +1039,29 @@ void MainFrame::init_menubar()
m_menu_item_repeat->Enable(false);
fileMenu->AppendSeparator();
#endif
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice No&w")) + "\tCtrl+R", _(L("Start new slicing process")),
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _L("(Re)Slice No&w") + "\tCtrl+R", _L("Start new slicing process"),
[this](wxCommandEvent&) { reslice_now(); }, "re_slice", nullptr,
[this](){return m_plater != nullptr && can_reslice(); }, this);
[this]() { return m_plater != nullptr && can_reslice(); }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"),
[this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr,
[this]() {return true; }, this);
[this]() { return true; }, this);
#if ENABLE_GCODE_VIEWER
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), wxString::Format(_(L("Quit %s")), SLIC3R_APP_NAME),
append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"),
[this](wxCommandEvent&) {
if (m_plater->model().objects.empty() ||
wxMessageDialog((wxWindow*)this, _L("Switching to G-code preview mode will remove all objects, continue?"),
wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTRE).ShowModal() == wxID_YES)
set_mode(EMode::GCodeViewer);
}, "", nullptr);
#endif // ENABLE_GCODE_VIEWER
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { Close(false); });
}
#if !ENABLE_GCODE_VIEWER
#ifdef _MSC_VER
// \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators,
// as the simple numeric accelerators spoil all numeric data entry.
@ -887,6 +1071,7 @@ void MainFrame::init_menubar()
wxString sep = " - ";
wxString sep_space = "";
#endif
#endif // !ENABLE_GCODE_VIEWER
// Edit menu
wxMenu* editMenu = nullptr;
@ -899,44 +1084,44 @@ void MainFrame::init_menubar()
#else
wxString hotkey_delete = "Del";
#endif
append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A",
_(L("Selects all objects")), [this](wxCommandEvent&) { m_plater->select_all(); },
append_menu_item(editMenu, wxID_ANY, _L("&Select all") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A",
_L("Selects all objects"), [this](wxCommandEvent&) { m_plater->select_all(); },
"", nullptr, [this](){return can_select(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("D&eselect all")) + sep + "Esc",
_(L("Deselects all objects")), [this](wxCommandEvent&) { m_plater->deselect_all(); },
append_menu_item(editMenu, wxID_ANY, _L("D&eselect all") + sep + "Esc",
_L("Deselects all objects"), [this](wxCommandEvent&) { m_plater->deselect_all(); },
"", nullptr, [this](){return can_deselect(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete,
_(L("Deletes the current selection")),[this](wxCommandEvent&) { m_plater->remove_selected(); },
append_menu_item(editMenu, wxID_ANY, _L("&Delete selected") + sep + hotkey_delete,
_L("Deletes the current selection"),[this](wxCommandEvent&) { m_plater->remove_selected(); },
"remove_menu", nullptr, [this](){return can_delete(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete,
_(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
append_menu_item(editMenu, wxID_ANY, _L("Delete &all") + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete,
_L("Deletes all objects"), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
"delete_all_menu", nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
_(L("Undo")), [this](wxCommandEvent&) { m_plater->undo(); },
append_menu_item(editMenu, wxID_ANY, _L("&Undo") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
_L("Undo"), [this](wxCommandEvent&) { m_plater->undo(); },
"undo_menu", nullptr, [this](){return m_plater->can_undo(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y",
_(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); },
append_menu_item(editMenu, wxID_ANY, _L("&Redo") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y",
_L("Redo"), [this](wxCommandEvent&) { m_plater->redo(); },
"redo_menu", nullptr, [this](){return m_plater->can_redo(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
append_menu_item(editMenu, wxID_ANY, _L("&Copy") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_L("Copy selection to clipboard"), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
"copy_menu", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
append_menu_item(editMenu, wxID_ANY, _L("&Paste") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_L("Paste clipboard"), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
"paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("Re&load from disk")) + sep + "F5",
_(L("Reload the plater from disk")), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
append_menu_item(editMenu, wxID_ANY, _L("Re&load from disk") + sep + "F5",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
"", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\tCtrl+F",
_(L("Find option")), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); },
append_menu_item(editMenu, wxID_ANY, _L("Searc&h") + "\tCtrl+F",
_L("Find option"), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); },
"search", nullptr, [this]() {return true; }, this);
}
@ -944,32 +1129,33 @@ void MainFrame::init_menubar()
auto windowMenu = new wxMenu();
{
if (m_plater) {
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
append_menu_item(windowMenu, wxID_HIGHEST + 1, _L("&Plater Tab") + "\tCtrl+1", _L("Show the plater"),
[this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr,
[this]() {return true; }, this);
windowMenu->AppendSeparator();
}
append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
append_menu_item(windowMenu, wxID_HIGHEST + 2, _L("P&rint Settings Tab") + "\tCtrl+2", _L("Show the print settings"),
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(1); }, "cog", nullptr,
[this]() {return true; }, this);
wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _L("&Filament Settings Tab") + "\tCtrl+3", _L("Show the filament settings"),
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(2); }, "spool", nullptr,
[this]() {return true; }, this);
m_changeable_menu_items.push_back(item_material_tab);
wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _L("Print&er Settings Tab") + "\tCtrl+4", _L("Show the printer settings"),
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(3); }, "printer", nullptr,
[this]() {return true; }, this);
m_changeable_menu_items.push_back(item_printer_tab);
if (m_plater) {
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
append_menu_item(windowMenu, wxID_HIGHEST + 5, _L("3&D") + "\tCtrl+5", _L("Show the 3D editing view"),
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "editor_menu", nullptr,
[this](){return can_change_view(); }, this);
append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")),
append_menu_item(windowMenu, wxID_HIGHEST + 6, _L("Pre&view") + "\tCtrl+6", _L("Show the 3D slices preview"),
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "preview_menu", nullptr,
[this](){return can_change_view(); }, this);
}
#if !ENABLE_GCODE_VIEWER
#if _WIN32
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
wxAcceleratorEntry entries[6];
@ -982,9 +1168,10 @@ void MainFrame::init_menubar()
wxAcceleratorTable accel(6, entries);
SetAcceleratorTable(accel);
#endif // _WIN32
#endif // !ENABLE_GCODE_VIEWER
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")),
append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"),
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr,
[this]() {return true; }, this);
@ -1005,72 +1192,79 @@ void MainFrame::init_menubar()
wxMenu* viewMenu = nullptr;
if (m_plater) {
viewMenu = new wxMenu();
#if ENABLE_GCODE_VIEWER
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
#else
// The camera control accelerators are captured by GLCanvas3D::on_char().
append_menu_item(viewMenu, wxID_ANY, _(L("Iso")) + sep + "&0", _(L("Iso View")),[this](wxCommandEvent&) { select_view("iso"); },
append_menu_item(viewMenu, wxID_ANY, _L("Iso") + sep + "&0", _L("Iso View"), [this](wxCommandEvent&) { select_view("iso"); },
"", nullptr, [this](){return can_change_view(); }, this);
viewMenu->AppendSeparator();
//TRN To be shown in the main menu View->Top
append_menu_item(viewMenu, wxID_ANY, _(L("Top")) + sep + "&1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); },
append_menu_item(viewMenu, wxID_ANY, _L("Top") + sep + "&1", _L("Top View"), [this](wxCommandEvent&) { select_view("top"); },
"", nullptr, [this](){return can_change_view(); }, this);
//TRN To be shown in the main menu View->Bottom
append_menu_item(viewMenu, wxID_ANY, _(L("Bottom")) + sep + "&2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); },
append_menu_item(viewMenu, wxID_ANY, _L("Bottom") + sep + "&2", _L("Bottom View"), [this](wxCommandEvent&) { select_view("bottom"); },
"", nullptr, [this](){return can_change_view(); }, this);
append_menu_item(viewMenu, wxID_ANY, _(L("Front")) + sep + "&3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); },
append_menu_item(viewMenu, wxID_ANY, _L("Front") + sep + "&3", _L("Front View"), [this](wxCommandEvent&) { select_view("front"); },
"", nullptr, [this](){return can_change_view(); }, this);
append_menu_item(viewMenu, wxID_ANY, _(L("Rear")) + sep + "&4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); },
append_menu_item(viewMenu, wxID_ANY, _L("Rear") + sep + "&4", _L("Rear View"), [this](wxCommandEvent&) { select_view("rear"); },
"", nullptr, [this](){return can_change_view(); }, this);
append_menu_item(viewMenu, wxID_ANY, _(L("Left")) + sep + "&5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); },
append_menu_item(viewMenu, wxID_ANY, _L("Left") + sep + "&5", _L("Left View"), [this](wxCommandEvent&) { select_view("left"); },
"", nullptr, [this](){return can_change_view(); }, this);
append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + sep + "&6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); },
append_menu_item(viewMenu, wxID_ANY, _L("Right") + sep + "&6", _L("Right View"), [this](wxCommandEvent&) { select_view("right"); },
"", nullptr, [this](){return can_change_view(); }, this);
#endif // ENABLE_GCODE_VIEWER
viewMenu->AppendSeparator();
#if ENABLE_SLOPE_RENDERING
wxMenu* options_menu = new wxMenu();
append_menu_check_item(options_menu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")),
append_menu_check_item(options_menu, wxID_ANY, _L("Show &labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
append_menu_check_item(options_menu, wxID_ANY, _(L("Show &slope")) + sep + "D", _(L("Objects coloring using faces' slope")),
append_menu_check_item(options_menu, wxID_ANY, _L("Show &slope") + sep + "D", _L("Objects coloring using faces' slope"),
[this](wxCommandEvent&) { m_plater->show_view3D_slope(!m_plater->is_view3D_slope_shown()); }, this,
[this]() { return m_plater->is_view3D_shown() && !m_plater->is_view3D_layers_editing_enabled(); }, [this]() { return m_plater->is_view3D_slope_shown(); }, this);
append_submenu(viewMenu, options_menu, wxID_ANY, _(L("&Options")), "");
append_submenu(viewMenu, options_menu, wxID_ANY, _L("&Options"), "");
#else
append_menu_check_item(viewMenu, wxID_ANY, _(L("Show &labels")) + sep + "E", _(L("Show object/instance labels in 3D scene")),
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
#endif // ENABLE_SLOPE_RENDERING
append_menu_check_item(viewMenu, wxID_ANY, _(L("&Collapse sidebar")), _(L("Collapse sidebar")),
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar"), _L("Collapse sidebar"),
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
[this]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
}
// Help menu
#if ENABLE_GCODE_VIEWER
auto helpMenu = generate_help_menu();
#else
auto helpMenu = new wxMenu();
{
append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D &Drivers")), _(L("Open the Prusa3D drivers download page in your browser")),
append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"),
[this](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); });
append_menu_item(helpMenu, wxID_ANY, _(L("Software &Releases")), _(L("Open the software releases page in your browser")),
append_menu_item(helpMenu, wxID_ANY, _L("Software &Releases"), _L("Open the software releases page in your browser"),
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); });
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
//# wxTheApp->check_version(1);
//# });
//# $versioncheck->Enable(wxTheApp->have_version_check);
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("%s &Website")), SLIC3R_APP_NAME),
wxString::Format(_(L("Open the %s website in your browser")), SLIC3R_APP_NAME),
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Website"), SLIC3R_APP_NAME),
wxString::Format(_L("Open the %s website in your browser"), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); });
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("%s &Manual")), SLIC3R_APP_NAME),
// wxString::Format(_(L("Open the %s manual in your browser")), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _(L("System &Info")), _(L("Show system information")),
append_menu_item(helpMenu, wxID_ANY, _L("System &Info"), _L("Show system information"),
[this](wxCommandEvent&) { wxGetApp().system_info(); });
append_menu_item(helpMenu, wxID_ANY, _(L("Show &Configuration Folder")), _(L("Show user configuration folder (datadir)")),
append_menu_item(helpMenu, wxID_ANY, _L("Show &Configuration Folder"), _L("Show user configuration folder (datadir)"),
[this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
append_menu_item(helpMenu, wxID_ANY, _(L("Report an I&ssue")), wxString::Format(_(L("Report an issue on %s")), SLIC3R_APP_NAME),
append_menu_item(helpMenu, wxID_ANY, _L("Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); });
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("&About %s")), SLIC3R_APP_NAME), _(L("Show about dialog")),
[this](wxCommandEvent&) { Slic3r::GUI::about(); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + sep + "&?", _(L("Show the list of the keyboard shortcuts")),
append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"),
[this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); });
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
helpMenu->AppendSeparator();
@ -1078,10 +1272,22 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); });
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
}
#endif // ENABLE_GCODE_VIEWER
// menubar
// assign menubar to frame after appending items, otherwise special items
// will not be handled correctly
#if ENABLE_GCODE_VIEWER
m_editor_menubar = new wxMenuBar();
m_editor_menubar->Append(fileMenu, _L("&File"));
if (editMenu) m_editor_menubar->Append(editMenu, _L("&Edit"));
m_editor_menubar->Append(windowMenu, _L("&Window"));
if (viewMenu) m_editor_menubar->Append(viewMenu, _L("&View"));
// Add additional menus from C++
wxGetApp().add_config_menu(m_editor_menubar);
m_editor_menubar->Append(helpMenu, _L("&Help"));
SetMenuBar(m_editor_menubar);
#else
auto menubar = new wxMenuBar();
menubar->Append(fileMenu, _(L("&File")));
if (editMenu) menubar->Append(editMenu, _(L("&Edit")));
@ -1091,11 +1297,16 @@ void MainFrame::init_menubar()
wxGetApp().add_config_menu(menubar);
menubar->Append(helpMenu, _(L("&Help")));
SetMenuBar(menubar);
#endif // ENABLE_GCODE_VIEWER
#ifdef __APPLE__
// This fixes a bug on Mac OS where the quit command doesn't emit window close events
// wx bug: https://trac.wxwidgets.org/ticket/18328
#if ENABLE_GCODE_VIEWER
wxMenu* apple_menu = m_editor_menubar->OSXGetAppleMenu();
#else
wxMenu *apple_menu = menubar->OSXGetAppleMenu();
#endif // ENABLE_GCODE_VIEWER
if (apple_menu != nullptr) {
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) {
Close();
@ -1104,10 +1315,179 @@ void MainFrame::init_menubar()
#endif
if (plater()->printer_technology() == ptSLA)
#if ENABLE_GCODE_VIEWER
update_editor_menubar();
#else
update_menubar();
#endif // ENABLE_GCODE_VIEWER
}
#if ENABLE_GCODE_VIEWER
void MainFrame::init_gcodeviewer_menubar()
{
wxMenu* fileMenu = new wxMenu;
{
append_menu_item(fileMenu, wxID_ANY, _L("&Open G-code") + dots + "\tCtrl+O", _L("Open a G-code file"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->load_gcode(); }, "open", nullptr,
[this]() {return m_plater != nullptr; }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _L("Exit &G-code preview"), _L("Switch to editor mode"),
[this](wxCommandEvent&) { set_mode(EMode::Editor); });
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { Close(false); });
}
// View menu
wxMenu* viewMenu = nullptr;
if (m_plater != nullptr) {
viewMenu = new wxMenu();
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
}
// helpmenu
auto helpMenu = generate_help_menu();
m_gcodeviewer_menubar = new wxMenuBar();
m_gcodeviewer_menubar->Append(fileMenu, _L("&File"));
if ((viewMenu != nullptr))
m_gcodeviewer_menubar->Append(viewMenu, _L("&View"));
m_gcodeviewer_menubar->Append(helpMenu, _L("&Help"));
}
void MainFrame::set_mode(EMode mode)
{
if (m_mode == mode)
return;
wxBusyCursor busy;
m_mode = mode;
switch (m_mode)
{
default:
case EMode::Editor:
{
update_layout();
select_tab(0);
m_plater->reset();
m_plater->reset_gcode_toolpaths();
m_plater->Freeze();
// reinitialize undo/redo stack
m_plater->clear_undo_redo_stack_main();
m_plater->take_snapshot(_L("New Project"));
// restore sla printer if it was deselected when switching to gcode viewer mode
if (m_restore_from_gcode_viewer.sla_technology) {
m_plater->set_printer_technology(ptSLA);
m_restore_from_gcode_viewer.sla_technology = false;
}
// switch view
m_plater->select_view_3D("3D");
m_plater->select_view("iso");
// switch printbed
m_plater->set_bed_shape();
// switch menubar
SetMenuBar(m_editor_menubar);
// show toolbars
m_plater->enable_view_toolbar(true);
if (m_restore_from_gcode_viewer.collapse_toolbar_enabled) {
m_plater->get_collapse_toolbar().set_enabled(true);
m_restore_from_gcode_viewer.collapse_toolbar_enabled = false;
}
// show sidebar
if (m_restore_from_gcode_viewer.collapsed_sidebar) {
m_plater->collapse_sidebar(false);
m_restore_from_gcode_viewer.collapsed_sidebar = false;
}
m_plater->Thaw();
SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
if (m_taskbar_icon != nullptr) {
m_taskbar_icon->RemoveIcon();
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
}
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
break;
}
case EMode::GCodeViewer:
{
update_layout();
m_plater->reset();
m_plater->reset_last_loaded_gcode();
m_plater->reset_gcode_toolpaths();
m_plater->Freeze();
// reinitialize undo/redo stack
m_plater->clear_undo_redo_stack_main();
m_plater->take_snapshot(_L("New Project"));
// switch to FFF printer mode
m_restore_from_gcode_viewer.sla_technology = m_plater->set_printer_technology(ptFFF);
// switch view
m_plater->select_view_3D("Preview");
m_plater->select_view("iso");
// switch printbed
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, "", "", true);
// switch menubar
SetMenuBar(m_gcodeviewer_menubar);
// hide toolbars
m_plater->enable_view_toolbar(false);
if (wxGetApp().app_config->get("show_collapse_button") == "1") {
m_plater->get_collapse_toolbar().set_enabled(false);
m_restore_from_gcode_viewer.collapse_toolbar_enabled = true;
}
// hide sidebar
if (wxGetApp().app_config->get("collapsed_sidebar") != "1") {
m_plater->collapse_sidebar(true);
m_restore_from_gcode_viewer.collapsed_sidebar = true;
}
m_plater->Thaw();
SetIcon(wxIcon(Slic3r::var("PrusaSlicerGCodeViewer_128px.png"), wxBITMAP_TYPE_PNG));
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
if (m_taskbar_icon != nullptr) {
m_taskbar_icon->RemoveIcon();
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer-GCode viewer");
}
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
break;
}
}
}
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void MainFrame::update_editor_menubar()
#else
void MainFrame::update_menubar()
#endif // ENABLE_GCODE_VIEWER
{
const bool is_fff = plater()->printer_technology() == ptFFF;
@ -1600,16 +1980,19 @@ SettingsDialog::SettingsDialog(MainFrame* mainframe)
this->SetFont(wxGetApp().normal_font());
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
TCHAR szExeFileName[MAX_PATH];
GetModuleFileName(nullptr, szExeFileName, MAX_PATH);
SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO));
}
#else
SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
#endif // _WIN32
SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
// // Load the icon either from the exe, or from the ico file.
//#if _WIN32
// {
//
// TCHAR szExeFileName[MAX_PATH];
// GetModuleFileName(nullptr, szExeFileName, MAX_PATH);
// SetIcon(wxIcon(szExeFileName, wxBITMAP_TYPE_ICO));
// }
//#else
// SetIcon(wxIcon(var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG));
//#endif // _WIN32
this->Bind(wxEVT_SHOW, [this](wxShowEvent& evt) {

View file

@ -7,6 +7,9 @@
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/filehistory.h>
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
#include <wx/taskbar.h>
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
#include <string>
#include <map>
@ -68,6 +71,20 @@ class MainFrame : public DPIFrame
wxString m_qs_last_input_file = wxEmptyString;
wxString m_qs_last_output_file = wxEmptyString;
wxString m_last_config = wxEmptyString;
#if ENABLE_GCODE_VIEWER
wxMenuBar* m_editor_menubar{ nullptr };
wxMenuBar* m_gcodeviewer_menubar{ nullptr };
struct RestoreFromGCodeViewer
{
bool collapsed_sidebar{ false };
bool collapse_toolbar_enabled{ false };
bool sla_technology{ false };
};
RestoreFromGCodeViewer m_restore_from_gcode_viewer;
#endif // ENABLE_GCODE_VIEWER
#if 0
wxMenuItem* m_menu_item_repeat { nullptr }; // doesn't used now
#endif
@ -121,17 +138,36 @@ class MainFrame : public DPIFrame
Old,
New,
Dlg,
#if ENABLE_GCODE_VIEWER
GCodeViewer
#endif // ENABLE_GCODE_VIEWER
};
ESettingsLayout m_layout{ ESettingsLayout::Unknown };
#if ENABLE_GCODE_VIEWER
public:
enum class EMode : unsigned char
{
Editor,
GCodeViewer
};
private:
EMode m_mode{ EMode::Editor };
#endif // ENABLE_GCODE_VIEWER
protected:
virtual void on_dpi_changed(const wxRect &suggested_rect);
virtual void on_sys_color_changed() override;
public:
MainFrame();
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
~MainFrame();
#else
~MainFrame() = default;
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
void update_layout();
@ -145,8 +181,17 @@ public:
void init_tabpanel();
void create_preset_tabs();
void add_created_tab(Tab* panel);
#if ENABLE_GCODE_VIEWER
void init_editor_menubar();
void update_editor_menubar();
void init_gcodeviewer_menubar();
EMode get_mode() const { return m_mode; }
void set_mode(EMode mode);
#else
void init_menubar();
void update_menubar();
#endif // ENABLE_GCODE_VIEWER
void update_ui_from_settings();
bool is_loaded() const { return m_loaded; }
@ -181,6 +226,10 @@ public:
wxProgressDialog* m_progress_dialog { nullptr };
std::shared_ptr<ProgressStatusBar> m_statusbar;
#if ENABLE_GCODE_VIEWER_TASKBAR_ICON
wxTaskBarIcon* m_taskbar_icon{ nullptr };
#endif // ENABLE_GCODE_VIEWER_TASKBAR_ICON
#ifdef _WIN32
void* m_hDeviceNotify { nullptr };
uint32_t m_ulSHChangeNotifyRegister { 0 };

View file

@ -28,6 +28,13 @@
namespace Slic3r {
namespace GUI {
// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr.
inline std::string gl_get_string_safe(GLenum param, const std::string& default_value)
{
const char* value = (const char*)::glGetString(param);
return std::string((value != nullptr) ? value : default_value);
}
const std::string& OpenGLManager::GLInfo::get_version() const
{
if (!m_detected)
@ -85,21 +92,10 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const
void OpenGLManager::GLInfo::detect() const
{
const char* data = (const char*)::glGetString(GL_VERSION);
if (data != nullptr)
m_version = data;
data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
if (data != nullptr)
m_glsl_version = data;
data = (const char*)::glGetString(GL_VENDOR);
if (data != nullptr)
m_vendor = data;
data = (const char*)::glGetString(GL_RENDERER);
if (data != nullptr)
m_renderer = data;
m_version = gl_get_string_safe(GL_VERSION, "N/A");
m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A");
m_vendor = gl_get_string_safe(GL_VENDOR, "N/A");
m_renderer = gl_get_string_safe(GL_RENDERER, "N/A");
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
@ -114,13 +110,13 @@ void OpenGLManager::GLInfo::detect() const
m_detected = true;
}
bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor)
{
if (!m_detected)
detect();
if (version == "N/A")
return false;
std::vector<std::string> tokens;
boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on);
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty())
return false;
@ -145,6 +141,22 @@ bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, u
return gl_minor >= minor;
}
bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{
if (!m_detected)
detect();
return version_greater_or_equal_to(m_version, major, minor);
}
bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{
if (!m_detected)
detect();
return version_greater_or_equal_to(m_glsl_version, major, minor);
}
std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const
{
if (!m_detected)
@ -159,15 +171,15 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension
std::string line_end = format_as_html ? "<br>" : "\n";
out << h2_start << "OpenGL installation" << h2_end << line_end;
out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end;
out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end;
out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end;
out << b_start << "GL version: " << b_end << m_version << line_end;
out << b_start << "Vendor: " << b_end << m_vendor << line_end;
out << b_start << "Renderer: " << b_end << m_renderer << line_end;
out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end;
if (extensions)
{
std::vector<std::string> extensions_list;
std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS);
std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, "");
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off);
if (!extensions_list.empty())
@ -199,6 +211,8 @@ OpenGLManager::OSInfo OpenGLManager::s_os_info;
OpenGLManager::~OpenGLManager()
{
m_shaders_manager.shutdown();
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
#ifdef __APPLE__
// This is an ugly hack needed to solve the crash happening when closing the application on OSX 10.9.5 with newer wxWidgets
@ -240,19 +254,30 @@ bool OpenGLManager::init_gl()
else
s_framebuffers_type = EFramebufferType::Unknown;
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
// Complain about the OpenGL version.
bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0);
if (!valid_version) {
// Complain about the OpenGL version.
wxString message = from_u8((boost::format(
_utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
"while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str());
message += "\n";
message += "\n";
message += _L("You may need to update your graphics card driver.");
#ifdef _WIN32
message += "\n";
message += "\n";
message += _L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter.");
#endif
wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Unsupported OpenGL version"), wxOK | wxICON_ERROR);
}
if (valid_version) {
// load shaders
auto [result, error] = m_shaders_manager.init();
if (!result) {
wxString message = from_u8((boost::format(
_utf8(L("Unable to load the following shaders:\n%s"))) % error).str());
wxMessageBox(message, wxString("PrusaSlicer - ") + _L("Error loading shaders"), wxOK | wxICON_ERROR);
}
}
}
return true;
@ -260,8 +285,7 @@ bool OpenGLManager::init_gl()
wxGLContext* OpenGLManager::init_glcontext(wxGLCanvas& canvas)
{
if (m_context == nullptr)
{
if (m_context == nullptr) {
m_context = new wxGLContext(&canvas);
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5

View file

@ -1,6 +1,8 @@
#ifndef slic3r_OpenGLManager_hpp_
#define slic3r_OpenGLManager_hpp_
#include "GLShadersManager.hpp"
class wxWindow;
class wxGLCanvas;
class wxGLContext;
@ -41,6 +43,7 @@ public:
float get_max_anisotropy() const;
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
std::string to_string(bool format_as_html, bool extensions) const;
@ -70,6 +73,7 @@ private:
bool m_gl_initialized{ false };
wxGLContext* m_context{ nullptr };
GLShadersManager m_shaders_manager;
static GLInfo s_gl_info;
#if ENABLE_HACK_CLOSING_ON_OSX_10_9_5
#ifdef __APPLE__
@ -86,9 +90,11 @@ public:
~OpenGLManager();
bool init_gl();
wxGLContext* init_glcontext(wxGLCanvas& canvas);
GLShaderProgram* get_shader(const std::string& shader_name) { return m_shaders_manager.get_shader(shader_name); }
GLShaderProgram* get_current_shader() { return m_shaders_manager.get_current_shader(); }
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; }
static bool are_framebuffers_supported() { return (s_framebuffers_type != EFramebufferType::Unknown); }

View file

@ -32,7 +32,11 @@
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
#if ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/GCodeProcessor.hpp"
#else
#include "libslic3r/GCode/PreviewData.hpp"
#endif // ENABLE_GCODE_VIEWER
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/SLA/Hollowing.hpp"
@ -1161,35 +1165,39 @@ void Sidebar::update_sliced_info_sizer()
wxString::Format("%.2f", ps.total_cost);
p->sliced_info->SetTextAndShow(siCost, info_text, new_label);
#if ENABLE_GCODE_VIEWER
// hide the estimate time
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
#else
if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
else {
new_label = _L("Estimated printing time") +":";
new_label = _L("Estimated printing time") + ":";
info_text = "";
wxString str_color = _L("Color");
wxString str_pause = _L("Pause");
auto fill_labels = [str_color, str_pause](const std::vector<std::pair<CustomGCode::Type, std::string>>& times,
wxString& new_label, wxString& info_text)
{
int color_change_count = 0;
for (auto time : times)
if (time.first == CustomGCode::ColorChange)
color_change_count++;
for (int i = (int)times.size() - 1; i >= 0; --i)
auto fill_labels = [str_color, str_pause](const std::vector<std::pair<CustomGCode::Type, std::string>>& times,
wxString& new_label, wxString& info_text)
{
if (i == 0 || times[i - 1].first == CustomGCode::PausePrint)
new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count);
else if (times[i - 1].first == CustomGCode::ColorChange)
new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count--);
int color_change_count = 0;
for (auto time : times)
if (time.first == CustomGCode::ColorChange)
color_change_count++;
if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint)
new_label += format_wxstr(" -> %1%", str_pause);
for (int i = (int)times.size() - 1; i >= 0; --i)
{
if (i == 0 || times[i - 1].first == CustomGCode::PausePrint)
new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count);
else if (times[i - 1].first == CustomGCode::ColorChange)
new_label += format_wxstr("\n - %1%%2%", str_color + " ", color_change_count--);
info_text += format_wxstr("\n%1%", times[i].second);
}
};
if (i != (int)times.size() - 1 && times[i].first == CustomGCode::PausePrint)
new_label += format_wxstr(" -> %1%", str_pause);
info_text += format_wxstr("\n%1%", times[i].second);
}
};
if (ps.estimated_normal_print_time != "N/A") {
new_label += format_wxstr("\n - %1%", _L("normal mode"));
@ -1207,8 +1215,9 @@ void Sidebar::update_sliced_info_sizer()
info_text += format_wxstr("\n%1%", ps.estimated_silent_print_time);
fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text);
}
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
}
#endif // !ENABLE_GCODE_VIEWER
// if there is a wipe tower, insert number of toolchanges info into the array:
p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", ps.total_toolchanges) : "N/A");
@ -1217,6 +1226,8 @@ void Sidebar::update_sliced_info_sizer()
p->sliced_info->SetTextAndShow(siMateril_unit, "N/A");
}
}
Layout();
}
void Sidebar::show_sliced_info_sizer(const bool show)
@ -1338,21 +1349,78 @@ private:
Plater *plater;
static const std::regex pattern_drop;
#if ENABLE_GCODE_VIEWER
static const std::regex pattern_gcode_drop;
#endif // ENABLE_GCODE_VIEWER
};
const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase);
#if ENABLE_GCODE_VIEWER
const std::regex PlaterDropTarget::pattern_gcode_drop(".*[.](gcode)", std::regex::icase);
#endif // ENABLE_GCODE_VIEWER
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
{
std::vector<fs::path> paths;
for (const auto &filename : filenames) {
#if ENABLE_GCODE_VIEWER
#ifdef WIN32
// hides the system icon
this->MSWUpdateDragImageOnLeave();
#endif // WIN32
// gcode section
for (const auto& filename : filenames) {
fs::path path(into_path(filename));
if (std::regex_match(path.string(), pattern_drop)) {
if (std::regex_match(path.string(), pattern_gcode_drop))
paths.push_back(std::move(path));
} else {
}
if (paths.size() > 1) {
wxMessageDialog((wxWindow*)plater, _L("You can open only one .gcode file at a time."),
wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxCLOSE | wxICON_WARNING | wxCENTRE).ShowModal();
return false;
}
else if (paths.size() == 1) {
if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) {
plater->load_gcode(from_path(paths.front()));
return true;
}
else {
if (wxMessageDialog((wxWindow*)plater, _L("Do you want to switch to G-code preview ?"),
wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop G-code file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) {
if (plater->model().objects.empty() ||
wxMessageDialog((wxWindow*)plater, _L("Switching to G-code preview mode will remove all objects, continue?"),
wxString(SLIC3R_APP_NAME) + " - " + _L("Switch to G-code preview mode"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES) {
wxGetApp().mainframe->set_mode(MainFrame::EMode::GCodeViewer);
plater->load_gcode(from_path(paths.front()));
return true;
}
}
return false;
}
}
#endif // ENABLE_GCODE_VIEWER
// model section
for (const auto &filename : filenames) {
fs::path path(into_path(filename));
if (std::regex_match(path.string(), pattern_drop))
paths.push_back(std::move(path));
else
return false;
}
#if ENABLE_GCODE_VIEWER
if (wxGetApp().mainframe->get_mode() == MainFrame::EMode::GCodeViewer) {
if (wxMessageDialog((wxWindow*)plater, _L("Do you want to exit G-code preview ?"),
wxString(SLIC3R_APP_NAME) + " - " + _L("Drag and drop model file"), wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
wxGetApp().mainframe->set_mode(MainFrame::EMode::Editor);
else
return false;
}
#endif // ENABLE_GCODE_VIEWER
wxString snapshot_label;
assert(! paths.empty());
@ -1379,13 +1447,10 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
// because right now the plater is not cleared, we set the project file (from the latest imported .3mf or .amf file)
// only if not set yet
// if res is empty no data has been loaded
if (!res.empty() && plater->get_project_filename().empty())
{
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it)
{
if (!res.empty() && plater->get_project_filename().empty()) {
for (std::vector<fs::path>::const_reverse_iterator it = paths.rbegin(); it != paths.rend(); ++it) {
std::string filename = (*it).filename().string();
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf"))
{
if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) {
plater->set_project_filename(from_path(*it));
break;
}
@ -1426,7 +1491,11 @@ struct Plater::priv
Slic3r::SLAPrint sla_print;
Slic3r::Model model;
PrinterTechnology printer_technology = ptFFF;
#if ENABLE_GCODE_VIEWER
Slic3r::GCodeProcessor::Result gcode_result;
#else
Slic3r::GCodePreviewData gcode_preview_data;
#endif // ENABLE_GCODE_VIEWER
// GUI elements
wxSizer* panel_sizer{ nullptr };
@ -1539,6 +1608,15 @@ struct Plater::priv
bool init_view_toolbar();
bool init_collapse_toolbar();
#if ENABLE_GCODE_VIEWER
void update_preview_bottom_toolbar();
void update_preview_moves_slider();
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void reset_gcode_toolpaths();
#endif // ENABLE_GCODE_VIEWER
void reset_all_gizmos();
void update_ui_from_settings();
void update_main_toolbar_tooltips();
@ -1659,7 +1737,7 @@ struct Plater::priv
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
bool can_delete() const;
bool can_delete_all() const;
@ -1755,7 +1833,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
background_process.set_fff_print(&fff_print);
background_process.set_sla_print(&sla_print);
#if ENABLE_GCODE_VIEWER
background_process.set_gcode_result(&gcode_result);
#else
background_process.set_gcode_preview_data(&gcode_preview_data);
#endif // ENABLE_GCODE_VIEWER
background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
{
std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) {
@ -1780,7 +1862,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this);
view3D = new View3D(q, &model, config, &background_process);
#if ENABLE_GCODE_VIEWER
preview = new Preview(q, &model, config, &background_process, &gcode_result, [this]() { schedule_background_process(); });
#else
preview = new Preview(q, &model, config, &background_process, &gcode_preview_data, [this]() { schedule_background_process(); });
#endif // ENABLE_GCODE_VIEWER
#ifdef __APPLE__
// set default view_toolbar icons size equal to GLGizmosManager::Default_Icons_Size
@ -1860,24 +1946,19 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
{
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
config->option<ConfigOptionString>("bed_custom_texture")->value,
config->option<ConfigOptionString>("bed_custom_model")->value);
});
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
// Preview events:
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
{
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
config->option<ConfigOptionString>("bed_custom_texture")->value,
config->option<ConfigOptionString>("bed_custom_model")->value);
});
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [q](SimpleEvent&) { q->set_bed_shape(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
#if ENABLE_GCODE_VIEWER
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_LAYERS_SLIDER, [this](wxKeyEvent& evt) { preview->move_layers_slider(evt); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); });
#else
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
#endif // ENABLE_GCODE_VIEWER
q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
@ -2015,6 +2096,10 @@ void Plater::priv::select_view_3D(const std::string& name)
set_current_panel(view3D);
else if (name == "Preview")
set_current_panel(preview);
#if ENABLE_GCODE_VIEWER
wxGetApp().update_ui_from_settings();
#endif // ENABLE_GCODE_VIEWER
}
void Plater::priv::select_next_view_3D()
@ -2529,8 +2614,10 @@ void Plater::priv::deselect_all()
void Plater::priv::remove(size_t obj_idx)
{
#if !ENABLE_GCODE_VIEWER
// Prevent toolpaths preview from rendering while we modify the Print object
preview->set_enabled(false);
#endif // !ENABLE_GCODE_VIEWER
if (view3D->is_layers_editing_enabled())
view3D->enable_layers_editing(false);
@ -2562,12 +2649,19 @@ void Plater::priv::reset()
set_project_filename(wxEmptyString);
#if !ENABLE_GCODE_VIEWER
// Prevent toolpaths preview from rendering while we modify the Print object
preview->set_enabled(false);
#endif // !ENABLE_GCODE_VIEWER
if (view3D->is_layers_editing_enabled())
view3D->enable_layers_editing(false);
#if ENABLE_GCODE_VIEWER
reset_gcode_toolpaths();
gcode_result.reset();
#endif // ENABLE_GCODE_VIEWER
// Stop and reset the Print content.
this->background_process.reset();
model.clear_objects();
@ -2713,10 +2807,19 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
this->sidebar->show_sliced_info_sizer(false);
// Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared.
// Otherwise they will be just refreshed.
#if ENABLE_GCODE_VIEWER
if (this->preview != nullptr) {
// If the preview is not visible, the following line just invalidates the preview,
// but the G-code paths or SLA preview are calculated first once the preview is made visible.
this->preview->get_canvas3d()->reset_gcode_toolpaths();
this->preview->reload_print();
}
#else
if (this->preview != nullptr)
// If the preview is not visible, the following line just invalidates the preview,
// but the G-code paths or SLA preview are calculated first once the preview is made visible.
this->preview->reload_print();
#endif // ENABLE_GCODE_VIEWER
// In FDM mode, we need to reload the 3D scene because of the wipe tower preview box.
// In SLA mode, we need to reload the 3D scene every time to show the support structures.
if (this->printer_technology == ptSLA || (this->printer_technology == ptFFF && this->config->opt_bool("wipe_tower")))
@ -3952,6 +4055,25 @@ bool Plater::priv::init_collapse_toolbar()
return true;
}
#if ENABLE_GCODE_VIEWER
void Plater::priv::update_preview_bottom_toolbar()
{
preview->update_bottom_toolbar();
}
void Plater::priv::update_preview_moves_slider()
{
preview->update_moves_slider();
}
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void Plater::priv::reset_gcode_toolpaths()
{
preview->get_canvas3d()->reset_gcode_toolpaths();
}
#endif // ENABLE_GCODE_VIEWER
bool Plater::priv::can_set_instance_to_object() const
{
const int obj_idx = get_selected_object_idx();
@ -4026,11 +4148,10 @@ bool Plater::priv::can_reload_from_disk() const
return !paths.empty();
}
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
bool new_shape = bed.set_shape(shape, custom_texture, custom_model);
if (new_shape)
{
bool new_shape = bed.set_shape(shape, custom_texture, custom_model, force_as_custom);
if (new_shape) {
if (view3D) view3D->bed_shape_changed();
if (preview) preview->bed_shape_changed();
}
@ -4483,6 +4604,44 @@ void Plater::extract_config_from_project()
load_files(input_paths, false, true);
}
#if ENABLE_GCODE_VIEWER
void Plater::load_gcode()
{
// Ask user for a gcode file name.
wxString input_file;
wxGetApp().load_gcode(this, input_file);
// And finally load the gcode file.
load_gcode(input_file);
}
void Plater::load_gcode(const wxString& filename)
{
if (filename.empty() || m_last_loaded_gcode == filename)
return;
m_last_loaded_gcode = filename;
// cleanup view before to start loading/processing
p->gcode_result.reset();
reset_gcode_toolpaths();
p->preview->reload_print(false);
p->get_current_canvas3D()->render();
wxBusyCursor wait;
// process gcode
GCodeProcessor processor;
processor.enable_producers(true);
processor.enable_machine_envelope_processing(true);
processor.process_file(filename.ToUTF8().data());
p->gcode_result = std::move(processor.extract_result());
// show results
p->preview->reload_print(false);
p->preview->get_canvas3d()->zoom_to_gcode();
}
#endif // ENABLE_GCODE_VIEWER
std::vector<size_t> Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool imperial_units /*= false*/) { return p->load_files(input_files, load_model, load_config, imperial_units); }
// To be called when providing a list of files to the GUI slic3r on command line.
@ -4983,6 +5142,9 @@ void Plater::reslice()
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
return;
#if ENABLE_GCODE_VIEWER
bool clean_gcode_toolpaths = true;
#endif // ENABLE_GCODE_VIEWER
if (p->background_process.running())
{
if (wxGetApp().get_mode() == comSimple)
@ -4995,9 +5157,19 @@ void Plater::reslice()
}
else if (!p->background_process.empty() && !p->background_process.idle())
p->show_action_buttons(true);
#if ENABLE_GCODE_VIEWER
else
clean_gcode_toolpaths = false;
if (clean_gcode_toolpaths)
reset_gcode_toolpaths();
// update type of preview
p->preview->update_view_type(!clean_gcode_toolpaths);
#else
// update type of preview
p->preview->update_view_type(true);
#endif // ENABLE_GCODE_VIEWER
}
void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages)
@ -5235,9 +5407,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
if (bed_shape_changed)
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
set_bed_shape();
if (update_scheduled)
update();
@ -5248,11 +5418,24 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
void Plater::set_bed_shape() const
{
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
#if ENABLE_GCODE_VIEWER
set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
#else
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
#endif // ENABLE_GCODE_VIEWER
}
#if ENABLE_GCODE_VIEWER
void Plater::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) const
{
p->set_bed_shape(shape, custom_texture, custom_model, force_as_custom);
}
#endif // ENABLE_GCODE_VIEWER
void Plater::force_filament_colors_update()
{
bool update_scheduled = false;
@ -5413,24 +5596,43 @@ PrinterTechnology Plater::printer_technology() const
const DynamicPrintConfig * Plater::config() const { return p->config; }
#if ENABLE_GCODE_VIEWER
bool Plater::set_printer_technology(PrinterTechnology printer_technology)
#else
void Plater::set_printer_technology(PrinterTechnology printer_technology)
#endif // ENABLE_GCODE_VIEWER
{
p->printer_technology = printer_technology;
#if ENABLE_GCODE_VIEWER
bool ret = p->background_process.select_technology(printer_technology);
if (ret) {
// Update the active presets.
}
#else
if (p->background_process.select_technology(printer_technology)) {
// Update the active presets.
}
#endif // ENABLE_GCODE_VIEWER
//FIXME for SLA synchronize
//p->background_process.apply(Model)!
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
if (wxGetApp().mainframe)
if (wxGetApp().mainframe != nullptr)
#if ENABLE_GCODE_VIEWER
wxGetApp().mainframe->update_editor_menubar();
#else
wxGetApp().mainframe->update_menubar();
#endif // ENABLE_GCODE_VIEWER
p->update_main_toolbar_tooltips();
p->sidebar->get_searcher().set_printer_technology(printer_technology);
#if ENABLE_GCODE_VIEWER
return ret;
#endif // ENABLE_GCODE_VIEWER
}
void Plater::changed_object(int obj_idx)
@ -5578,11 +5780,25 @@ bool Plater::init_view_toolbar()
return p->init_view_toolbar();
}
#if ENABLE_GCODE_VIEWER
void Plater::enable_view_toolbar(bool enable)
{
p->view_toolbar.set_enabled(enable);
}
#endif // ENABLE_GCODE_VIEWER
bool Plater::init_collapse_toolbar()
{
return p->init_collapse_toolbar();
}
#if ENABLE_GCODE_VIEWER
void Plater::enable_collapse_toolbar(bool enable)
{
p->collapse_toolbar.set_enabled(enable);
}
#endif // ENABLE_GCODE_VIEWER
const Camera& Plater::get_camera() const
{
return p->camera;
@ -5636,6 +5852,23 @@ GLToolbar& Plater::get_collapse_toolbar()
return p->collapse_toolbar;
}
#if ENABLE_GCODE_VIEWER
void Plater::update_preview_bottom_toolbar()
{
p->update_preview_bottom_toolbar();
}
void Plater::update_preview_moves_slider()
{
p->update_preview_moves_slider();
}
void Plater::reset_gcode_toolpaths()
{
p->reset_gcode_toolpaths();
}
#endif // ENABLE_GCODE_VIEWER
const Mouse3DController& Plater::get_mouse3d_controller() const
{
return p->mouse3d_controller;
@ -5703,6 +5936,9 @@ bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot();
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
#if ENABLE_GCODE_VIEWER
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
#endif // ENABLE_GCODE_VIEWER
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); }

View file

@ -140,6 +140,10 @@ public:
void add_model(bool imperial_units = false);
void import_sl1_archive();
void extract_config_from_project();
#if ENABLE_GCODE_VIEWER
void load_gcode();
void load_gcode(const wxString& filename);
#endif // ENABLE_GCODE_VIEWER
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false);
// To be called when providing a list of files to the GUI slic3r on command line.
@ -219,6 +223,9 @@ public:
bool search_string_getter(int idx, const char** label, const char** tooltip);
// For the memory statistics.
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
#if ENABLE_GCODE_VIEWER
void clear_undo_redo_stack_main();
#endif // ENABLE_GCODE_VIEWER
// Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
void enter_gizmos_stack();
void leave_gizmos_stack();
@ -256,7 +263,11 @@ public:
PrinterTechnology printer_technology() const;
const DynamicPrintConfig * config() const;
#if ENABLE_GCODE_VIEWER
bool set_printer_technology(PrinterTechnology printer_technology);
#else
void set_printer_technology(PrinterTechnology printer_technology);
#endif // ENABLE_GCODE_VIEWER
void copy_selection_to_clipboard();
void paste_from_clipboard();
@ -282,7 +293,13 @@ public:
void sys_color_changed();
bool init_view_toolbar();
#if ENABLE_GCODE_VIEWER
void enable_view_toolbar(bool enable);
#endif // ENABLE_GCODE_VIEWER
bool init_collapse_toolbar();
#if ENABLE_GCODE_VIEWER
void enable_collapse_toolbar(bool enable);
#endif // ENABLE_GCODE_VIEWER
const Camera& get_camera() const;
Camera& get_camera();
@ -301,10 +318,21 @@ public:
const GLToolbar& get_collapse_toolbar() const;
GLToolbar& get_collapse_toolbar();
#if ENABLE_GCODE_VIEWER
void update_preview_bottom_toolbar();
void update_preview_moves_slider();
void reset_gcode_toolpaths();
void reset_last_loaded_gcode() { m_last_loaded_gcode = ""; }
#endif // ENABLE_GCODE_VIEWER
const Mouse3DController& get_mouse3d_controller() const;
Mouse3DController& get_mouse3d_controller();
void set_bed_shape() const;
#if ENABLE_GCODE_VIEWER
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false) const;
#endif // ENABLE_GCODE_VIEWER
const NotificationManager* get_notification_manager() const;
NotificationManager* get_notification_manager();
@ -358,6 +386,10 @@ private:
bool m_tracking_popup_menu = false;
wxString m_tracking_popup_menu_error_message;
#if ENABLE_GCODE_VIEWER
wxString m_last_loaded_gcode;
#endif // ENABLE_GCODE_VIEWER
void suppress_snapshots();
void allow_snapshots();

View file

@ -16,8 +16,11 @@
#include <GL/glew.h>
#include <boost/algorithm/string/predicate.hpp>
#if ENABLE_GCODE_VIEWER
#include <boost/log/trivial.hpp>
#endif // ENABLE_GCODE_VIEWER
static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f };
static const float UNIFORM_SCALE_COLOR[4] = { 0.923f, 0.504f, 0.264f, 1.0f };
namespace Slic3r {
namespace GUI {
@ -110,8 +113,10 @@ Selection::Selection()
, m_valid(false)
, m_scale_factor(1.0f)
{
#if !ENABLE_GCODE_VIEWER
m_arrow.reset(new GLArrow);
m_curved_arrow.reset(new GLCurvedArrow(16));
#endif // !ENABLE_GCODE_VIEWER
this->set_bounding_boxes_dirty();
#if ENABLE_RENDER_SELECTION_CENTER
@ -138,6 +143,10 @@ void Selection::set_volumes(GLVolumePtrs* volumes)
// Init shall be called from the OpenGL render function, so that the OpenGL context is initialized!
bool Selection::init()
{
#if ENABLE_GCODE_VIEWER
m_arrow.init_from(straight_arrow(10.0f, 5.0f, 5.0f, 10.0f, 1.0f));
m_curved_arrow.init_from(circular_arrow(16, 10.0f, 5.0f, 10.0f, 5.0f, 1.0f));
#else
if (!m_arrow->init())
return false;
@ -147,6 +156,7 @@ bool Selection::init()
return false;
m_curved_arrow->set_scale(5.0 * Vec3d::Ones());
#endif //ENABLE_GCODE_VIEWER
return true;
}
@ -1261,40 +1271,40 @@ void Selection::render_center(bool gizmo_is_dragging) const
}
#endif // ENABLE_RENDER_SELECTION_CENTER
void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const
void Selection::render_sidebar_hints(const std::string& sidebar_field) const
{
if (sidebar_field.empty())
return;
GLShaderProgram* shader = nullptr;
if (!boost::starts_with(sidebar_field, "layer"))
{
shader.start_using();
shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
shader->start_using();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_LIGHTING));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
if (!boost::starts_with(sidebar_field, "layer"))
{
if (!boost::starts_with(sidebar_field, "layer")) {
const Vec3d& center = get_bounding_box().center();
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
{
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) {
glsafe(::glTranslated(center(0), center(1), center(2)));
if (!boost::starts_with(sidebar_field, "position"))
{
if (!boost::starts_with(sidebar_field, "position")) {
Transform3d orient_matrix = Transform3d::Identity();
if (boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::starts_with(sidebar_field, "rotation"))
{
else if (boost::starts_with(sidebar_field, "rotation")) {
if (boost::ends_with(sidebar_field, "x"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::ends_with(sidebar_field, "y"))
{
else if (boost::ends_with(sidebar_field, "y")) {
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation(0) == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
@ -1305,21 +1315,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha
glsafe(::glMultMatrixd(orient_matrix.data()));
}
}
else if (is_single_volume() || is_single_modifier())
{
} else if (is_single_volume() || is_single_modifier()) {
glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
else
{
} else {
glsafe(::glTranslated(center(0), center(1), center(2)));
if (requires_local_axes())
{
if (requires_local_axes()) {
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
@ -1330,20 +1335,15 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, const Sha
render_sidebar_position_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "rotation"))
render_sidebar_rotation_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "scale"))
else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size"))
render_sidebar_scale_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "size"))
render_sidebar_size_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "layer"))
render_sidebar_layers_hints(sidebar_field);
glsafe(::glPopMatrix());
if (!boost::starts_with(sidebar_field, "layer"))
{
glsafe(::glDisable(GL_LIGHTING));
shader.stop_using();
}
shader->stop_using();
}
bool Selection::requires_local_axes() const
@ -1944,6 +1944,29 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons
glsafe(::glEnd());
}
#if ENABLE_GCODE_VIEWER
void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const
{
auto set_color = [](Axis axis) {
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("uniform_color", AXES_COLOR[axis], 4);
};
if (boost::ends_with(sidebar_field, "x")) {
set_color(X);
glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0));
m_arrow.render();
} else if (boost::ends_with(sidebar_field, "y")) {
set_color(Y);
m_arrow.render();
} else if (boost::ends_with(sidebar_field, "z")) {
set_color(Z);
glsafe(::glRotated(90.0, 1.0, 0.0, 0.0));
m_arrow.render();
}
}
#else
void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const
{
if (boost::ends_with(sidebar_field, "x"))
@ -1959,8 +1982,38 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field)
render_sidebar_position_hint(Z);
}
}
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_GCODE_VIEWER
void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const
{
auto set_color = [](Axis axis) {
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("uniform_color", AXES_COLOR[axis], 4);
};
auto render_sidebar_rotation_hint = [this]() {
m_curved_arrow.render();
glsafe(::glRotated(180.0, 0.0, 0.0, 1.0));
m_curved_arrow.render();
};
if (boost::ends_with(sidebar_field, "x")) {
set_color(X);
glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
render_sidebar_rotation_hint();
} else if (boost::ends_with(sidebar_field, "y")) {
set_color(Y);
glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
render_sidebar_rotation_hint();
} else if (boost::ends_with(sidebar_field, "z")) {
set_color(Z);
render_sidebar_rotation_hint();
}
}
#else
void Selection::render_sidebar_rotation_hints(const std::string & sidebar_field) const
{
if (boost::ends_with(sidebar_field, "x"))
{
@ -1975,11 +2028,33 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field)
else if (boost::ends_with(sidebar_field, "z"))
render_sidebar_rotation_hint(Z);
}
#endif // ENABLE_GCODE_VIEWER
void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const
{
bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling();
auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis) {
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("uniform_color", uniform_scale ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis], 4);
glsafe(::glTranslated(0.0, 5.0, 0.0));
#if ENABLE_GCODE_VIEWER
m_arrow.render();
#else
m_arrow->render();
#endif // ENABLE_GCODE_VIEWER
glsafe(::glTranslated(0.0, -10.0, 0.0));
glsafe(::glRotated(180.0, 0.0, 0.0, 1.0));
#if ENABLE_GCODE_VIEWER
m_arrow.render();
#else
m_arrow->render();
#endif // ENABLE_GCODE_VIEWER
};
if (boost::ends_with(sidebar_field, "x") || uniform_scale)
{
glsafe(::glPushMatrix());
@ -2004,11 +2079,6 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con
}
}
void Selection::render_sidebar_size_hints(const std::string& sidebar_field) const
{
render_sidebar_scale_hints(sidebar_field);
}
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
{
static const double Margin = 10.0;
@ -2081,6 +2151,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
glsafe(::glDisable(GL_BLEND));
}
#if !ENABLE_GCODE_VIEWER
void Selection::render_sidebar_position_hint(Axis axis) const
{
m_arrow->set_color(AXES_COLOR[axis], 3);
@ -2107,10 +2178,7 @@ void Selection::render_sidebar_scale_hint(Axis axis) const
glsafe(::glRotated(180.0, 0.0, 0.0, 1.0));
m_arrow->render();
}
void Selection::render_sidebar_size_hint(Axis axis, double length) const
{
}
#endif // !ENABLE_GCODE_VIEWER
#ifndef NDEBUG
static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)

View file

@ -3,7 +3,9 @@
#include <set>
#include "libslic3r/Geometry.hpp"
#if ENABLE_GCODE_VIEWER
#include "GLModel.hpp"
#endif // ENABLE_GCODE_VIEWER
#if ENABLE_RENDER_SELECTION_CENTER
class GLUquadric;
@ -19,6 +21,7 @@ class GLVolume;
class GLArrow;
class GLCurvedArrow;
class DynamicPrintConfig;
class GLShaderProgram;
using GLVolumePtrs = std::vector<GLVolume*>;
using ModelObjectPtrs = std::vector<ModelObject*>;
@ -218,10 +221,15 @@ private:
GLUquadricObj* m_quadric;
#endif // ENABLE_RENDER_SELECTION_CENTER
#if ENABLE_GCODE_VIEWER
GLModel m_arrow;
GLModel m_curved_arrow;
#else
// Arrows are saved through pointers to avoid including 3DScene.hpp.
// It also allows mutability.
std::unique_ptr<GLArrow> m_arrow;
std::unique_ptr<GLCurvedArrow> m_curved_arrow;
#endif // ENABLE_GCODE_VIEWER
mutable float m_scale_factor;
@ -336,7 +344,7 @@ public:
#if ENABLE_RENDER_SELECTION_CENTER
void render_center(bool gizmo_is_dragging) const;
#endif // ENABLE_RENDER_SELECTION_CENTER
void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const;
void render_sidebar_hints(const std::string& sidebar_field) const;
bool requires_local_axes() const;
@ -377,12 +385,12 @@ private:
void render_sidebar_position_hints(const std::string& sidebar_field) const;
void render_sidebar_rotation_hints(const std::string& sidebar_field) const;
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_layers_hints(const std::string& sidebar_field) const;
#if !ENABLE_GCODE_VIEWER
void render_sidebar_position_hint(Axis axis) 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_GCODE_VIEWER
public:
enum SyncRotationType {

View file

@ -174,7 +174,6 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string,
const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18;
bool wxCheckListBoxComboPopup::Create(wxWindow* parent)
{
@ -198,17 +197,22 @@ wxString wxCheckListBoxComboPopup::GetStringValue() const
wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
{
// matches owner wxComboCtrl's width
// and sets height dinamically in dependence of contained items count
// set width dinamically in dependence of items text
// and set height dinamically in dependence of items count
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr)
{
if (cmb != nullptr) {
wxSize size = GetComboCtrl()->GetSize();
unsigned int count = GetCount();
if (count > 0)
size.SetHeight(count * DefaultItemHeight);
if (count > 0) {
int max_width = size.x;
for (unsigned int i = 0; i < count; ++i) {
max_width = std::max(max_width, 60 + GetTextExtent(GetString(i)).x);
}
size.SetWidth(max_width);
size.SetHeight(count * cmb->GetCharHeight());
}
else
size.SetHeight(DefaultHeight);

View file

@ -63,7 +63,6 @@ class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
{
static const unsigned int DefaultWidth;
static const unsigned int DefaultHeight;
static const unsigned int DefaultItemHeight;
wxString m_text;

View file

@ -244,8 +244,12 @@ std::string gcode(Print & print)
boost::filesystem::path temp = boost::filesystem::unique_path();
print.set_status_silent();
print.process();
#if ENABLE_GCODE_VIEWER
print.export_gcode(temp.string(), nullptr, nullptr);
#else
print.export_gcode(temp.string(), nullptr);
std::ifstream t(temp.string());
#endif // ENABLE_GCODE_VIEWER
std::ifstream t(temp.string());
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
boost::nowide::remove(temp.string().c_str());
return str;

View file

@ -50,7 +50,11 @@ SCENARIO("Model construction", "[Model]") {
print.apply(model, config);
print.process();
boost::filesystem::path temp = boost::filesystem::unique_path();
#if ENABLE_GCODE_VIEWER
print.export_gcode(temp.string(), nullptr, nullptr);
#else
print.export_gcode(temp.string(), nullptr);
#endif // ENABLE_GCODE_VIEWER
REQUIRE(boost::filesystem::exists(temp));
REQUIRE(boost::filesystem::is_regular_file(temp));
REQUIRE(boost::filesystem::file_size(temp) > 0);

View file

@ -15,7 +15,7 @@ REGISTER_CLASS(Filler, "Filler");
REGISTER_CLASS(Flow, "Flow");
REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
REGISTER_CLASS(GCode, "GCode");
REGISTER_CLASS(GCodePreviewData, "GCode::PreviewData");
//REGISTER_CLASS(GCodePreviewData, "GCode::PreviewData");
// REGISTER_CLASS(GCodeSender, "GCode::Sender");
REGISTER_CLASS(Layer, "Layer");
REGISTER_CLASS(SupportLayer, "Layer::Support");

View file

@ -26,14 +26,14 @@
croak("%s\n", e.what());
}
%};
void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data)
%code%{
try {
THIS->do_export(print, path, preview_data);
} catch (std::exception& e) {
croak("%s\n", e.what());
}
%};
// void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data)
// %code%{
// try {
// THIS->do_export(print, path, preview_data);
// } catch (std::exception& e) {
// croak("%s\n", e.what());
// }
// %};
Ref<Vec2d> origin()
%code{% RETVAL = &(THIS->origin()); %};
@ -60,26 +60,26 @@
%code{% RETVAL = const_cast<StaticPrintConfig*>(static_cast<const StaticPrintConfig*>(static_cast<const PrintObjectConfig*>(&THIS->config()))); %};
};
%name{Slic3r::GCode::PreviewData} class GCodePreviewData {
GCodePreviewData();
~GCodePreviewData();
void reset();
bool empty() const;
void set_type(int type)
%code%{
if ((0 <= type) && (type < GCodePreviewData::Extrusion::Num_View_Types))
THIS->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
%};
int type() %code%{ RETVAL = (int)THIS->extrusion.view_type; %};
void set_extrusion_flags(int flags)
%code%{ THIS->extrusion.role_flags = (unsigned int)flags; %};
void set_travel_visible(bool visible)
%code%{ THIS->travel.is_visible = visible; %};
void set_retractions_visible(bool visible)
%code%{ THIS->retraction.is_visible = visible; %};
void set_unretractions_visible(bool visible)
%code%{ THIS->unretraction.is_visible = visible; %};
void set_shells_visible(bool visible)
%code%{ THIS->shell.is_visible = visible; %};
void set_extrusion_paths_colors(std::vector<std::string> colors);
};
//%name{Slic3r::GCode::PreviewData} class GCodePreviewData {
// GCodePreviewData();
// ~GCodePreviewData();
// void reset();
// bool empty() const;
// void set_type(int type)
// %code%{
// if ((0 <= type) && (type < GCodePreviewData::Extrusion::Num_View_Types))
// THIS->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
// %};
// int type() %code%{ RETVAL = (int)THIS->extrusion.view_type; %};
// void set_extrusion_flags(int flags)
// %code%{ THIS->extrusion.role_flags = (unsigned int)flags; %};
// void set_travel_visible(bool visible)
// %code%{ THIS->travel.is_visible = visible; %};
// void set_retractions_visible(bool visible)
// %code%{ THIS->retraction.is_visible = visible; %};
// void set_unretractions_visible(bool visible)
// %code%{ THIS->unretraction.is_visible = visible; %};
// void set_shells_visible(bool visible)
// %code%{ THIS->shell.is_visible = visible; %};
// void set_extrusion_paths_colors(std::vector<std::string> colors);
//};

View file

@ -76,10 +76,10 @@ _constant()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->skirt()); %};
Ref<ExtrusionEntityCollection> brim()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->brim()); %};
std::string estimated_normal_print_time()
%code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %};
std::string estimated_silent_print_time()
%code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %};
// std::string estimated_normal_print_time()
// %code%{ RETVAL = THIS->print_statistics().estimated_normal_print_time; %};
// std::string estimated_silent_print_time()
// %code%{ RETVAL = THIS->print_statistics().estimated_silent_print_time; %};
double total_used_filament()
%code%{ RETVAL = THIS->print_statistics().total_used_filament; %};
double total_extruded_volume()

View file

@ -191,9 +191,9 @@ GCode* O_OBJECT_SLIC3R
Ref<GCode> O_OBJECT_SLIC3R_T
Clone<GCode> O_OBJECT_SLIC3R_T
GCodePreviewData* O_OBJECT_SLIC3R
Ref<GCodePreviewData> O_OBJECT_SLIC3R_T
Clone<GCodePreviewData> O_OBJECT_SLIC3R_T
//GCodePreviewData* O_OBJECT_SLIC3R
//Ref<GCodePreviewData> O_OBJECT_SLIC3R_T
//Clone<GCodePreviewData> O_OBJECT_SLIC3R_T
MotionPlanner* O_OBJECT_SLIC3R
Ref<MotionPlanner> O_OBJECT_SLIC3R_T

View file

@ -155,9 +155,9 @@
%typemap{Ref<GCode>}{simple};
%typemap{Clone<GCode>}{simple};
%typemap{GCodePreviewData*};
%typemap{Ref<GCodePreviewData>}{simple};
%typemap{Clone<GCodePreviewData>}{simple};
//%typemap{GCodePreviewData*};
//%typemap{Ref<GCodePreviewData>}{simple};
//%typemap{Clone<GCodePreviewData>}{simple};
%typemap{Points};
%typemap{Pointfs};