Merge branch 'ys_msw_dpi' of https://github.com/prusa3d/Slic3r into ys_msw_dpi

This commit is contained in:
bubnikv 2019-04-18 17:16:42 +02:00
commit 5e45cff855
55 changed files with 1384 additions and 1046 deletions

View File

@ -321,7 +321,7 @@ include_directories(${GLEW_INCLUDE_DIRS})
# l10n # l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
add_custom_target(pot add_custom_target(pot
COMMAND xgettext --keyword=L --from-code=UTF-8 --debug COMMAND xgettext --keyword=L --add-comments=TRN --from-code=UTF-8 --debug
-f "${L10N_DIR}/list.txt" -f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/Slic3rPE.pot" -o "${L10N_DIR}/Slic3rPE.pot"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}

View File

@ -61,3 +61,10 @@ Please note that the `CMAKE_OSX_DEPLOYMENT_TARGET` and `CMAKE_OSX_SYSROOT` optio
on both the dependencies bundle as well as Slic3r PE itself. on both the dependencies bundle as well as Slic3r PE itself.
Official Mac Slic3r builds are currently built against SDK 10.9 to ensure compatibility with older Macs. Official Mac Slic3r builds are currently built against SDK 10.9 to ensure compatibility with older Macs.
_Warning:_ XCode may be set such that it rejects SDKs bellow some version (silently, more or less).
This is set in the property list file
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Info.plist
To remove the limitation, simply delete the key `MinimumSDKVersion` from that file.

View File

@ -46,7 +46,7 @@ https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt.
2. Create template file(*.POT) with GNUgettext command: 2. Create template file(*.POT) with GNUgettext command:
``` ```
xgettext --keyword=L --from-code=UTF-8 --debug -o Slic3rPE.pot -f list.txt xgettext --keyword=L --add-comments=TRN --from-code=UTF-8 --debug -o Slic3rPE.pot -f list.txt
``` ```
Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding

View File

@ -5,13 +5,13 @@
<g id="layers"> <g id="layers">
<g> <g>
<g> <g>
<rect x="1" y="13" fill="#808080" width="14" height="2"/> <rect x="1" y="13" fill="#FFFFFF" width="14" height="2"/>
</g> </g>
<g> <g>
<rect x="1" y="10.6" fill="#808080" width="14" height="1.74"/> <rect x="1" y="10.6" fill="#FFFFFF" width="14" height="1.74"/>
</g> </g>
<g> <g>
<rect x="1" y="8.19" fill="#808080" width="14" height="1.47"/> <rect x="1" y="8.19" fill="#FFFFFF" width="14" height="1.47"/>
</g> </g>
<g> <g>
<rect x="1" y="5.79" fill="#ED6B21" width="14" height="1.2"/> <rect x="1" y="5.79" fill="#ED6B21" width="14" height="1.2"/>
@ -20,7 +20,7 @@
<rect x="1" y="3.39" fill="#ED6B21" width="14" height="0.93"/> <rect x="1" y="3.39" fill="#ED6B21" width="14" height="0.93"/>
</g> </g>
<g> <g>
<rect x="1" y="0.99" fill="#808080" width="14" height="0.67"/> <rect x="1" y="0.99" fill="#FFFFFF" width="14" height="0.67"/>
</g> </g>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 845 B

After

Width:  |  Height:  |  Size: 845 B

File diff suppressed because it is too large Load Diff

View File

@ -173,7 +173,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin // Toolchangeresult.gcode assumes the wipe tower corner is at the origin
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation/180.f * M_PI; float alpha = m_wipe_tower_rotation/180.f * float(M_PI);
WipeTower::xy start_pos = tcr.start_pos; WipeTower::xy start_pos = tcr.start_pos;
WipeTower::xy end_pos = tcr.end_pos; WipeTower::xy end_pos = tcr.end_pos;
start_pos.rotate(alpha); start_pos.rotate(alpha);
@ -519,43 +519,43 @@ void GCode::_do_export(Print &print, FILE *file)
// this->print_machine_envelope(file, print); // this->print_machine_envelope(file, print);
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
if (print.config().gcode_flavor.value == gcfMarlin) { if (print.config().gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]); m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]); m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]); m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]); m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]); m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]); m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]); m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]); m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]); m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]); m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]); m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]); m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]); m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]); m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]); m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]); m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]);
if (m_silent_time_estimator_enabled) if (m_silent_time_estimator_enabled)
{ {
m_silent_time_estimator.reset(); m_silent_time_estimator.reset();
m_silent_time_estimator.set_dialect(print.config().gcode_flavor); m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]); m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]); m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]); m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]); m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]); m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]); m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]); m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]); m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]); m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]); m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]); m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]); m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]); m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]); m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]); m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]); m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[1]);
if (print.config().single_extruder_multi_material) { if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only. // are considered to be active for the single extruder multi-material printers only.
@ -1054,26 +1054,54 @@ void GCode::_do_export(Print &print, FILE *file)
print.m_print_statistics.clear(); print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) { std::vector<Extruder> extruders = m_writer.extruders();
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f); if (! extruders.empty()) {
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);
double filament_weight = extruded_volume * extruder.filament_density() * 0.001; std::pair<std::string, unsigned int> out_filament_used_cm3("; filament used [cm3] = ", 0);
double filament_cost = filament_weight * extruder.filament_cost() * 0.001; std::pair<std::string, unsigned int> out_filament_used_g ("; filament used [g] = ", 0);
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament)); std::pair<std::string, unsigned int> out_filament_cost ("; filament cost = ", 0);
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001); for (const Extruder &extruder : extruders) {
if (filament_weight > 0.) { double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f);
print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight; double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
_write_format(file, "; filament used = %.1lf\n", filament_weight); double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
if (filament_cost > 0.) { double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost; auto append = [&extruder, &extruders](std::pair<std::string, unsigned int> &dst, const char *tmpl, double value) {
_write_format(file, "; filament cost = %.1lf\n", filament_cost); while (dst.second < extruder.id()) {
// Fill in the non-printing extruders with zeros.
dst.first += (dst.second > 0) ? ", 0" : "0";
++ dst.second;
}
if (dst.second > 0)
dst.first += ", ";
char buf[64];
sprintf(buf, tmpl, value);
dst.first += buf;
++ dst.second;
};
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
append(out_filament_used_mm, "%.1lf", used_filament);
append(out_filament_used_cm3, "%.1lf", extruded_volume * 0.001);
if (filament_weight > 0.) {
print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight;
append(out_filament_used_g, "%.1lf", filament_weight);
if (filament_cost > 0.) {
print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost;
append(out_filament_cost, "%.1lf", filament_cost);
}
} }
print.m_print_statistics.total_used_filament += used_filament;
print.m_print_statistics.total_extruded_volume += extruded_volume;
print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
} }
print.m_print_statistics.total_used_filament += used_filament; _writeln(file, out_filament_used_mm.first);
print.m_print_statistics.total_extruded_volume += extruded_volume; _writeln(file, out_filament_used_cm3.first);
print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; if (out_filament_used_g.second)
print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; _writeln(file, out_filament_used_g.first);
if (out_filament_cost.second)
_writeln(file, out_filament_cost.first);
} }
_write_format(file, "; total filament used [g] = %.1lf\n", print.m_print_statistics.total_weight);
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
if (m_silent_time_estimator_enabled) if (m_silent_time_estimator_enabled)
@ -1528,7 +1556,7 @@ void GCode::process_layer(
std::max<int>(region.config().perimeter_extruder.value - 1, 0); std::max<int>(region.config().perimeter_extruder.value - 1, 0);
// Let's recover vector of extruder overrides: // Let's recover vector of extruder overrides:
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, (int)layer_to_print.object()->copies().size());
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : layer_tools.extruders) for (unsigned int extruder : layer_tools.extruders)

View File

@ -197,7 +197,7 @@ private:
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
const float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^2 const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
const float WT_EPSILON = 1e-3f; const float WT_EPSILON = 1e-3f;
@ -224,8 +224,8 @@ private:
bool m_retain_speed_override = true; bool m_retain_speed_override = true;
bool m_adhesion = true; bool m_adhesion = true;
float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill. float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter. float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
struct FilamentParameters { struct FilamentParameters {
@ -269,12 +269,12 @@ private:
{ {
if ( layer_height < 0 ) if ( layer_height < 0 )
return m_extrusion_flow; return m_extrusion_flow;
return layer_height * ( m_perimeter_width - layer_height * (1-M_PI/4.f)) / Filament_Area; return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
} }
// Calculates length of extrusion line to extrude given volume // Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const { float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)))); return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
} }
// Calculates depth for all layers and propagates them downwards // Calculates depth for all layers and propagates them downwards

View File

@ -38,7 +38,6 @@ static const std::string MOVE_TYPE_STR[Slic3r::GCodeTimeEstimator::Block::Num_Ty
#endif // ENABLE_MOVE_STATS #endif // ENABLE_MOVE_STATS
namespace Slic3r { namespace Slic3r {
void GCodeTimeEstimator::Feedrates::reset() void GCodeTimeEstimator::Feedrates::reset()
{ {
feedrate = 0.0f; feedrate = 0.0f;
@ -695,6 +694,8 @@ namespace Slic3r {
set_axis_position(X, 0.0f); set_axis_position(X, 0.0f);
set_axis_position(Y, 0.0f); set_axis_position(Y, 0.0f);
set_axis_position(Z, 0.0f); set_axis_position(Z, 0.0f);
if (get_e_local_positioning_type() == Absolute)
set_axis_position(E, 0.0f);
set_additional_time(0.0f); set_additional_time(0.0f);
@ -715,7 +716,6 @@ namespace Slic3r {
_blocks.clear(); _blocks.clear();
} }
void GCodeTimeEstimator::_calculate_time() void GCodeTimeEstimator::_calculate_time()
{ {
PROFILE_FUNC(); PROFILE_FUNC();

View File

@ -943,7 +943,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane. // Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
// This method is cheap in that it does not make any unnecessary copy of the volume meshes. // This method is cheap in that it does not make any unnecessary copy of the volume meshes.
// This method is used by the auto arrange function. // This method is used by the auto arrange function.
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
{ {
Points pts; Points pts;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
@ -1189,6 +1189,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
volume->mesh.transform(instance_matrix * volume_matrix, true); volume->mesh.transform(instance_matrix * volume_matrix, true);
// Perform cut // Perform cut
volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer tms(&volume->mesh); TriangleMeshSlicer tms(&volume->mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh); tms.cut(float(z), &upper_mesh, &lower_mesh);

View File

@ -234,7 +234,7 @@ public:
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane. // Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
// This method is cheap in that it does not make any unnecessary copy of the volume meshes. // This method is cheap in that it does not make any unnecessary copy of the volume meshes.
// This method is used by the auto arrange function. // This method is used by the auto arrange function.
Polygon convex_hull_2d(const Transform3d &trafo_instance); Polygon convex_hull_2d(const Transform3d &trafo_instance) const;
#if ENABLE_VOLUMES_CENTERING_FIXES #if ENABLE_VOLUMES_CENTERING_FIXES
void center_around_origin(bool include_modifiers = true); void center_around_origin(bool include_modifiers = true);

View File

@ -1,5 +1,6 @@
#include "ModelArrange.hpp" #include "ModelArrange.hpp"
#include "Model.hpp" #include "Model.hpp"
#include "Geometry.hpp"
#include "SVG.hpp" #include "SVG.hpp"
#include <libnest2d.h> #include <libnest2d.h>
@ -551,7 +552,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
ret.reserve(s); ret.reserve(s);
for(ModelObject* objptr : model.objects) { for(ModelObject* objptr : model.objects) {
if(objptr) { if (! objptr->instances.empty()) {
// TODO export the exact 2D projection. Cannot do it as libnest2d // TODO export the exact 2D projection. Cannot do it as libnest2d
// does not support concave shapes (yet). // does not support concave shapes (yet).
@ -572,23 +573,23 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
clpath = Slic3rMultiPoint_to_ClipperPath(p); clpath = Slic3rMultiPoint_to_ClipperPath(p);
} }
Vec3d rotation0 = objptr->instances.front()->get_rotation();
rotation0(2) = 0.;
for(ModelInstance* objinst : objptr->instances) { for(ModelInstance* objinst : objptr->instances) {
if(objinst) { ClipperLib::Polygon pn;
ClipperLib::Polygon pn; pn.Contour = clpath;
pn.Contour = clpath;
// Efficient conversion to item. // Efficient conversion to item.
Item item(std::move(pn)); Item item(std::move(pn));
// Invalid geometries would throw exceptions when arranging // Invalid geometries would throw exceptions when arranging
if(item.vertexCount() > 3) { if(item.vertexCount() > 3) {
item.rotation(objinst->get_rotation(Z)); item.rotation(float(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()))),
item.translation({ item.translation({
ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR)
}); });
ret.emplace_back(objinst, item); ret.emplace_back(objinst, item);
}
} }
} }
} }

View File

@ -1139,31 +1139,29 @@ std::string Print::validate() const
// Check horizontal clearance. // Check horizontal clearance.
{ {
Polygons convex_hulls_other; Polygons convex_hulls_other;
for (const PrintObject *object : m_objects) { for (const PrintObject *print_object : m_objects) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->copies().empty());
// Get convex hull of all meshes assigned to this print object. // Get convex hull of all meshes assigned to this print object.
Polygon convex_hull; ModelInstance *model_instance0 = print_object->model_object()->instances.front();
{ Vec3d rotation = model_instance0->get_rotation();
Polygons mesh_convex_hulls; rotation.z() = 0.;
for (const std::vector<int> &volumes : object->region_volumes) // Calculate the convex hull of a printable object centered around X=0,Y=0.
for (int volume_id : volumes)
mesh_convex_hulls.emplace_back(object->model_object()->volumes[volume_id]->mesh.convex_hull());
// make a single convex hull for all of them
convex_hull = Slic3r::Geometry::convex_hull(mesh_convex_hulls);
}
// Apply the same transformations we apply to the actual meshes when slicing them.
object->model_object()->instances.front()->transform_polygon(&convex_hull);
// Grow convex hull with the clearance margin. // Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2) // FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the // which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up. // appropriate object distance. Even if I set this to jtMiter the warning still shows up.
convex_hull = offset(convex_hull, scale_(m_config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); Polygon convex_hull0 = offset(
print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
scale_(m_config.extruder_clearance_radius.value) / 2., jtRound, scale_(0.1)).front();
// Now we check that no instance of convex_hull intersects any of the previously checked object instances. // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const Point &copy : object->m_copies) { for (const Point &copy : print_object->m_copies) {
Polygon p = convex_hull; Polygon convex_hull = convex_hull0;
p.translate(copy); convex_hull.translate(copy);
if (! intersection(convex_hulls_other, p).empty()) if (! intersection(convex_hulls_other, convex_hull).empty())
return L("Some objects are too close; your extruder will collide with them."); return L("Some objects are too close; your extruder will collide with them.");
polygons_append(convex_hulls_other, p); polygons_append(convex_hulls_other, convex_hull);
} }
} }
} }

View File

@ -2926,7 +2926,7 @@ CLIActionsConfigDef::CLIActionsConfigDef()
// Actions: // Actions:
def = this->add("export_obj", coBool); def = this->add("export_obj", coBool);
def->label = L("Export SVG"); def->label = L("Export OBJ");
def->tooltip = L("Export the model(s) as OBJ."); def->tooltip = L("Export the model(s) as OBJ.");
def->default_value = new ConfigOptionBool(false); def->default_value = new ConfigOptionBool(false);

View File

@ -1813,6 +1813,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1840,6 +1841,7 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();

View File

@ -552,6 +552,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
float layerh, ThrowOnCancel thrfn) float layerh, ThrowOnCancel thrfn)
{ {
TriangleMesh m = mesh; TriangleMesh m = mesh;
m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&m); TriangleMeshSlicer slicer(&m);
auto bb = mesh.bounding_box(); auto bb = mesh.bounding_box();

View File

@ -817,6 +817,10 @@ public:
meshcache = mesh(merged); meshcache = mesh(merged);
// The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this.
meshcache.require_shared_vertices();
// TODO: Is this necessary? // TODO: Is this necessary?
//meshcache.repair(); //meshcache.repair();
@ -2231,6 +2235,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
TriangleMesh fullmesh = m_impl->merged_mesh(); TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad()); fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&fullmesh); TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret; SlicedSupports ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn); slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
@ -2243,6 +2248,7 @@ SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
{ {
TriangleMesh fullmesh = m_impl->merged_mesh(); TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad()); fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&fullmesh); TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret; SlicedSupports ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn); slicer.slice(heights, cr, &ret, get().ctl().cancelfn);

View File

@ -598,8 +598,9 @@ std::string SLAPrint::validate() const
for(SLAPrintObject * po : m_objects) { for(SLAPrintObject * po : m_objects) {
const ModelObject *mo = po->model_object(); const ModelObject *mo = po->model_object();
bool supports_en = po->config().supports_enable.getBool();
if(po->config().supports_enable.getBool() && if(supports_en &&
mo->sla_points_status == sla::PointsStatus::UserModified && mo->sla_points_status == sla::PointsStatus::UserModified &&
mo->sla_support_points.empty()) mo->sla_support_points.empty())
return L("Cannot proceed without support points! " return L("Cannot proceed without support points! "
@ -613,7 +614,7 @@ std::string SLAPrint::validate() const
2 * cfg.head_back_radius_mm - 2 * cfg.head_back_radius_mm -
cfg.head_penetration_mm; cfg.head_penetration_mm;
if(pinhead_width > cfg.object_elevation_mm) if(supports_en && pinhead_width > cfg.object_elevation_mm)
return L("Elevation is too low for object."); return L("Elevation is too low for object.");
} }
@ -696,6 +697,7 @@ void SLAPrint::process()
po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
if(slindex_it == po.m_slice_index.end()) if(slindex_it == po.m_slice_index.end())
//TRN To be shown at the status bar on SLA slicing error.
throw std::runtime_error(L("Slicing had to be stopped " throw std::runtime_error(L("Slicing had to be stopped "
"due to an internal error.")); "due to an internal error."));
@ -706,6 +708,7 @@ void SLAPrint::process()
po.m_model_height_levels.emplace_back(it->slice_level()); po.m_model_height_levels.emplace_back(it->slice_level());
} }
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&mesh); TriangleMeshSlicer slicer(&mesh);
po.m_model_slices.clear(); po.m_model_slices.clear();

View File

@ -160,17 +160,13 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
m_support_material_interface_flow = m_support_material_flow; m_support_material_interface_flow = m_support_material_flow;
} }
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures. // Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.; coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id)
if (! object->region_volumes[region_id].empty()) { if (! object->region_volumes[region_id].empty())
const PrintRegionConfig &config = object->print()->get_region(region_id)->config(); external_perimeter_width = std::max(external_perimeter_width,
coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height); (coordf_t)object->print()->get_region(region_id)->flow(frExternalPerimeter, slicing_params.layer_height, false, false, -1, *object).width);
if (width <= 0.)
width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1);
external_perimeter_width = std::max(external_perimeter_width, width);
}
}
m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width);
m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value;

View File

@ -607,10 +607,12 @@ void TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
} }
void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel) void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel)
{ {
mesh = _mesh; mesh = _mesh;
_mesh->require_shared_vertices(); if (! mesh->has_shared_vertices())
throw std::invalid_argument("TriangleMeshSlicer was passed a mesh without shared vertices.");
throw_on_cancel(); throw_on_cancel();
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);

View File

@ -67,18 +67,17 @@ public:
TriangleMesh convex_hull_3d() const; TriangleMesh convex_hull_3d() const;
void reset_repair_stats(); void reset_repair_stats();
bool needed_repair() const; bool needed_repair() const;
void require_shared_vertices();
bool has_shared_vertices() const { return stl.v_shared != NULL; }
size_t facets_count() const { return this->stl.stats.number_of_facets; } size_t facets_count() const { return this->stl.stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; } bool empty() const { return this->facets_count() == 0; }
bool is_splittable() const; bool is_splittable() const;
stl_file stl; stl_file stl;
bool repaired; bool repaired;
private: private:
void require_shared_vertices();
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const; std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
friend class TriangleMeshSlicer;
}; };
enum FacetEdgeType { enum FacetEdgeType {
@ -159,9 +158,8 @@ class TriangleMeshSlicer
public: public:
typedef std::function<void()> throw_on_cancel_callback_type; typedef std::function<void()> throw_on_cancel_callback_type;
TriangleMeshSlicer() : mesh(nullptr) {} TriangleMeshSlicer() : mesh(nullptr) {}
// Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices() TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); }
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); } void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const; void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const; void slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
enum FacetSliceType { enum FacetSliceType {

View File

@ -88,7 +88,7 @@ std::string string_printf(const char *format, ...);
extern std::string timestamp_str(); extern std::string timestamp_str();
// Standard "generated by Slic3r version xxx timestamp xxx" header string, // Standard "generated by Slic3r version xxx timestamp xxx" header string,
// to be placed at the top of Slic3r generated files. // to be placed at the top of Slic3r generated files.
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
// getpid platform wrapper // getpid platform wrapper
extern unsigned get_current_pid(); extern unsigned get_current_pid();

View File

@ -1,7 +1,8 @@
#ifndef __SLIC3R_VERSION_H #ifndef __SLIC3R_VERSION_H
#define __SLIC3R_VERSION_H #define __SLIC3R_VERSION_H
#define SLIC3R_FORK_NAME "@SLIC3R_FORK_NAME@" #define SLIC3R_APP_NAME "@SLIC3R_APP_NAME@"
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
#define SLIC3R_VERSION "@SLIC3R_VERSION@" #define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SLIC3R_BUILD "@SLIC3R_BUILD@" #define SLIC3R_BUILD "@SLIC3R_BUILD@"

View File

@ -7,12 +7,12 @@ PRODUCTVERSION @SLIC3R_RC_VERSION@
BLOCK "040904E4" BLOCK "040904E4"
{ {
VALUE "CompanyName", "Prusa Research" VALUE "CompanyName", "Prusa Research"
VALUE "FileDescription", "Slic3r Prusa Edition" VALUE "FileDescription", "@SLIC3R_APP_NAME@"
VALUE "FileVersion", "@SLIC3R_BUILD_ID@" VALUE "FileVersion", "@SLIC3R_BUILD_ID@"
VALUE "ProductName", "Slic3r Prusa Edition" VALUE "ProductName", "@SLIC3R_APP_NAME@"
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@" VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
VALUE "InternalName", "Slic3r Prusa Edition" VALUE "InternalName", "@SLIC3R_APP_NAME@"
VALUE "LegalCopyright", "Copyright \251 2011-2017 Alessandro Ranelucci, \251 2016 Prusa Research" VALUE "LegalCopyright", "Copyright \251 2011-2019 Alessandro Ranelucci, \251 2016-2019 Prusa Research"
VALUE "OriginalFilename", "slic3r.exe" VALUE "OriginalFilename", "slic3r.exe"
} }
} }

View File

@ -3,15 +3,15 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>Slic3r</string> <string>@SLIC3R_APP_KEY@</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>Slic3r Copyright (C) 2011-2017 Alessandro Ranellucci, (C) 2016-2018 Prusa Reseach</string> <string>@SLIC3R_APP_NAME@ Copyright (C) 2011-2019 Alessandro Ranellucci, (C) 2016-2019 Prusa Reseach</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>Slic3r.icns</string> <string>Slic3r.icns</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Slic3r</string> <string>@SLIC3R_APP_KEY@</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>Slic3r @SLIC3R_BUILD_ID@</string> <string>@SLIC3R_APP_NAME@ @SLIC3R_BUILD_ID@</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>com.prusa3d.slic3r/</string> <string>com.prusa3d.slic3r/</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>

View File

@ -568,7 +568,7 @@ bool CLI::setup(int argc, char **argv)
void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
{ {
boost::nowide::cout boost::nowide::cout
<< "Slic3r Prusa Edition " << SLIC3R_BUILD << SLIC3R_APP_NAME << " " << SLIC3R_BUILD
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
<< " (with GUI support)" << " (with GUI support)"
#else /* SLIC3R_GUI */ #else /* SLIC3R_GUI */

View File

@ -32,7 +32,7 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
} }
AboutDialog::AboutDialog() AboutDialog::AboutDialog()
: DPIDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, : DPIDialog(NULL, wxID_ANY, wxString::Format(_(L("About %s")), SLIC3R_APP_NAME), wxDefaultPosition,
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{ {
SetFont(wxGetApp().normal_font()); SetFont(wxGetApp().normal_font());
@ -55,7 +55,7 @@ AboutDialog::AboutDialog()
// title // title
{ {
wxStaticText* title = new wxStaticText(this, wxID_ANY, "Slic3r Prusa Edition", wxDefaultPosition, wxDefaultSize); wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_APP_NAME, wxDefaultPosition, wxDefaultSize);
wxFont title_font = GUI::wxGetApp().bold_font();// wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont title_font = GUI::wxGetApp().bold_font();// wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
// title_font.SetWeight(wxFONTWEIGHT_BOLD); // title_font.SetWeight(wxFONTWEIGHT_BOLD);
title_font.SetFamily(wxFONTFAMILY_ROMAN); title_font.SetFamily(wxFONTFAMILY_ROMAN);

View File

@ -16,6 +16,7 @@
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PostProcessor.hpp"
#include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/GCode/PreviewData.hpp"
#include "libslic3r/libslic3r.h"
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
@ -390,7 +391,7 @@ void BackgroundSlicingProcess::prepare_upload()
// Generate a unique temp path to which the gcode/zip file is copied/exported // Generate a unique temp path to which the gcode/zip file is copied/exported
boost::filesystem::path source_path = boost::filesystem::temp_directory_path() boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
/ boost::filesystem::unique_path(".Slic3rPE.upload.%%%%-%%%%-%%%%-%%%%"); / boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%");
if (m_print == m_fff_print) { if (m_print == m_fff_print) {
m_print->set_status(95, "Running post-processing scripts"); m_print->set_status(95, "Running post-processing scripts");

View File

@ -261,9 +261,6 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency) wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
{ {
width = width * 0.1f * Slic3r::GUI::wxGetApp().em_unit() + 0.5f;
height = height * 0.1f * Slic3r::GUI::wxGetApp().em_unit() + 0.5f;
wxImage image(width, height); wxImage image(width, height);
image.InitAlpha(); image.InitAlpha();
unsigned char* imgdata = image.GetData(); unsigned char* imgdata = image.GetData();

View File

@ -281,13 +281,14 @@ void ConfigWizardPage::append_spacer(int space)
// Wizard pages // Wizard pages
PageWelcome::PageWelcome(ConfigWizard *parent) PageWelcome::PageWelcome(ConfigWizard *parent)
: ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))) : ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the %s %s")), SLIC3R_APP_NAME, ConfigWizard::name()), _(L("Welcome")))
, cbox_reset(nullptr) , cbox_reset(nullptr)
{ {
if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) {
wxString::Format(_(L("Run %s")), ConfigWizard::name()); wxString::Format(_(L("Run %s")), ConfigWizard::name());
append_text(wxString::Format( append_text(wxString::Format(
_(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), _(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")),
SLIC3R_APP_NAME,
ConfigWizard::name()) ConfigWizard::name())
); );
} else { } else {
@ -398,7 +399,9 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates")));
box_slic3r->SetValue(app_config->get("version_check") == "1"); box_slic3r->SetValue(app_config->get("version_check") == "1");
append(box_slic3r); append(box_slic3r);
append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."))); append_text(wxString::Format(_(L("If enabled, Slic3r checks for new versions of %s online. When a new version becomes available, "
"a notification is displayed at the next application startup (never during program usage). "
"This is only a notification mechanisms, no automatic installation is done.")), SLIC3R_APP_NAME));
append_spacer(VERTICAL_SPACING); append_spacer(VERTICAL_SPACING);
@ -420,7 +423,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
PageVendors::PageVendors(ConfigWizard *parent) PageVendors::PageVendors(ConfigWizard *parent)
: ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors")))
{ {
append_text(_(L("Pick another vendor supported by Slic3r PE:"))); append_text(wxString::Format(_(L("Pick another vendor supported by %s:")), SLIC3R_APP_NAME));
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
boldfont.SetWeight(wxFONTWEIGHT_BOLD); boldfont.SetWeight(wxFONTWEIGHT_BOLD);
@ -779,6 +782,8 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0;
const int yinc = item_height(); const int yinc = item_height();
int index_width = 0;
unsigned y = 0; unsigned y = 0;
for (size_t i = 0; i < items.size(); i++) { for (size_t i = 0; i < items.size(); i++) {
const Item& item = items[i]; const Item& item = items[i];
@ -796,8 +801,18 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); } else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); }
else if (i > item_active) { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); } else if (i > item_active) { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); }
dc.DrawText(item.label, x + bullet_w + em/2, y + yoff_text); x += + bullet_w + em/2;
const auto text_size = dc.GetTextExtent(item.label);
dc.DrawText(item.label, x, y + yoff_text);
y += yinc; y += yinc;
index_width = std::max(index_width, (int)x + text_size.x);
}
if (GetMinSize().x < index_width) {
CallAfter([this, index_width]() {
SetMinSize(wxSize(index_width, GetMinSize().y));
});
} }
} }

View File

@ -1486,6 +1486,8 @@ void GLCanvas3D::enable_layers_editing(bool enable)
if (v->is_modifier) if (v->is_modifier)
v->force_transparent = enable; v->force_transparent = enable;
} }
set_as_dirty();
} }
void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_legend_texture(bool enable)
@ -1654,7 +1656,7 @@ void GLCanvas3D::render()
#endif // !ENABLE_SVG_ICONS #endif // !ENABLE_SVG_ICONS
_render_toolbar(); _render_toolbar();
_render_view_toolbar(); _render_view_toolbar();
if (m_layers_editing.last_object_id >= 0) if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
m_layers_editing.render_overlay(*this); m_layers_editing.render_overlay(*this);
wxGetApp().imgui()->render(); wxGetApp().imgui()->render();
@ -2254,6 +2256,22 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */ #endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
break; break;
#ifdef __APPLE__
case 'c':
case 'C':
#else /* __APPLE__ */
case WXK_CONTROL_C:
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
break;
#ifdef __APPLE__
case 'v':
case 'V':
#else /* __APPLE__ */
case WXK_CONTROL_V:
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE));
break;
#ifdef __APPLE__ #ifdef __APPLE__
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
#else /* __APPLE__ */ #else /* __APPLE__ */
@ -2372,6 +2390,10 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
} }
} }
// Inform gizmos about the event so they have the opportunity to react.
if (m_gizmos.on_mouse_wheel(evt, *this))
return;
// Calculate the zoom delta and apply it to the current zoom factor // Calculate the zoom delta and apply it to the current zoom factor
float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
set_camera_zoom(zoom); set_camera_zoom(zoom);
@ -2596,7 +2618,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_selection.remove(m_hover_volume_id); m_selection.remove(m_hover_volume_id);
else else
{ {
m_selection.add(m_hover_volume_id, !ctrl_down); m_selection.add(m_hover_volume_id, !ctrl_down, true);
m_mouse.drag.move_requires_threshold = !already_selected; m_mouse.drag.move_requires_threshold = !already_selected;
if (already_selected) if (already_selected)
m_mouse.set_move_start_threshold_position_2D_as_invalid(); m_mouse.set_move_start_threshold_position_2D_as_invalid();
@ -3676,8 +3698,8 @@ void GLCanvas3D::_render_objects() const
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
m_shader.start_using(); m_shader.start_using();
if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) { 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; int object_id = m_layers_editing.last_object_id;
m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume &volume) { m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume &volume) {
// Which volume to paint without the layer height profile shader? // Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);

View File

@ -34,7 +34,7 @@
#include "../Utils/PresetUpdater.hpp" #include "../Utils/PresetUpdater.hpp"
#include "../Utils/PrintHost.hpp" #include "../Utils/PrintHost.hpp"
#include "ConfigWizard_private.hpp" #include "ConfigWizard.hpp"
#include "slic3r/Config/Snapshot.hpp" #include "slic3r/Config/Snapshot.hpp"
#include "ConfigSnapshotDialog.hpp" #include "ConfigSnapshotDialog.hpp"
#include "FirmwareDialog.hpp" #include "FirmwareDialog.hpp"
@ -149,8 +149,8 @@ bool GUI_App::on_init_inner()
wxCHECK_MSG(wxDirExists(resources_dir), false, wxCHECK_MSG(wxDirExists(resources_dir), false,
wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir)); wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
SetAppName("Slic3rPE-beta"); SetAppName(SLIC3R_APP_KEY "-beta");
SetAppDisplayName("Slic3r Prusa Edition"); SetAppDisplayName(SLIC3R_APP_NAME);
// Enable this to get the default Win32 COMCTRL32 behavior of static boxes. // Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
// wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0); // wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0);
@ -230,7 +230,7 @@ bool GUI_App::on_init_inner()
// and after MainFrame is created & shown. // and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way // The extra CallAfter() is needed because of Mac, where this is the only way
// to popup a modal dialog on start without screwing combo boxes. // to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found not better way to do it. // This is ugly but I honestly found no better way to do it.
// Neither wxShowEvent nor wxWindowCreateEvent work reliably. // Neither wxShowEvent nor wxWindowCreateEvent work reliably.
static bool once = true; static bool once = true;
if (once) { if (once) {
@ -381,7 +381,7 @@ void GUI_App::recreate_GUI()
topwindow->Destroy(); topwindow->Destroy();
} }
dlg.Update(80, _(L("Loading of a current presets")) + dots); dlg.Update(80, _(L("Loading of current presets")) + dots);
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
@ -513,7 +513,7 @@ bool GUI_App::select_language( wxArrayString & names,
m_wxLocale = new wxLocale; m_wxLocale = new wxLocale;
m_wxLocale->Init(identifiers[index]); m_wxLocale->Init(identifiers[index]);
m_wxLocale->AddCatalogLookupPathPrefix(from_u8(localization_dir())); m_wxLocale->AddCatalogLookupPathPrefix(from_u8(localization_dir()));
m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); m_wxLocale->AddCatalog("Slic3rPE");
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
wxSetlocale(LC_NUMERIC, "C"); wxSetlocale(LC_NUMERIC, "C");
Preset::update_suffix_modified(); Preset::update_suffix_modified();
@ -542,7 +542,7 @@ bool GUI_App::load_language()
m_wxLocale = new wxLocale; m_wxLocale = new wxLocale;
m_wxLocale->Init(identifiers[i]); m_wxLocale->Init(identifiers[i]);
m_wxLocale->AddCatalogLookupPathPrefix(from_u8(localization_dir())); m_wxLocale->AddCatalogLookupPathPrefix(from_u8(localization_dir()));
m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); m_wxLocale->AddCatalog("Slic3rPE");
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
wxSetlocale(LC_NUMERIC, "C"); wxSetlocale(LC_NUMERIC, "C");
Preset::update_suffix_modified(); Preset::update_suffix_modified();
@ -586,9 +586,7 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident
if (langinfo != NULL) if (langinfo != NULL)
{ {
auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() +
filename + wxFileName::GetPathSeparator() + filename + wxFileName::GetPathSeparator() + "Slic3rPE" + wxT(".mo");
/*GetAppName()*/"Slic3rPE" +
wxT(".mo");
if (wxFileExists(full_file_name)) if (wxFileExists(full_file_name))
{ {
names.Add(langinfo->Description); names.Add(langinfo->Description);

View File

@ -543,12 +543,13 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
for (const size_t object : object_idxs) for (const size_t object : object_idxs)
{ {
add_object_to_list(object); add_object_to_list(object);
m_parts_changed = true;
parts_changed(object);
items.Add(m_objects_model->GetItemById(object)); items.Add(m_objects_model->GetItemById(object));
} }
m_parts_changed = true;
wxGetApp().plater()->changed_objects(object_idxs);
m_parts_changed = false;
select_items(items); select_items(items);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME #ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed(); selection_changed();
@ -644,6 +645,10 @@ void ObjectList::key_event(wxKeyEvent& event)
} }
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
select_item_all_children(); select_item_all_children();
else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
else else
event.Skip(); event.Skip();
} }

View File

@ -16,7 +16,7 @@
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
const float GLGizmoBase::Grabber::SizeFactor = 0.025f; const float GLGizmoBase::Grabber::SizeFactor = 0.05f;
const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
@ -53,7 +53,7 @@ float GLGizmoBase::Grabber::get_half_size(float size) const
float GLGizmoBase::Grabber::get_dragging_half_size(float size) const float GLGizmoBase::Grabber::get_dragging_half_size(float size) const
{ {
return std::max(size * SizeFactor * DraggingScaleFactor, MinHalfSize); return get_half_size(size) * DraggingScaleFactor;
} }
void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const void GLGizmoBase::Grabber::render(float size, const float* render_color, bool use_lighting) const

View File

@ -150,7 +150,8 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
glsafe(::glEnd()); glsafe(::glEnd());
// draw grabber // draw grabber
m_grabbers[m_hover_id].render(true, box.max_size()); float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
m_grabbers[m_hover_id].render(true, mean_size);
render_grabber_extension((Axis)m_hover_id, box, false); render_grabber_extension((Axis)m_hover_id, box, false);
} }
} }

View File

@ -51,6 +51,9 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
return; return;
} }
if (m_model_object != model_object)
m_print_object_idx = -1;
m_model_object = model_object; m_model_object = model_object;
m_active_instance = selection.get_instance_idx(); m_active_instance = selection.get_instance_idx();
@ -89,54 +92,113 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
// we'll recover current look direction from the modelview matrix (in world coords):
Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
if (m_quadric != nullptr && selection.is_from_single_instance()) if (m_quadric != nullptr && selection.is_from_single_instance())
render_points(selection, direction_to_camera, false); render_points(selection, false);
render_selection_rectangle(); render_selection_rectangle();
render_clipping_plane(selection, direction_to_camera); render_clipping_plane(selection);
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
{ {
if (m_clipping_plane_distance == 0.f) if (m_clipping_plane_distance == 0.f)
return; return;
if (m_clipping_plane_normal == Vec3d::Zero())
reset_clipping_plane_normal();
const Vec3d& direction_to_camera = m_clipping_plane_normal;
// First cache instance transformation to be used later.
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast<float>(); Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast<float>();
Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast<float>(); Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast<float>();
Vec3f scaling = vol->get_instance_scaling_factor().cast<float>(); Vec3f scaling = vol->get_instance_scaling_factor().cast<float>();
Vec3d instance_offset = vol->get_instance_offset();
// Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast<float>(); Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast<float>();
Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2)); Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm()); float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm());
if (m_clipping_plane_distance != m_old_clipping_plane_distance // Get transformation of the supports and calculate how far from its origin the clipping plane is.
|| m_old_direction_to_camera != direction_to_camera) { Transform3d supports_trafo = Transform3d::Identity();
supports_trafo = supports_trafo.rotate(Eigen::AngleAxisd(vol->get_instance_rotation()(2), Vec3d::UnitZ()));
Vec3f up_supports = (supports_trafo.inverse() * direction_to_camera).cast<float>();
supports_trafo = supports_trafo.pretranslate(Vec3d(instance_offset(0), instance_offset(1), vol->get_sla_shift_z()));
// Instance and supports origin do not coincide, so the following is quite messy:
float height_supports = height_mesh * (up.norm() / up_supports.norm()) + instance_offset(2) * (direction_to_camera(2) / direction_to_camera.norm());
std::vector<ExPolygons> list_of_expolys; // In case either of these was recently changed, the cached triangulated ExPolygons are invalid now.
// We are gonna recalculate them both for the object and for the support structures.
if (m_clipping_plane_distance != m_old_clipping_plane_distance
|| m_old_clipping_plane_normal != direction_to_camera) {
m_old_clipping_plane_normal = direction_to_camera;
m_old_clipping_plane_distance = m_clipping_plane_distance;
// Now initialize the TMS for the object, perform the cut and save the result.
if (! m_tms) { if (! m_tms) {
m_tms.reset(new TriangleMeshSlicer); m_tms.reset(new TriangleMeshSlicer);
m_tms->init(const_cast<TriangleMesh*>(&m_mesh), [](){}); m_tms->init(m_mesh, [](){});
} }
std::vector<ExPolygons> list_of_expolys;
m_tms->set_up_direction(up); m_tms->set_up_direction(up);
m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){}); m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){});
m_triangles = triangulate_expolygons_2f(list_of_expolys[0]); m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
m_old_direction_to_camera = direction_to_camera;
m_old_clipping_plane_distance = m_clipping_plane_distance;
// Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
// First we need a pointer to the respective SLAPrintObject. The index into objects vector is
// cached so we don't have todo it on each render. We only search for the po if needed:
if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) {
m_print_objects_count = m_parent.sla_print()->objects().size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
++m_print_object_idx;
if (po->model_object()->id() == m_model_object->id())
break;
}
}
if (m_print_object_idx >= 0) {
const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx];
if (print_object->is_step_done(slaposSupportTree)) {
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (!m_supports_tms || (int)timestamp != m_old_timestamp) {
// The timestamp has changed - stash the mesh and initialize the TMS.
m_supports_mesh = &print_object->support_mesh();
m_supports_tms.reset(new TriangleMeshSlicer);
// The mesh should already have the shared vertices calculated.
m_supports_tms->init(m_supports_mesh, [](){});
m_old_timestamp = timestamp;
}
// The TMS is initialized - let's do the cutting:
list_of_expolys.clear();
m_supports_tms->set_up_direction(up_supports);
m_supports_tms->slice(std::vector<float>{height_supports}, 0.f, &list_of_expolys, [](){});
m_supports_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
}
else {
// The supports are not valid. We better dump the cached data.
m_supports_tms.reset();
m_supports_triangles.clear();
}
}
} }
// At this point we have the triangulated cuts for both the object and supports - let's render.
if (! m_triangles.empty()) { if (! m_triangles.empty()) {
::glPushMatrix(); ::glPushMatrix();
::glTranslated(0.0, 0.0, m_z_shift); ::glTranslated(0.0, 0.0, m_z_shift);
@ -145,12 +207,30 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const
q.setFromTwoVectors(Vec3f::UnitZ(), up); q.setFromTwoVectors(Vec3f::UnitZ(), up);
Eigen::AngleAxisf aa(q); Eigen::AngleAxisf aa(q);
::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)); ::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane ::glTranslatef(0.f, 0.f, 0.01f); // to make sure the cut does not intersect the structure itself
::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
::glColor3f(1.0f, 0.37f, 0.0f); ::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
for (const Vec2f& point : m_triangles) for (const Vec2f& point : m_triangles)
::glVertex3f(point(0), point(1), height_mesh); ::glVertex3f(point(0), point(1), height_mesh);
::glEnd();
::glPopMatrix();
}
if (! m_supports_triangles.empty() && !m_editing_mode) {
// The supports are hidden in the editing mode, so it makes no sense to render the cuts.
::glPushMatrix();
::glMultMatrixd(supports_trafo.data());
Eigen::Quaternionf q;
q.setFromTwoVectors(Vec3f::UnitZ(), up_supports);
Eigen::AngleAxisf aa(q);
::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
::glTranslatef(0.f, 0.f, 0.01f);
::glColor3f(1.0f, 0.f, 0.37f);
::glBegin(GL_TRIANGLES);
for (const Vec2f& point : m_supports_triangles)
::glVertex3f(point(0), point(1), height_supports);
::glEnd(); ::glEnd();
::glPopMatrix(); ::glPopMatrix();
} }
@ -206,16 +286,10 @@ void GLGizmoSlaSupports::render_selection_rectangle() const
void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
{ {
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
// we'll recover current look direction from the modelview matrix (in world coords):
Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
render_points(selection, direction_to_camera, true);
} }
void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking) const void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const
{ {
if (!picking) if (!picking)
glsafe(::glEnable(GL_LIGHTING)); glsafe(::glEnable(GL_LIGHTING));
@ -234,7 +308,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d&
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point; const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
const bool& point_selected = m_editing_mode_cache[i].selected; const bool& point_selected = m_editing_mode_cache[i].selected;
if (is_point_clipped(support_point.pos.cast<double>(), direction_to_camera)) if (is_point_clipped(support_point.pos.cast<double>()))
continue; continue;
// First decide about the color of the point. // First decide about the color of the point.
@ -308,8 +382,10 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d&
bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
{ {
const Vec3d& direction_to_camera = m_clipping_plane_normal;
if (m_clipping_plane_distance == 0.f) if (m_clipping_plane_distance == 0.f)
return false; return false;
@ -332,9 +408,12 @@ void GLGizmoSlaSupports::update_mesh()
wxBusyCursor wait; wxBusyCursor wait;
Eigen::MatrixXf& V = m_V; Eigen::MatrixXf& V = m_V;
Eigen::MatrixXi& F = m_F; Eigen::MatrixXi& F = m_F;
// We rely on SLA model object having a single volume,
// this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset. // This mesh does not account for the possible Z up SLA offset.
m_mesh = m_model_object->raw_mesh(); m_mesh = &m_model_object->volumes.front()->mesh;
const stl_file& stl = m_mesh.stl; const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this
const stl_file& stl = m_mesh->stl;
V.resize(3 * stl.stats.number_of_facets, 3); V.resize(3 * stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) { for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
@ -376,9 +455,6 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
// we'll recover current look direction from the modelview matrix (in world coords):
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
point1(2) -= m_z_shift; point1(2) -= m_z_shift;
point2(2) -= m_z_shift; point2(2) -= m_z_shift;
@ -405,7 +481,7 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>(), direction_to_camera)) if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>()))
break; break;
} }
@ -510,7 +586,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z); ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
out_y = m_canvas_height - out_y; out_y = m_canvas_height - out_y;
if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast<double>(), direction_to_camera.cast<double>())) { if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast<double>())) {
bool is_obscured = false; bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh: // Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<igl::Hit> hits; std::vector<igl::Hit> hits;
@ -534,7 +610,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
if (is_point_clipped(hit_pos.cast<double>(), direction_to_camera.cast<double>())) { if (is_point_clipped(hit_pos.cast<double>())) {
hits.erase(hits.begin()+j); hits.erase(hits.begin()+j);
--j; --j;
} }
@ -626,6 +702,23 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
} }
} }
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f);
m_parent.set_as_dirty();
return true;
}
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f);
m_parent.set_as_dirty();
return true;
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
reset_clipping_plane_normal();
return true;
}
return false; return false;
} }
@ -709,11 +802,12 @@ ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
if (!m_model_object || m_state == Off) if (!m_model_object || m_state == Off)
return ClippingPlane::ClipsNothing(); return ClippingPlane::ClipsNothing();
Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> modelview_matrix; //Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> modelview_matrix;
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); //::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
// we'll recover current look direction from the modelview matrix (in world coords): // we'll recover current look direction from the modelview matrix (in world coords):
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]); //Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
const Vec3d& direction_to_camera = m_clipping_plane_normal;
float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)); float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift));
return ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); return ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius));
@ -872,18 +966,27 @@ RENDER_AGAIN:
m_imgui->text(""); m_imgui->text("");
m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? "No points (will be autogenerated)" : m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? _(L("No points (will be autogenerated)")) :
(m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? "Autogenerated points (no modifications)" : (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) :
(m_model_object->sla_points_status == sla::PointsStatus::UserModified ? "User-modified points" : (m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) :
(m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS")))); (m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS"))));
} }
// Following is rendered in both editing and non-editing mode: // Following is rendered in both editing and non-editing mode:
m_imgui->text("Clipping of view: "); if (m_clipping_plane_distance == 0.f)
ImGui::SameLine(); m_imgui->text("Clipping of view: ");
else {
if (m_imgui->button(_(L("Reset direction [R] ")))) {
wxGetApp().CallAfter([this](){
reset_clipping_plane_normal();
});
}
}
ImGui::SameLine(140.f);
ImGui::PushItemWidth(150.0f); ImGui::PushItemWidth(150.0f);
bool value_changed = ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f"); ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f");
m_imgui->end(); m_imgui->end();
@ -972,12 +1075,12 @@ void GLGizmoSlaSupports::on_set_state()
m_editing_mode = false; // so it is not active next time the gizmo opens m_editing_mode = false; // so it is not active next time the gizmo opens
m_editing_mode_cache.clear(); m_editing_mode_cache.clear();
m_clipping_plane_distance = 0.f; m_clipping_plane_distance = 0.f;
// Release copy of the mesh, triangle slicer and the AABB spatial search structure. // Release triangle mesh slicer and the AABB spatial search structure.
m_mesh.clear();
m_AABB.deinit(); m_AABB.deinit();
m_V = Eigen::MatrixXf(); m_V = Eigen::MatrixXf();
m_F = Eigen::MatrixXi(); m_F = Eigen::MatrixXi();
m_tms.reset(nullptr); m_tms.reset();
m_supports_tms.reset();
}); });
} }
m_old_state = m_state; m_old_state = m_state;
@ -1126,5 +1229,17 @@ void GLGizmoSlaSupports::switch_to_editing_mode()
m_editing_mode = true; m_editing_mode = true;
} }
void GLGizmoSlaSupports::reset_clipping_plane_normal() const
{
Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
m_clipping_plane_normal = Vec3d(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
m_parent.set_as_dirty();
}
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -36,8 +36,13 @@ private:
Eigen::MatrixXf m_V; // vertices Eigen::MatrixXf m_V; // vertices
Eigen::MatrixXi m_F; // facets indices Eigen::MatrixXi m_F; // facets indices
igl::AABB<Eigen::MatrixXf,3> m_AABB; igl::AABB<Eigen::MatrixXf,3> m_AABB;
TriangleMesh m_mesh; const TriangleMesh* m_mesh;
mutable const TriangleMesh* m_supports_mesh;
mutable std::vector<Vec2f> m_triangles; mutable std::vector<Vec2f> m_triangles;
mutable std::vector<Vec2f> m_supports_triangles;
mutable int m_old_timestamp = -1;
mutable int m_print_object_idx = -1;
mutable int m_print_objects_count = -1;
class CacheEntry { class CacheEntry {
public: public:
@ -68,8 +73,8 @@ private:
virtual void on_render_for_picking(const Selection& selection) const; virtual void on_render_for_picking(const Selection& selection) const;
void render_selection_rectangle() const; void render_selection_rectangle() const;
void render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking = false) const; void render_points(const Selection& selection, bool picking = false) const;
void render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const; void render_clipping_plane(const Selection& selection) const;
bool is_mesh_update_necessary() const; bool is_mesh_update_necessary() const;
void update_mesh(); void update_mesh();
void update_cache_entry_normal(unsigned int i) const; void update_cache_entry_normal(unsigned int i) const;
@ -79,11 +84,11 @@ private:
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter; // Size of a new point. float m_new_point_head_diameter; // Size of a new point.
float m_minimal_point_distance = 20.f; float m_minimal_point_distance = 20.f;
float m_density = 100.f;
mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected
float m_clipping_plane_distance = 0.f; float m_clipping_plane_distance = 0.f;
mutable float m_old_clipping_plane_distance = 0.f; mutable float m_old_clipping_plane_distance = 0.f;
mutable Vec3d m_old_direction_to_camera; mutable Vec3d m_old_clipping_plane_normal;
mutable Vec3d m_clipping_plane_normal = Vec3d::Zero();
enum SelectionRectangleStatus { enum SelectionRectangleStatus {
srOff = 0, srOff = 0,
@ -101,10 +106,11 @@ private:
int m_canvas_height; int m_canvas_height;
mutable std::unique_ptr<TriangleMeshSlicer> m_tms; mutable std::unique_ptr<TriangleMeshSlicer> m_tms;
mutable std::unique_ptr<TriangleMeshSlicer> m_supports_tms;
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const; std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const; bool is_point_clipped(const Vec3d& point) const;
void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const; //void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
// Methods that do the model_object and editing cache synchronization, // Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc: // editing mode selection, etc:
@ -120,6 +126,7 @@ private:
void get_data_from_backend(); void get_data_from_backend();
void auto_generate(); void auto_generate();
void switch_to_editing_mode(); void switch_to_editing_mode();
void reset_clipping_plane_normal() const;
protected: protected:
void on_set_state() override; void on_set_state() override;

View File

@ -14,7 +14,10 @@ enum class SLAGizmoEventType {
ApplyChanges, ApplyChanges,
DiscardChanges, DiscardChanges,
AutomaticGeneration, AutomaticGeneration,
ManualEditing ManualEditing,
MouseWheelUp,
MouseWheelDown,
ResetClippingPlane
}; };
#include "slic3r/GUI/Gizmos/GLGizmoMove.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMove.hpp"

View File

@ -520,6 +520,23 @@ void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection&
glsafe(::glPopMatrix()); glsafe(::glPopMatrix());
} }
bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas)
{
bool processed = false;
if (m_current == SlaSupports) {
float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
processed = true;
}
return processed;
}
bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
{ {
Point pos(evt.GetX(), evt.GetY()); Point pos(evt.GetX(), evt.GetY());
@ -761,6 +778,16 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
break; break;
} }
case 'r' :
case 'R' :
{
if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane))
processed = true;
break;
}
#ifdef __APPLE__ #ifdef __APPLE__
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
#else /* __APPLE__ */ #else /* __APPLE__ */
@ -794,7 +821,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
} }
} }
if (!processed) if (!processed && !evt.HasModifiers())
{ {
if (handle_shortcut(keyCode, canvas.get_selection())) if (handle_shortcut(keyCode, canvas.get_selection()))
{ {

View File

@ -157,6 +157,7 @@ public:
const std::string& get_tooltip() const { return m_tooltip; } const std::string& get_tooltip() const { return m_tooltip; }
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas); bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas);
bool on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas);
bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas); bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas);
bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas); bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas);

View File

@ -10,7 +10,7 @@ namespace Slic3r {
namespace GUI { namespace GUI {
KBShortcutsDialog::KBShortcutsDialog() KBShortcutsDialog::KBShortcutsDialog()
: DPIDialog(NULL, wxID_ANY, _(L("Slic3r Prusa Edition - Keyboard Shortcuts")), : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{ {
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@ -136,6 +136,8 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects"))); plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects")));
plater_shortcuts.push_back(Shortcut("Del", L("Delete selected"))); plater_shortcuts.push_back(Shortcut("Del", L("Delete selected")));
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All"))); plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All")));
plater_shortcuts.push_back(Shortcut(ctrl+"C", L("Copy to clipboard")));
plater_shortcuts.push_back(Shortcut(ctrl+"V", L("Paste from clipboard")));
plater_shortcuts.push_back(Shortcut("M", L("Gizmo move"))); plater_shortcuts.push_back(Shortcut("M", L("Gizmo move")));
plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale"))); plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale")));
plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate"))); plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate")));
@ -177,8 +179,8 @@ void KBShortcutsDialog::fill_shortcuts()
Shortcuts layers_slider_shortcuts; Shortcuts layers_slider_shortcuts;
layers_slider_shortcuts.reserve(6); layers_slider_shortcuts.reserve(6);
layers_slider_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Move current slider thump Up"))); layers_slider_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Move current slider thumb Up")));
layers_slider_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Move current slider thump Down"))); layers_slider_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Move current slider thumb Down")));
layers_slider_shortcuts.push_back(Shortcut(L("Arrow Left"), L("Set upper thumb to current slider thumb"))); layers_slider_shortcuts.push_back(Shortcut(L("Arrow Left"), L("Set upper thumb to current slider thumb")));
layers_slider_shortcuts.push_back(Shortcut(L("Arrow Right"),L("Set lower thumb to current slider thumb"))); layers_slider_shortcuts.push_back(Shortcut(L("Arrow Right"),L("Set lower thumb to current slider thumb")));
layers_slider_shortcuts.push_back(Shortcut("+", L("Add color change marker for current layer"))); layers_slider_shortcuts.push_back(Shortcut("+", L("Add color change marker for current layer")));

View File

@ -403,9 +403,9 @@ void MainFrame::init_menubar()
editMenu->AppendSeparator(); editMenu->AppendSeparator();
wxMenuItem* item_copy = append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + "\tCtrl+C", _(L("Copy selection to clipboard")), wxMenuItem* item_copy = 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"); [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, "copy_menu");
wxMenuItem* item_paste = append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + "\tCtrl+V", _(L("Paste clipboard")), wxMenuItem* item_paste = 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"); [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "paste_menu");
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
@ -505,9 +505,9 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { wxGetApp().system_info(); }); [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(); }); [this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
append_menu_item(helpMenu, wxID_ANY, _(L("Report an I&ssue")), _(L("Report an issue on the Slic3r Prusa Edition")), 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("http://github.com/prusa3d/slic3r/issues/new"); }); [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); });
append_menu_item(helpMenu, wxID_ANY, _(L("&About Slic3r")), _(L("Show about dialog")), append_menu_item(helpMenu, wxID_ANY, wxString::Format(_(L("&About %s")), SLIC3R_APP_NAME), _(L("Show about dialog")),
[this](wxCommandEvent&) { Slic3r::GUI::about(); }); [this](wxCommandEvent&) { Slic3r::GUI::about(); });
helpMenu->AppendSeparator(); 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")),
@ -776,7 +776,7 @@ void MainFrame::export_configbundle()
// Ask user for a file name. // Ask user for a file name.
auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")), auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"Slic3r_config_bundle.ini", SLIC3R_APP_KEY "_config_bundle.ini",
file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString file; wxString file;
if (dlg->ShowModal() == wxID_OK) if (dlg->ShowModal() == wxID_OK)

View File

@ -280,7 +280,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
cfg.set_key_value("extruder_colour", colors); cfg.set_key_value("extruder_colour", colors);
wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg); wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg);
wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this); wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this, wxGetApp().em_unit());
wxGetApp().plater()->on_config_change(cfg); wxGetApp().plater()->on_config_change(cfg);
} }
dialog->Destroy(); dialog->Destroy();
@ -813,7 +813,7 @@ void Sidebar::update_all_preset_comboboxes()
// update the dirty flags. // update the dirty flags.
if (print_tech == ptFFF) { if (print_tech == ptFFF) {
for (size_t i = 0; i < p->combos_filament.size(); ++i) for (size_t i = 0; i < p->combos_filament.size(); ++i)
preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); preset_bundle.update_platter_filament_ui(i, p->combos_filament[i], wxGetApp().em_unit());
} }
p->show_preset_comboboxes(); p->show_preset_comboboxes();
} }
@ -837,7 +837,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
} }
for (size_t i = 0; i < filament_cnt; i++) { for (size_t i = 0; i < filament_cnt; i++) {
preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); preset_bundle.update_platter_filament_ui(i, p->combos_filament[i], wxGetApp().em_unit());
} }
break; break;
@ -872,7 +872,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
// // update the dirty flags. // // update the dirty flags.
// if (print_tech == ptFFF) { // if (print_tech == ptFFF) {
// for (size_t i = 0; i < p->combos_filament.size(); ++ i) // for (size_t i = 0; i < p->combos_filament.size(); ++ i)
// preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]); // preset_bundle.update_platter_filament_ui(i, p->combos_filament[i], wxGetApp().em_unit());
// } // }
// p->show_preset_comboboxes(); // p->show_preset_comboboxes();
update_all_preset_comboboxes(); update_all_preset_comboboxes();
@ -1239,8 +1239,8 @@ struct Plater::priv
wxString project_filename; wxString project_filename;
BackgroundSlicingProcess background_process; BackgroundSlicingProcess background_process;
std::atomic<bool> arranging; bool arranging;
std::atomic<bool> rotoptimizing; bool rotoptimizing;
bool delayed_scene_refresh; bool delayed_scene_refresh;
std::string delayed_error_message; std::string delayed_error_message;
@ -1403,8 +1403,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, view_toolbar(GLToolbar::Radio) , view_toolbar(GLToolbar::Radio)
#endif // ENABLE_SVG_ICONS #endif // ENABLE_SVG_ICONS
{ {
arranging.store(false); arranging = false;
rotoptimizing.store(false); rotoptimizing = false;
background_process.set_fff_print(&fff_print); background_process.set_fff_print(&fff_print);
background_process.set_sla_print(&sla_print); background_process.set_sla_print(&sla_print);
background_process.set_gcode_preview_data(&gcode_preview_data); background_process.set_gcode_preview_data(&gcode_preview_data);
@ -1703,6 +1703,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
break; break;
} }
// is there any advanced config data ?
auto opt_keys = model_object->config.keys();
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
{
advanced = true;
break;
}
// is there any modifier ? // is there any modifier ?
for (const ModelVolume* model_volume : model_object->volumes) for (const ModelVolume* model_volume : model_object->volumes)
{ {
@ -1711,6 +1719,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
advanced = true; advanced = true;
break; break;
} }
// is there any advanced config data ?
opt_keys = model_volume->config.keys();
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
{
advanced = true;
break;
}
} }
if (advanced) if (advanced)
@ -2079,15 +2095,14 @@ void Plater::priv::mirror(Axis axis)
void Plater::priv::arrange() void Plater::priv::arrange()
{ {
// don't do anything if currently arranging. Then this is a re-entrance if (arranging) { return; }
if(arranging.load()) return; arranging = true;
Slic3r::ScopeGuard arranging_guard([this]() { arranging = false; });
// Guard the arrange process
arranging.store(true);
wxBusyCursor wait; wxBusyCursor wait;
this->background_process.stop(); this->background_process.stop();
unsigned count = 0; unsigned count = 0;
for(auto obj : model.objects) count += obj->instances.size(); for(auto obj : model.objects) count += obj->instances.size();
@ -2103,14 +2118,14 @@ void Plater::priv::arrange()
statusbar()->set_progress(count - st); statusbar()->set_progress(count - st);
statusbar()->set_status_text(msg); statusbar()->set_status_text(msg);
// ok, this is dangerous, but we are protected by the atomic flag // ok, this is dangerous, but we are protected by the flag
// 'arranging' and the arrange button is also disabled. // 'arranging' and the arrange button is also disabled.
// This call is needed for the cancel button to work. // This call is needed for the cancel button to work.
wxYieldIfNeeded(); wxYieldIfNeeded();
}; };
statusbar()->set_cancel_callback([this, statusfn](){ statusbar()->set_cancel_callback([this, statusfn](){
arranging.store(false); arranging = false;
statusfn(0, L("Arranging canceled")); statusfn(0, L("Arranging canceled"));
}); });
@ -2146,7 +2161,7 @@ void Plater::priv::arrange()
hint, hint,
false, // create many piles not just one pile false, // create many piles not just one pile
[statusfn](unsigned st) { statusfn(st, arrangestr); }, [statusfn](unsigned st) { statusfn(st, arrangestr); },
[this] () { return !arranging.load(); }); [this] () { return !arranging; });
} catch(std::exception& /*e*/) { } catch(std::exception& /*e*/) {
GUI::show_error(this->q, L("Could not arrange model objects! " GUI::show_error(this->q, L("Could not arrange model objects! "
"Some geometries may be invalid.")); "Some geometries may be invalid."));
@ -2155,7 +2170,6 @@ void Plater::priv::arrange()
statusfn(0, L("Arranging done.")); statusfn(0, L("Arranging done."));
statusbar()->set_range(prev_range); statusbar()->set_range(prev_range);
statusbar()->set_cancel_callback(); // remove cancel button statusbar()->set_cancel_callback(); // remove cancel button
arranging.store(false);
// Do a full refresh of scene tree, including regenerating all the GLVolumes. // Do a full refresh of scene tree, including regenerating all the GLVolumes.
//FIXME The update function shall just reload the modified matrices. //FIXME The update function shall just reload the modified matrices.
@ -2170,11 +2184,12 @@ void Plater::priv::sla_optimize_rotation() {
// running we should probably disable explicit slicing and background // running we should probably disable explicit slicing and background
// processing // processing
if(rotoptimizing.load()) return; if (rotoptimizing) { return; }
rotoptimizing.store(true); rotoptimizing = true;
Slic3r::ScopeGuard rotoptimizing_guard([this]() { rotoptimizing = false; });
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
if(obj_idx < 0) { rotoptimizing.store(false); return; } if (obj_idx < 0) { return; }
ModelObject * o = model.objects[size_t(obj_idx)]; ModelObject * o = model.objects[size_t(obj_idx)];
@ -2192,14 +2207,14 @@ void Plater::priv::sla_optimize_rotation() {
}; };
statusbar()->set_cancel_callback([this, stfn](){ statusbar()->set_cancel_callback([this, stfn](){
rotoptimizing.store(false); rotoptimizing = false;
stfn(0, L("Orientation search canceled")); stfn(0, L("Orientation search canceled"));
}); });
auto r = sla::find_best_rotation( auto r = sla::find_best_rotation(
*o, .005f, *o, .005f,
[stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); }, [stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); },
[this](){ return !rotoptimizing.load(); } [this](){ return !rotoptimizing; }
); );
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape"); const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
@ -2212,7 +2227,7 @@ void Plater::priv::sla_optimize_rotation() {
double mindist = 6.0; // FIXME double mindist = 6.0; // FIXME
double offs = mindist / 2.0 - EPSILON; double offs = mindist / 2.0 - EPSILON;
if(rotoptimizing.load()) // wasn't canceled if(rotoptimizing) // wasn't canceled
for(ModelInstance * oi : o->instances) { for(ModelInstance * oi : o->instances) {
oi->set_rotation({r[X], r[Y], r[Z]}); oi->set_rotation({r[X], r[Y], r[Z]});
@ -2262,7 +2277,6 @@ void Plater::priv::sla_optimize_rotation() {
stfn(0, L("Orientation found.")); stfn(0, L("Orientation found."));
statusbar()->set_range(prev_range); statusbar()->set_range(prev_range);
statusbar()->set_cancel_callback(); statusbar()->set_cancel_callback();
rotoptimizing.store(false);
update(true); update(true);
} }
@ -2440,6 +2454,11 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
bool Plater::priv::restart_background_process(unsigned int state) bool Plater::priv::restart_background_process(unsigned int state)
{ {
if (arranging || rotoptimizing) {
// Avoid a race condition
return false;
}
if ( ! this->background_process.empty() && if ( ! this->background_process.empty() &&
(state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 &&
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
@ -2650,7 +2669,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
// TODO: ? // TODO: ?
if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) {
// Only update the platter UI for the 2nd and other filaments. // Only update the platter UI for the 2nd and other filaments.
wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo); wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo, wxGetApp().em_unit());
} }
else { else {
wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
@ -2666,6 +2685,11 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
{ {
if (evt.status.percent >= -1) { if (evt.status.percent >= -1) {
if (arranging || rotoptimizing) {
// Avoid a race condition
return;
}
this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("")); this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8(""));
} }
@ -3123,12 +3147,20 @@ bool Plater::priv::can_delete_all() const
bool Plater::priv::can_increase_instances() const bool Plater::priv::can_increase_instances() const
{ {
if (arranging || rotoptimizing) {
return false;
}
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
} }
bool Plater::priv::can_decrease_instances() const bool Plater::priv::can_decrease_instances() const
{ {
if (arranging || rotoptimizing) {
return false;
}
int obj_idx = get_selected_object_idx(); int obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
} }
@ -3145,7 +3177,7 @@ bool Plater::priv::can_split_to_volumes() const
bool Plater::priv::can_arrange() const bool Plater::priv::can_arrange() const
{ {
return !model.objects.empty() && !arranging.load(); return !model.objects.empty() && !arranging;
} }
bool Plater::priv::can_layers_editing() const bool Plater::priv::can_layers_editing() const
@ -3286,9 +3318,9 @@ void Plater::remove_selected()
void Plater::increase_instances(size_t num) void Plater::increase_instances(size_t num)
{ {
if (! can_increase_instances()) { return; }
int obj_idx = p->get_selected_object_idx(); int obj_idx = p->get_selected_object_idx();
if (obj_idx == -1)
return;
ModelObject* model_object = p->model.objects[obj_idx]; ModelObject* model_object = p->model.objects[obj_idx];
ModelInstance* model_instance = model_object->instances.back(); ModelInstance* model_instance = model_object->instances.back();
@ -3320,9 +3352,9 @@ void Plater::increase_instances(size_t num)
void Plater::decrease_instances(size_t num) void Plater::decrease_instances(size_t num)
{ {
if (! can_decrease_instances()) { return; }
int obj_idx = p->get_selected_object_idx(); int obj_idx = p->get_selected_object_idx();
if (obj_idx == -1)
return;
ModelObject* model_object = p->model.objects[obj_idx]; ModelObject* model_object = p->model.objects[obj_idx];
if (model_object->instances.size() > num) { if (model_object->instances.size() > num) {
@ -3618,7 +3650,7 @@ void Plater::on_extruders_change(int num_extruders)
choices.push_back(choice); choices.push_back(choice);
// initialize selection // initialize selection
wxGetApp().preset_bundle->update_platter_filament_ui(i, choice); wxGetApp().preset_bundle->update_platter_filament_ui(i, choice, wxGetApp().em_unit());
++i; ++i;
} }
@ -3768,6 +3800,36 @@ void Plater::changed_object(int obj_idx)
this->p->schedule_background_process(); this->p->schedule_background_process();
} }
void Plater::changed_objects(const std::vector<size_t>& object_idxs)
{
if (object_idxs.empty())
return;
auto list = wxGetApp().obj_list();
wxASSERT(list != nullptr);
if (list == nullptr)
return;
if (list->is_parts_changed()) {
for (int obj_idx : object_idxs)
{
if (obj_idx < p->model.objects.size())
// recenter and re - align to Z = 0
p->model.objects[obj_idx]->ensure_on_bed();
}
if (this->p->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data, update the 3D scene.
this->p->update_restart_background_process(true, false);
}
else
p->view3D->reload_scene(false);
}
// update print
this->p->schedule_background_process();
}
void Plater::schedule_background_process() void Plater::schedule_background_process()
{ {
this->p->schedule_background_process(); this->p->schedule_background_process();

View File

@ -167,6 +167,7 @@ public:
void reslice(); void reslice();
void reslice_SLA_supports(const ModelObject &object); void reslice_SLA_supports(const ModelObject &object);
void changed_object(int obj_idx); void changed_object(int obj_idx);
void changed_objects(const std::vector<size_t>& object_idxs);
void schedule_background_process(); void schedule_background_process();
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode(); void send_gcode();

View File

@ -60,7 +60,7 @@ void PreferencesDialog::build()
// Please keep in sync with ConfigWizard // Please keep in sync with ConfigWizard
def.label = L("Check for application updates"); def.label = L("Check for application updates");
def.type = coBool; def.type = coBool;
def.tooltip = L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."); def.tooltip = L("If enabled, Slic3r checks for new versions of " SLIC3R_APP_NAME " online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.");
def.default_value = new ConfigOptionBool(app_config->get("version_check") == "1"); def.default_value = new ConfigOptionBool(app_config->get("version_check") == "1");
option = Option (def, "version_check"); option = Option (def, "version_check");
m_optgroup->append_single_option_line(option); m_optgroup->append_single_option_line(option);

View File

@ -1451,7 +1451,7 @@ void PresetBundle::load_default_preset_bitmaps(wxWindow *window)
this->load_compatible_bitmaps(window); this->load_compatible_bitmaps(window);
} }
void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui) void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui, const int em/* = 10*/)
{ {
if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA || if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA ||
this->filament_presets.size() <= idx_extruder ) this->filament_presets.size() <= idx_extruder )
@ -1476,6 +1476,18 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
wxString selected_str = ""; wxString selected_str = "";
if (!this->filaments().front().is_visible) if (!this->filaments().front().is_visible)
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap)); ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
* So set sizes for solid_colored icons used for filament preset
* and scale then in respect to em_unit value
*/
const float scale_f = em * 0.1f;
const int icon_height = 16 * scale_f + 0.5f;
const int normal_icon_width = 16 * scale_f + 0.5f;
const int space_icon_width = 2 * scale_f + 0.5f;
const int wide_icon_width = 24 * scale_f + 0.5f;
const int thin_icon_width = 8 * scale_f + 0.5f;
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) {
const Preset &preset = this->filaments.preset(i); const Preset &preset = this->filaments.preset(i);
bool selected = this->filament_presets[idx_extruder] == preset.name; bool selected = this->filament_presets[idx_extruder] == preset.name;
@ -1499,17 +1511,17 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
std::vector<wxBitmap> bmps; std::vector<wxBitmap> bmps;
if (wide_icons) if (wide_icons)
// Paint a red flag for incompatible presets. // Paint a red flag for incompatible presets.
bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(16, 16) : *m_bitmapIncompatible); bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(normal_icon_width, icon_height) : *m_bitmapIncompatible);
// Paint the color bars. // Paint the color bars.
parse_color(filament_rgb, rgb); parse_color(filament_rgb, rgb);
bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? 24 : 16, 16, rgb)); bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? wide_icon_width : normal_icon_width, icon_height, rgb));
if (! single_bar) { if (! single_bar) {
parse_color(extruder_rgb, rgb); parse_color(extruder_rgb, rgb);
bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb)); bmps.emplace_back(m_bitmapCache->mksolid(thin_icon_width, icon_height, rgb));
} }
// Paint a lock at the system presets. // Paint a lock at the system presets.
bmps.emplace_back(m_bitmapCache->mkclear(2, 16)); bmps.emplace_back(m_bitmapCache->mkclear(space_icon_width, icon_height));
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(16, 16)); bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(normal_icon_width, icon_height));
// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); // (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
bitmap = m_bitmapCache->insert(bitmap_key, bmps); bitmap = m_bitmapCache->insert(bitmap_key, bmps);
} }

View File

@ -107,7 +107,7 @@ public:
void export_configbundle(const std::string &path, bool export_system_settings = false); void export_configbundle(const std::string &path, bool export_system_settings = false);
// Update a filament selection combo box on the platter for an idx_extruder. // Update a filament selection combo box on the platter for an idx_extruder.
void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui); void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui, const int em = 10);
// Enable / disable the "- default -" preset. // Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed); void set_default_suppressed(bool default_suppressed);

View File

@ -99,7 +99,7 @@ void Selection::set_model(Model* model)
update_valid(); update_valid();
} }
void Selection::add(unsigned int volume_idx, bool as_single_selection) void Selection::add(unsigned int volume_idx, bool as_single_selection, bool check_for_already_contained)
{ {
if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx)) if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx))
return; return;
@ -110,7 +110,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection)
return; return;
bool keep_instance_mode = (m_mode == Instance) && !as_single_selection; bool keep_instance_mode = (m_mode == Instance) && !as_single_selection;
bool already_contained = contains_volume(volume_idx); bool already_contained = check_for_already_contained && contains_volume(volume_idx);
// resets the current list if needed // resets the current list if needed
bool needs_reset = as_single_selection && !already_contained; bool needs_reset = as_single_selection && !already_contained;

View File

@ -210,7 +210,7 @@ public:
EMode get_mode() const { return m_mode; } EMode get_mode() const { return m_mode; }
void set_mode(EMode mode) { m_mode = mode; } void set_mode(EMode mode) { m_mode = mode; }
void add(unsigned int volume_idx, bool as_single_selection = true); void add(unsigned int volume_idx, bool as_single_selection = true, bool check_for_already_contained = false);
void remove(unsigned int volume_idx); void remove(unsigned int volume_idx);
void add_object(unsigned int object_idx, bool as_single_selection = true); void add_object(unsigned int object_idx, bool as_single_selection = true);

View File

@ -22,7 +22,7 @@ std::string get_main_info(bool format_as_html)
std::string line_end = format_as_html ? "<br>" : "\n"; std::string line_end = format_as_html ? "<br>" : "\n";
if (!format_as_html) if (!format_as_html)
out << b_start << SLIC3R_FORK_NAME << b_end << line_end; out << b_start << SLIC3R_APP_NAME << b_end << line_end;
out << b_start << "Version: " << b_end << SLIC3R_VERSION << line_end; out << b_start << "Version: " << b_end << SLIC3R_VERSION << line_end;
out << b_start << "Build: " << b_end << SLIC3R_BUILD << line_end; out << b_start << "Build: " << b_end << SLIC3R_BUILD << line_end;
out << line_end; out << line_end;
@ -41,7 +41,7 @@ std::string get_main_info(bool format_as_html)
} }
SysInfoDialog::SysInfoDialog() SysInfoDialog::SysInfoDialog()
: DPIDialog(NULL, wxID_ANY, _(L("Slic3r Prusa Edition - System Information")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("System Information")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
{ {
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
SetBackgroundColour(bgr_clr); SetBackgroundColour(bgr_clr);
@ -64,7 +64,7 @@ SysInfoDialog::SysInfoDialog()
// title // title
{ {
wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_FORK_NAME, wxDefaultPosition, wxDefaultSize); wxStaticText* title = new wxStaticText(this, wxID_ANY, SLIC3R_APP_NAME, wxDefaultPosition, wxDefaultSize);
wxFont title_font = wxGetApp().bold_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont title_font = wxGetApp().bold_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
// title_font.SetWeight(wxFONTWEIGHT_BOLD); // title_font.SetWeight(wxFONTWEIGHT_BOLD);
title_font.SetFamily(wxFONTFAMILY_ROMAN); title_font.SetFamily(wxFONTFAMILY_ROMAN);

View File

@ -27,7 +27,7 @@ static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Slic
// MsgUpdateSlic3r // MsgUpdateSlic3r
MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) : MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) :
MsgDialog(nullptr, _(L("Update available")), _(L("New version of Slic3r PE is available"))), MsgDialog(nullptr, _(L("Update available")), wxString::Format(_(L("New version of %s is available")), SLIC3R_APP_NAME)),
ver_current(ver_current), ver_current(ver_current),
ver_online(ver_online) ver_online(ver_online)
{ {
@ -113,17 +113,17 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, w
{ {
logo->SetBitmap(create_scaled_bitmap(this, "Slic3r_192px_grayscale.png", 192)); logo->SetBitmap(create_scaled_bitmap(this, "Slic3r_192px_grayscale.png", 192));
auto *text = new wxStaticText(this, wxID_ANY, _(L( auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L(
"This version of Slic3r PE is not compatible with currently installed configuration bundles.\n" "This version of %s is not compatible with currently installed configuration bundles.\n"
"This probably happened as a result of running an older Slic3r PE after using a newer one.\n\n" "This probably happened as a result of running an older %s after using a newer one.\n\n"
"You may either exit Slic3r and try again with a newer version, or you may re-run the initial configuration. " "You may either exit Slic3r and try again with a newer version, or you may re-run the initial configuration. "
"Doing so will create a backup snapshot of the existing configuration before installing files compatible with this Slic3r.\n" "Doing so will create a backup snapshot of the existing configuration before installing files compatible with this Slic3r.\n"
))); )), SLIC3R_APP_NAME, SLIC3R_APP_NAME));
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
content_sizer->Add(text); content_sizer->Add(text);
auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION)); auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This %s version: %s")), SLIC3R_APP_NAME, SLIC3R_VERSION));
text2->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); text2->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
content_sizer->Add(text2); content_sizer->Add(text2);
content_sizer->AddSpacer(VERT_SPACING); content_sizer->AddSpacer(VERT_SPACING);
@ -168,7 +168,7 @@ MsgDataLegacy::MsgDataLegacy() :
{ {
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format( auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(
_(L( _(L(
"Slic3r PE now uses an updated configuration structure.\n\n" "%s now uses an updated configuration structure.\n\n"
"So called 'System presets' have been introduced, which hold the built-in default settings for various " "So called 'System presets' have been introduced, which hold the built-in default settings for various "
"printers. These System presets cannot be modified, instead, users now may create their " "printers. These System presets cannot be modified, instead, users now may create their "
@ -178,7 +178,7 @@ MsgDataLegacy::MsgDataLegacy() :
"Please proceed with the %s that follows to set up the new presets " "Please proceed with the %s that follows to set up the new presets "
"and to choose whether to enable automatic preset updates." "and to choose whether to enable automatic preset updates."
)), )),
ConfigWizard::name() SLIC3R_APP_NAME, ConfigWizard::name()
)); ));
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
content_sizer->Add(text); content_sizer->Add(text);

View File

@ -88,7 +88,7 @@ Http::priv::priv(const std::string &url)
set_timeout_connect(DEFAULT_TIMEOUT_CONNECT); set_timeout_connect(DEFAULT_TIMEOUT_CONNECT);
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_APP_NAME "/" SLIC3R_VERSION);
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
} }
@ -228,6 +228,7 @@ std::string Http::priv::body_size_error()
void Http::priv::http_perform() void Http::priv::http_perform()
{ {
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
::curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this)); ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb); ::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb);

View File

@ -208,7 +208,7 @@ void PresetUpdater::priv::sync_version() const
{ {
if (! enabled_version_check) { return; } if (! enabled_version_check) { return; }
BOOST_LOG_TRIVIAL(info) << boost::format("Downloading Slic3rPE online version from: `%1%`") % version_check_url; BOOST_LOG_TRIVIAL(info) << boost::format("Downloading %1% online version from: `%2%`") % SLIC3R_APP_NAME % version_check_url;
Http::get(version_check_url) Http::get(version_check_url)
.size_limit(SLIC3R_VERSION_BODY_MAX) .size_limit(SLIC3R_VERSION_BODY_MAX)
@ -224,7 +224,7 @@ void PresetUpdater::priv::sync_version() const
}) })
.on_complete([&](std::string body, unsigned /* http_status */) { .on_complete([&](std::string body, unsigned /* http_status */) {
boost::trim(body); boost::trim(body);
BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body; BOOST_LOG_TRIVIAL(info) << boost::format("Got %1% online version: `%2%`. Sending to GUI thread...") % SLIC3R_APP_NAME % body;
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE); wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
evt->SetString(GUI::from_u8(body)); evt->SetString(GUI::from_u8(body));

View File

@ -1,7 +1,8 @@
# Included by CMakeLists, edited by the build script # Included by CMakeLists, edited by the build script
# (the version numbers are generated by the build script from the git current label) # (the version numbers are generated by the build script from the git current label)
set(SLIC3R_FORK_NAME "Slic3r Prusa Edition") set(SLIC3R_APP_NAME "Slic3r Prusa Edition")
set(SLIC3R_APP_KEY "Slic3rPE")
set(SLIC3R_VERSION "1.42.0-beta2") set(SLIC3R_VERSION "1.42.0-beta2")
set(SLIC3R_BUILD "${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_BUILD "${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_BUILD_ID "${SLIC3R_BUILD_ID}") set(SLIC3R_BUILD_ID "${SLIC3R_BUILD_ID}")

View File

@ -180,6 +180,8 @@ SV*
TriangleMesh::slice(z) TriangleMesh::slice(z)
std::vector<double> z std::vector<double> z
CODE: CODE:
THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
// convert doubles to floats // convert doubles to floats
std::vector<float> z_f = cast<float>(z); std::vector<float> z_f = cast<float>(z);
@ -210,6 +212,7 @@ TriangleMesh::cut(z, upper, lower)
TriangleMesh* upper; TriangleMesh* upper;
TriangleMesh* lower; TriangleMesh* lower;
CODE: CODE:
THIS->require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer mslicer(THIS); TriangleMeshSlicer mslicer(THIS);
mslicer.cut(z, upper, lower); mslicer.cut(z, upper, lower);

View File

@ -32,7 +32,7 @@ DEBUG_OUT_PATH_PREFIX()
SV* SV*
FORK_NAME() FORK_NAME()
CODE: CODE:
RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); RETVAL = newSVpv(SLIC3R_APP_NAME, 0);
OUTPUT: RETVAL OUTPUT: RETVAL
void void