Merge branch 'master' into fs_emboss
This commit is contained in:
commit
24207403ea
@ -506,6 +506,12 @@ endif ()
|
|||||||
|
|
||||||
# Find the Cereal serialization library
|
# Find the Cereal serialization library
|
||||||
find_package(cereal REQUIRED)
|
find_package(cereal REQUIRED)
|
||||||
|
add_library(libcereal INTERFACE)
|
||||||
|
if (NOT TARGET cereal::cereal)
|
||||||
|
target_link_libraries(libcereal INTERFACE cereal)
|
||||||
|
else()
|
||||||
|
target_link_libraries(libcereal INTERFACE cereal::cereal)
|
||||||
|
endif()
|
||||||
|
|
||||||
# l10n
|
# l10n
|
||||||
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
|
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
|
||||||
|
4
deps/wxWidgets/wxWidgets.cmake
vendored
4
deps/wxWidgets/wxWidgets.cmake
vendored
@ -12,8 +12,8 @@ endif()
|
|||||||
prusaslicer_add_cmake_project(wxWidgets
|
prusaslicer_add_cmake_project(wxWidgets
|
||||||
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
|
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
|
||||||
# GIT_TAG tm_cross_compile #${_wx_git_tag}
|
# GIT_TAG tm_cross_compile #${_wx_git_tag}
|
||||||
URL https://github.com/prusa3d/wxWidgets/archive/73f029adfcc82fb3aa4b01220a013f716e57d110.zip
|
URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip
|
||||||
URL_HASH SHA256=c35fe0187db497b6a3f477e24ed5e307028657ff0c2554385810b6e7961ad2e4
|
URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b
|
||||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
|
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DwxBUILD_PRECOMP=ON
|
-DwxBUILD_PRECOMP=ON
|
||||||
|
@ -64,6 +64,13 @@ technology = FFF
|
|||||||
family = PREDATOR
|
family = PREDATOR
|
||||||
default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR
|
default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR
|
||||||
|
|
||||||
|
[printer_model:PHOTON MONO X]
|
||||||
|
name = Photon Mono X
|
||||||
|
variants = default
|
||||||
|
technology = SLA
|
||||||
|
family = PHOTON MONO
|
||||||
|
default_materials = Generic Blue Resin @MONO 0.05
|
||||||
|
|
||||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||||
# not make it into the user interface.
|
# not make it into the user interface.
|
||||||
|
|
||||||
@ -1898,3 +1905,94 @@ default_print_profile = 0.24mm 0.8 nozzle DETAILED QUALITY @PREDATOR
|
|||||||
#########################################
|
#########################################
|
||||||
########## end printer presets ##########
|
########## end printer presets ##########
|
||||||
#########################################
|
#########################################
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
########## SLA printer presets ##########
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
|
||||||
|
[sla_print:*common print ANYCUBIC SLA*]
|
||||||
|
compatible_printers_condition = family=="PHOTON MONO"
|
||||||
|
layer_height = 0.05
|
||||||
|
output_filename_format = [input_filename_base].pwmx
|
||||||
|
pad_edge_radius = 0.5
|
||||||
|
pad_enable = 0
|
||||||
|
pad_max_merge_distance = 50
|
||||||
|
pad_wall_height = 0
|
||||||
|
pad_wall_thickness = 1
|
||||||
|
pad_wall_slope = 45
|
||||||
|
faded_layers = 8
|
||||||
|
slice_closing_radius = 0.005
|
||||||
|
support_base_diameter = 3
|
||||||
|
support_base_height = 1
|
||||||
|
support_critical_angle = 45
|
||||||
|
support_density_at_45 = 250
|
||||||
|
support_density_at_horizontal = 500
|
||||||
|
support_head_front_diameter = 0.4
|
||||||
|
support_head_penetration = 0.4
|
||||||
|
support_head_width = 3
|
||||||
|
support_max_bridge_length = 10
|
||||||
|
support_minimal_z = 0
|
||||||
|
support_object_elevation = 5
|
||||||
|
support_pillar_diameter = 1
|
||||||
|
support_pillar_connection_mode = zigzag
|
||||||
|
support_pillar_widening_factor = 0
|
||||||
|
supports_enable = 1
|
||||||
|
support_small_pillar_diameter_percent = 60%
|
||||||
|
|
||||||
|
[sla_print:0.05 Normal @ANYCUBIC]
|
||||||
|
inherits = *common print ANYCUBIC SLA*
|
||||||
|
layer_height = 0.05
|
||||||
|
|
||||||
|
########### Materials
|
||||||
|
|
||||||
|
[sla_material:*common ANYCUBIC SLA*]
|
||||||
|
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/
|
||||||
|
compatible_prints_condition = layer_height == 0.05
|
||||||
|
exposure_time = 7
|
||||||
|
initial_exposure_time = 40
|
||||||
|
initial_layer_height = 0.05
|
||||||
|
material_correction = 1,1,1
|
||||||
|
material_notes = LIFT_DISTANCE=8.0\nLIFT_SPEED=2.5\nRETRACT_SPEED=3.0\nBOTTOM_LIFT_SPEED=2.0\nBOTTOM_LIFT_DISTANCE=9.0\nDELAY_BEFORE_EXPOSURE=0.5
|
||||||
|
|
||||||
|
[sla_material:*common 0.05 ANYCUBIC SLA*]
|
||||||
|
inherits = *common ANYCUBIC SLA*
|
||||||
|
|
||||||
|
[sla_material:Generic Blue Resin @MONO 0.05]
|
||||||
|
inherits = *common 0.05 ANYCUBIC SLA*
|
||||||
|
exposure_time = 2.5
|
||||||
|
initial_exposure_time = 40
|
||||||
|
material_type = Tough
|
||||||
|
material_vendor = Generic
|
||||||
|
material_colour = #6080EC
|
||||||
|
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/
|
||||||
|
|
||||||
|
########## Printers
|
||||||
|
|
||||||
|
[printer:Anycubic Photon Mono X]
|
||||||
|
printer_technology = SLA
|
||||||
|
printer_model = PHOTON MONO X
|
||||||
|
printer_variant = default
|
||||||
|
default_sla_material_profile = Generic Blue Resin @MONO 0.05
|
||||||
|
default_sla_print_profile = 0.05 Normal @ANYCUBIC
|
||||||
|
thumbnails = 224x168
|
||||||
|
sla_archive_format = pwmx
|
||||||
|
bed_shape = 1.48x1.02,193.48x1.02,193.48x121.02,1.48x121.02
|
||||||
|
display_height = 120
|
||||||
|
display_orientation = landscape
|
||||||
|
display_mirror_x = 1
|
||||||
|
display_mirror_y = 0
|
||||||
|
display_pixels_x = 3840
|
||||||
|
display_pixels_y = 2400
|
||||||
|
display_width = 192
|
||||||
|
max_print_height = 245
|
||||||
|
elefant_foot_compensation = 0.2
|
||||||
|
elefant_foot_min_width = 0.2
|
||||||
|
min_exposure_time = 1
|
||||||
|
max_exposure_time = 120
|
||||||
|
min_initial_exposure_time = 1
|
||||||
|
max_initial_exposure_time = 300
|
||||||
|
printer_correction = 1,1,1
|
||||||
|
gamma_correction = 1
|
||||||
|
area_fill = 45
|
||||||
|
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOX\n
|
||||||
|
BIN
resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png
Normal file
BIN
resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
@ -126,7 +126,7 @@ if (NOT WIN32 AND NOT APPLE)
|
|||||||
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
|
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(PrusaSlicer libslic3r cereal)
|
target_link_libraries(PrusaSlicer libslic3r libcereal)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# add_compile_options(-stdlib=libc++)
|
# add_compile_options(-stdlib=libc++)
|
||||||
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
||||||
|
@ -96,10 +96,14 @@ set(SLIC3R_SOURCES
|
|||||||
Format/objparser.hpp
|
Format/objparser.hpp
|
||||||
Format/STL.cpp
|
Format/STL.cpp
|
||||||
Format/STL.hpp
|
Format/STL.hpp
|
||||||
|
Format/SLAArchive.hpp
|
||||||
|
Format/SLAArchive.cpp
|
||||||
Format/SL1.hpp
|
Format/SL1.hpp
|
||||||
Format/SL1.cpp
|
Format/SL1.cpp
|
||||||
Format/SL1_SVG.hpp
|
Format/SL1_SVG.hpp
|
||||||
Format/SL1_SVG.cpp
|
Format/SL1_SVG.cpp
|
||||||
|
Format/pwmx.hpp
|
||||||
|
Format/pwmx.cpp
|
||||||
GCode/ThumbnailData.cpp
|
GCode/ThumbnailData.cpp
|
||||||
GCode/ThumbnailData.hpp
|
GCode/ThumbnailData.hpp
|
||||||
GCode/Thumbnails.cpp
|
GCode/Thumbnails.cpp
|
||||||
@ -362,7 +366,7 @@ find_package(JPEG REQUIRED)
|
|||||||
target_link_libraries(libslic3r
|
target_link_libraries(libslic3r
|
||||||
libnest2d
|
libnest2d
|
||||||
admesh
|
admesh
|
||||||
cereal
|
libcereal
|
||||||
libigl
|
libigl
|
||||||
miniz
|
miniz
|
||||||
boost_libs
|
boost_libs
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
@ -44,7 +45,8 @@ size_t max_concurrency(const EP &ep)
|
|||||||
template<class EP, class It, class Fn, class = ExecutionPolicyOnly<EP>>
|
template<class EP, class It, class Fn, class = ExecutionPolicyOnly<EP>>
|
||||||
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
|
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
|
||||||
{
|
{
|
||||||
AsTraits<EP>::for_each(ep, from, to, std::forward<Fn>(fn), granularity);
|
AsTraits<EP>::for_each(ep, from, to, std::forward<Fn>(fn),
|
||||||
|
std::max(granularity, size_t(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A reduce operation with the execution policy passed as argument.
|
// A reduce operation with the execution policy passed as argument.
|
||||||
@ -68,7 +70,7 @@ T reduce(const EP & ep,
|
|||||||
return AsTraits<EP>::reduce(ep, from, to, init,
|
return AsTraits<EP>::reduce(ep, from, to, init,
|
||||||
std::forward<MergeFn>(mergefn),
|
std::forward<MergeFn>(mergefn),
|
||||||
std::forward<AccessFn>(accessfn),
|
std::forward<AccessFn>(accessfn),
|
||||||
granularity);
|
std::max(granularity, size_t(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// An overload of reduce method to be used with iterators as 'from' and 'to'
|
// An overload of reduce method to be used with iterators as 'from' and 'to'
|
||||||
@ -87,7 +89,7 @@ T reduce(const EP &ep,
|
|||||||
{
|
{
|
||||||
return reduce(
|
return reduce(
|
||||||
ep, from, to, init, std::forward<MergeFn>(mergefn),
|
ep, from, to, init, std::forward<MergeFn>(mergefn),
|
||||||
[](const auto &i) { return i; }, granularity);
|
[](const auto &i) { return i; }, std::max(granularity, size_t(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class EP,
|
template<class EP,
|
||||||
@ -103,7 +105,8 @@ T accumulate(const EP & ep,
|
|||||||
size_t granularity = 1)
|
size_t granularity = 1)
|
||||||
{
|
{
|
||||||
return reduce(ep, from, to, init, std::plus<T>{},
|
return reduce(ep, from, to, init, std::plus<T>{},
|
||||||
std::forward<AccessFn>(accessfn), granularity);
|
std::forward<AccessFn>(accessfn),
|
||||||
|
std::max(granularity, size_t(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -119,7 +122,7 @@ T accumulate(const EP &ep,
|
|||||||
{
|
{
|
||||||
return reduce(
|
return reduce(
|
||||||
ep, from, to, init, std::plus<T>{}, [](const auto &i) { return i; },
|
ep, from, to, init, std::plus<T>{}, [](const auto &i) { return i; },
|
||||||
granularity);
|
std::max(granularity, size_t(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace execution_policy
|
} // namespace execution_policy
|
||||||
|
@ -20,11 +20,14 @@
|
|||||||
#include "libslic3r/miniz_extension.hpp"
|
#include "libslic3r/miniz_extension.hpp"
|
||||||
#include "libslic3r/PNGReadWrite.hpp"
|
#include "libslic3r/PNGReadWrite.hpp"
|
||||||
#include "libslic3r/LocalesUtils.hpp"
|
#include "libslic3r/LocalesUtils.hpp"
|
||||||
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||||
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include <miniz.h>
|
||||||
|
|
||||||
namespace marchsq {
|
namespace marchsq {
|
||||||
|
|
||||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||||
@ -482,10 +485,31 @@ sla::RasterEncoder SL1Archive::get_encoder() const
|
|||||||
return sla::PNGRasterEncoder{};
|
return sla::PNGRasterEncoder{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SL1Archive::export_print(Zipper& zipper,
|
static void write_thumbnail(Zipper &zipper, const ThumbnailData &data)
|
||||||
const SLAPrint &print,
|
|
||||||
const std::string &prjname)
|
|
||||||
{
|
{
|
||||||
|
size_t png_size = 0;
|
||||||
|
|
||||||
|
void *png_data = tdefl_write_image_to_png_file_in_memory_ex(
|
||||||
|
(const void *) data.pixels.data(), data.width, data.height, 4,
|
||||||
|
&png_size, MZ_DEFAULT_LEVEL, 1);
|
||||||
|
|
||||||
|
if (png_data != nullptr) {
|
||||||
|
zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) +
|
||||||
|
"x" + std::to_string(data.height) + ".png",
|
||||||
|
static_cast<const std::uint8_t *>(png_data),
|
||||||
|
png_size);
|
||||||
|
|
||||||
|
mz_free(png_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SL1Archive::export_print(const std::string fname,
|
||||||
|
const SLAPrint &print,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &prjname)
|
||||||
|
{
|
||||||
|
Zipper zipper{fname};
|
||||||
|
|
||||||
std::string project =
|
std::string project =
|
||||||
prjname.empty() ?
|
prjname.empty() ?
|
||||||
boost::filesystem::path(zipper.get_filename()).stem().string() :
|
boost::filesystem::path(zipper.get_filename()).stem().string() :
|
||||||
@ -512,6 +536,12 @@ void SL1Archive::export_print(Zipper& zipper,
|
|||||||
|
|
||||||
zipper.add_entry(imgname.c_str(), rst.data(), rst.size());
|
zipper.add_entry(imgname.c_str(), rst.data(), rst.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const ThumbnailData& data : thumbnails)
|
||||||
|
if (data.is_valid())
|
||||||
|
write_thumbnail(zipper, data);
|
||||||
|
|
||||||
|
zipper.finalize();
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||||
// Rethrow the exception
|
// Rethrow the exception
|
||||||
|
@ -3,8 +3,12 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "SLAArchive.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Zipper.hpp"
|
#include "libslic3r/Zipper.hpp"
|
||||||
#include "libslic3r/SLAPrint.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
|
||||||
|
struct indexed_triangle_set;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -23,8 +27,11 @@ public:
|
|||||||
SL1Archive() = default;
|
SL1Archive() = default;
|
||||||
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
|
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
|
||||||
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
|
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
|
||||||
|
|
||||||
void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "") override;
|
void export_print(const std::string fname,
|
||||||
|
const SLAPrint &print,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &projectname = "") override;
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
#include "SLA/RasterBase.hpp"
|
#include "SLA/RasterBase.hpp"
|
||||||
#include "libslic3r/LocalesUtils.hpp"
|
#include "libslic3r/LocalesUtils.hpp"
|
||||||
#include "libslic3r/ClipperUtils.hpp"
|
#include "libslic3r/ClipperUtils.hpp"
|
||||||
|
#include "libslic3r/BoundingBox.hpp"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <string_view>
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -77,20 +80,23 @@ void append_svg(std::string &buf, const Polygon &poly)
|
|||||||
|
|
||||||
char intbuf[coord_t_bufsize];
|
char intbuf[coord_t_bufsize];
|
||||||
|
|
||||||
buf += std::string("<path d=\"M ") + decimal_from(c.x(), intbuf);
|
buf += "<path d=\"M "sv;
|
||||||
buf += std::string(" ") + decimal_from(c.y(), intbuf) + " m";
|
buf += decimal_from(c.x(), intbuf);
|
||||||
|
buf += " "sv;
|
||||||
|
buf += decimal_from(c.y(), intbuf);
|
||||||
|
buf += " m"sv;
|
||||||
|
|
||||||
for (auto &p : poly) {
|
for (auto &p : poly) {
|
||||||
auto d = p - c;
|
auto d = p - c;
|
||||||
if (d.squaredNorm() == 0) continue;
|
if (d.squaredNorm() == 0) continue;
|
||||||
buf += " ";
|
buf += " "sv;
|
||||||
buf += decimal_from(p.x() - c.x(), intbuf);
|
buf += decimal_from(p.x() - c.x(), intbuf);
|
||||||
buf += " ";
|
buf += " "sv;
|
||||||
buf += decimal_from(p.y() - c.y(), intbuf);
|
buf += decimal_from(p.y() - c.y(), intbuf);
|
||||||
c = p;
|
c = p;
|
||||||
}
|
}
|
||||||
buf += " z\""; // mark path as closed
|
buf += " z\""sv; // mark path as closed
|
||||||
buf += " />\n";
|
buf += " />\n"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -167,12 +173,12 @@ public:
|
|||||||
sla::EncodedRaster encode(sla::RasterEncoder /*encoder*/) const override
|
sla::EncodedRaster encode(sla::RasterEncoder /*encoder*/) const override
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
constexpr const char finish[] = "</svg>\n";
|
constexpr auto finish = "</svg>\n"sv;
|
||||||
|
|
||||||
data.reserve(m_svg.size() + std::size(finish));
|
data.reserve(m_svg.size() + std::size(finish));
|
||||||
|
|
||||||
std::copy(m_svg.begin(), m_svg.end(), std::back_inserter(data));
|
std::copy(m_svg.begin(), m_svg.end(), std::back_inserter(data));
|
||||||
std::copy(finish, finish + std::size(finish) - 1, std::back_inserter(data));
|
std::copy(finish.begin(), finish.end() - 1, std::back_inserter(data));
|
||||||
|
|
||||||
return sla::EncodedRaster{std::move(data), "svg"};
|
return sla::EncodedRaster{std::move(data), "svg"};
|
||||||
}
|
}
|
||||||
|
74
src/libslic3r/Format/SLAArchive.cpp
Normal file
74
src/libslic3r/Format/SLAArchive.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "SLAArchive.hpp"
|
||||||
|
|
||||||
|
#include "SL1.hpp"
|
||||||
|
#include "SL1_SVG.hpp"
|
||||||
|
#include "pwmx.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
using ArchiveFactory = std::function<std::unique_ptr<SLAArchive>(const SLAPrinterConfig&)>;
|
||||||
|
|
||||||
|
struct ArchiveEntry {
|
||||||
|
const char *ext;
|
||||||
|
ArchiveFactory factoryfn;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
|
||||||
|
{
|
||||||
|
"SL1",
|
||||||
|
{ "sl1", [] (const auto &cfg) { return std::make_unique<SL1Archive>(cfg); } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SL2",
|
||||||
|
{ "sl2", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pwmx",
|
||||||
|
{ "pwmx", [] (const auto &cfg) { return std::make_unique<PwmxArchive>(cfg); } }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<SLAArchive>
|
||||||
|
SLAArchive::create(const std::string &archtype, const SLAPrinterConfig &cfg)
|
||||||
|
{
|
||||||
|
auto entry = REGISTERED_ARCHIVES.find(archtype);
|
||||||
|
|
||||||
|
if (entry != REGISTERED_ARCHIVES.end())
|
||||||
|
return entry->second.factoryfn(cfg);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<const char*>& SLAArchive::registered_archives()
|
||||||
|
{
|
||||||
|
static std::vector<const char*> archnames;
|
||||||
|
|
||||||
|
if (archnames.empty()) {
|
||||||
|
archnames.reserve(REGISTERED_ARCHIVES.size());
|
||||||
|
|
||||||
|
for (auto &[name, _] : REGISTERED_ARCHIVES)
|
||||||
|
archnames.emplace_back(name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return archnames;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SLAArchive::get_extension(const char *archtype)
|
||||||
|
{
|
||||||
|
static const char* DEFAULT_EXT = "zip";
|
||||||
|
|
||||||
|
auto entry = REGISTERED_ARCHIVES.find(archtype);
|
||||||
|
if (entry != REGISTERED_ARCHIVES.end())
|
||||||
|
return entry->second.ext;
|
||||||
|
|
||||||
|
return DEFAULT_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
64
src/libslic3r/Format/SLAArchive.hpp
Normal file
64
src/libslic3r/Format/SLAArchive.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef SLAARCHIVE_HPP
|
||||||
|
#define SLAARCHIVE_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "libslic3r/SLA/RasterBase.hpp"
|
||||||
|
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
||||||
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class SLAPrint;
|
||||||
|
class SLAPrinterConfig;
|
||||||
|
|
||||||
|
class SLAArchive {
|
||||||
|
protected:
|
||||||
|
std::vector<sla::EncodedRaster> m_layers;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<sla::RasterBase> create_raster() const = 0;
|
||||||
|
virtual sla::RasterEncoder get_encoder() const = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~SLAArchive() = default;
|
||||||
|
|
||||||
|
// Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid);
|
||||||
|
template<class Fn, class CancelFn, class EP = ExecutionTBB>
|
||||||
|
void draw_layers(
|
||||||
|
size_t layer_num,
|
||||||
|
Fn && drawfn,
|
||||||
|
CancelFn cancelfn = []() { return false; },
|
||||||
|
const EP & ep = {})
|
||||||
|
{
|
||||||
|
m_layers.resize(layer_num);
|
||||||
|
execution::for_each(
|
||||||
|
ep, size_t(0), m_layers.size(),
|
||||||
|
[this, &drawfn, &cancelfn](size_t idx) {
|
||||||
|
if (cancelfn()) return;
|
||||||
|
|
||||||
|
sla::EncodedRaster &enc = m_layers[idx];
|
||||||
|
auto rst = create_raster();
|
||||||
|
drawfn(*rst, idx);
|
||||||
|
enc = rst->encode(get_encoder());
|
||||||
|
},
|
||||||
|
execution::max_concurrency(ep));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the print into an archive using the provided filename.
|
||||||
|
virtual void export_print(const std::string fname,
|
||||||
|
const SLAPrint &print,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &projectname = "") = 0;
|
||||||
|
|
||||||
|
// Factory method to create an archiver instance
|
||||||
|
static std::unique_ptr<SLAArchive> create(const std::string &archtype, const SLAPrinterConfig&);
|
||||||
|
|
||||||
|
// Get the names of currently known archiver implementations
|
||||||
|
static const std::vector<const char *> & registered_archives();
|
||||||
|
|
||||||
|
// Get the default file extension belonging to an archive format
|
||||||
|
static const char *get_extension(const char *archtype);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // SLAARCHIVE_HPP
|
564
src/libslic3r/Format/pwmx.cpp
Normal file
564
src/libslic3r/Format/pwmx.cpp
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
#include "pwmx.hpp"
|
||||||
|
#include "GCode/ThumbnailData.hpp"
|
||||||
|
#include "SLA/RasterBase.hpp"
|
||||||
|
#include "libslic3r/SLAPrint.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#define TAG_INTRO "ANYCUBIC\0\0\0\0"
|
||||||
|
#define TAG_HEADER "HEADER\0\0\0\0\0\0"
|
||||||
|
#define TAG_PREVIEW "PREVIEW\0\0\0\0\0"
|
||||||
|
#define TAG_LAYERS "LAYERDEF\0\0\0\0"
|
||||||
|
|
||||||
|
#define CFG_LIFT_DISTANCE "LIFT_DISTANCE"
|
||||||
|
#define CFG_LIFT_SPEED "LIFT_SPEED"
|
||||||
|
#define CFG_RETRACT_SPEED "RETRACT_SPEED"
|
||||||
|
#define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE"
|
||||||
|
#define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED"
|
||||||
|
#define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE"
|
||||||
|
|
||||||
|
#define PREV_W 224
|
||||||
|
#define PREV_H 168
|
||||||
|
#define PREV_DPI 42
|
||||||
|
|
||||||
|
#define LAYER_SIZE_ESTIMATE (32 * 1024)
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
static void pwx_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end,
|
||||||
|
std::uint8_t& pixel, size_t& span_len)
|
||||||
|
{
|
||||||
|
size_t max_len;
|
||||||
|
|
||||||
|
span_len = 0;
|
||||||
|
pixel = (*ptr) & 0xF0;
|
||||||
|
// the maximum length of the span depends on the pixel color
|
||||||
|
max_len = (pixel == 0 || pixel == 0xF0) ? 0xFFF : 0xF;
|
||||||
|
while (ptr < end && span_len < max_len && ((*ptr) & 0xF0) == pixel) {
|
||||||
|
span_len++;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PWXRasterEncoder
|
||||||
|
{
|
||||||
|
sla::EncodedRaster operator()(const void *ptr,
|
||||||
|
size_t w,
|
||||||
|
size_t h,
|
||||||
|
size_t num_components)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> dst;
|
||||||
|
size_t span_len;
|
||||||
|
std::uint8_t pixel;
|
||||||
|
auto size = w * h * num_components;
|
||||||
|
dst.reserve(size);
|
||||||
|
|
||||||
|
const std::uint8_t *src = reinterpret_cast<const std::uint8_t *>(ptr);
|
||||||
|
const std::uint8_t *src_end = src + size;
|
||||||
|
while (src < src_end) {
|
||||||
|
pwx_get_pixel_span(src, src_end, pixel, span_len);
|
||||||
|
src += span_len;
|
||||||
|
// fully transparent of fully opaque pixel
|
||||||
|
if (pixel == 0 || pixel == 0xF0) {
|
||||||
|
pixel = pixel | (span_len >> 8);
|
||||||
|
std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst));
|
||||||
|
pixel = span_len & 0xFF;
|
||||||
|
std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst));
|
||||||
|
}
|
||||||
|
// antialiased pixel
|
||||||
|
else {
|
||||||
|
pixel = pixel | span_len;
|
||||||
|
std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sla::EncodedRaster(std::move(dst), "pwx");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ConfMap = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_intro
|
||||||
|
{
|
||||||
|
char tag[12];
|
||||||
|
std::uint32_t version; // value 1
|
||||||
|
std::uint32_t area_num; // unknown - usually 4
|
||||||
|
std::uint32_t header_data_offset;
|
||||||
|
std::float_t intro24; // unknown - usually 0
|
||||||
|
std::uint32_t preview_data_offset;
|
||||||
|
std::float_t intro32; // unknown
|
||||||
|
std::uint32_t layer_data_offset;
|
||||||
|
std::float_t intro40; // unknown
|
||||||
|
std::uint32_t image_data_offset;
|
||||||
|
} pwmx_format_intro;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_header
|
||||||
|
{
|
||||||
|
char tag[12];
|
||||||
|
std::uint32_t payload_size;
|
||||||
|
std::float_t pixel_size_um;
|
||||||
|
std::float_t layer_height_mm;
|
||||||
|
std::float_t exposure_time_s;
|
||||||
|
std::float_t delay_before_exposure_s;
|
||||||
|
std::float_t bottom_exposure_time_s;
|
||||||
|
std::float_t bottom_layer_count;
|
||||||
|
std::float_t lift_distance_mm;
|
||||||
|
std::float_t lift_speed_mms;
|
||||||
|
std::float_t retract_speed_mms;
|
||||||
|
std::float_t volume_ml;
|
||||||
|
std::uint32_t antialiasing;
|
||||||
|
std::uint32_t res_x;
|
||||||
|
std::uint32_t res_y;
|
||||||
|
std::float_t weight_g;
|
||||||
|
std::float_t price;
|
||||||
|
std::uint32_t price_currency;
|
||||||
|
std::uint32_t per_layer_override; // ? unknown meaning ?
|
||||||
|
std::uint32_t print_time_s;
|
||||||
|
std::uint32_t transition_layer_count;
|
||||||
|
std::uint32_t unknown; // ? usually 0 ?
|
||||||
|
|
||||||
|
} pwmx_format_header;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_preview
|
||||||
|
{
|
||||||
|
char tag[12];
|
||||||
|
std::uint32_t payload_size;
|
||||||
|
std::uint32_t preview_w;
|
||||||
|
std::uint32_t preview_dpi;
|
||||||
|
std::uint32_t preview_h;
|
||||||
|
// raw image data in BGR565 format
|
||||||
|
std::uint8_t pixels[PREV_W * PREV_H * 2];
|
||||||
|
} pwmx_format_preview;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_layers_header
|
||||||
|
{
|
||||||
|
char tag[12];
|
||||||
|
std::uint32_t payload_size;
|
||||||
|
std::uint32_t layer_count;
|
||||||
|
} pwmx_format_layers_header;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_layer
|
||||||
|
{
|
||||||
|
std::uint32_t image_offset;
|
||||||
|
std::uint32_t image_size;
|
||||||
|
std::float_t lift_distance_mm;
|
||||||
|
std::float_t lift_speed_mms;
|
||||||
|
std::float_t exposure_time_s;
|
||||||
|
std::float_t layer_height_mm;
|
||||||
|
std::float_t layer44; // unkown - usually 0
|
||||||
|
std::float_t layer48; // unkown - usually 0
|
||||||
|
} pwmx_format_layer;
|
||||||
|
|
||||||
|
typedef struct pwmx_format_misc
|
||||||
|
{
|
||||||
|
std::float_t bottom_layer_height_mm;
|
||||||
|
std::float_t bottom_lift_distance_mm;
|
||||||
|
std::float_t bottom_lift_speed_mms;
|
||||||
|
|
||||||
|
} pwmx_format_misc;
|
||||||
|
|
||||||
|
class PwmxFormatConfigDef : public ConfigDef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PwmxFormatConfigDef()
|
||||||
|
{
|
||||||
|
add(CFG_LIFT_DISTANCE, coFloat);
|
||||||
|
add(CFG_LIFT_SPEED, coFloat);
|
||||||
|
add(CFG_RETRACT_SPEED, coFloat);
|
||||||
|
add(CFG_DELAY_BEFORE_EXPOSURE, coFloat);
|
||||||
|
add(CFG_BOTTOM_LIFT_DISTANCE, coFloat);
|
||||||
|
add(CFG_BOTTOM_LIFT_SPEED, coFloat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PwmxFormatDynamicConfig : public DynamicConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PwmxFormatDynamicConfig(){};
|
||||||
|
const ConfigDef *def() const override { return &config_def; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PwmxFormatConfigDef config_def;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::float_t get_cfg_value_f(const DynamicConfig &cfg,
|
||||||
|
const std::string &key,
|
||||||
|
const std::float_t &def = 0.f)
|
||||||
|
{
|
||||||
|
if (cfg.has(key)) {
|
||||||
|
if (auto opt = cfg.option(key))
|
||||||
|
return opt->getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_cfg_value_i(const DynamicConfig &cfg,
|
||||||
|
const std::string &key,
|
||||||
|
const int &def = 0)
|
||||||
|
{
|
||||||
|
if (cfg.has(key)) {
|
||||||
|
if (auto opt = cfg.option(key))
|
||||||
|
return opt->getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> void crop_value(T &val, T val_min, T val_max)
|
||||||
|
{
|
||||||
|
if (val < val_min) {
|
||||||
|
val = val_min;
|
||||||
|
} else if (val > val_max) {
|
||||||
|
val = val_max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_preview(pwmx_format_preview &p,
|
||||||
|
pwmx_format_misc &/*m*/,
|
||||||
|
const ThumbnailsList &thumbnails)
|
||||||
|
{
|
||||||
|
|
||||||
|
p.preview_w = PREV_W;
|
||||||
|
p.preview_h = PREV_H;
|
||||||
|
p.preview_dpi = PREV_DPI;
|
||||||
|
p.payload_size = sizeof(p) - sizeof(p.tag) - sizeof(p.payload_size);
|
||||||
|
|
||||||
|
std::memset(p.pixels, 0 , sizeof(p.pixels));
|
||||||
|
if (!thumbnails.empty()) {
|
||||||
|
std::uint32_t dst_index;
|
||||||
|
std::uint32_t i = 0;
|
||||||
|
size_t len;
|
||||||
|
size_t pixel_x = 0;
|
||||||
|
auto t = thumbnails[0]; //use the first thumbnail
|
||||||
|
len = t.pixels.size();
|
||||||
|
//sanity check
|
||||||
|
if (len != PREV_W * PREV_H * 4) {
|
||||||
|
printf("incorrect thumbnail size. expected %ix%i\n", PREV_W, PREV_H);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// rearange pixels: they seem to be stored from bottom to top.
|
||||||
|
dst_index = (PREV_W * (PREV_H - 1) * 2);
|
||||||
|
while (i < len) {
|
||||||
|
std::uint32_t pixel;
|
||||||
|
std::uint32_t r = t.pixels[i++];
|
||||||
|
std::uint32_t g = t.pixels[i++];
|
||||||
|
std::uint32_t b = t.pixels[i++];
|
||||||
|
i++; // Alpha
|
||||||
|
// convert to BGRA565
|
||||||
|
pixel = ((b >> 3) << 11) | ((g >>2) << 5) | (r >> 3);
|
||||||
|
p.pixels[dst_index++] = pixel & 0xFF;
|
||||||
|
p.pixels[dst_index++] = (pixel >> 8) & 0xFF;
|
||||||
|
pixel_x++;
|
||||||
|
if (pixel_x == PREV_W) {
|
||||||
|
pixel_x = 0;
|
||||||
|
dst_index -= (PREV_W * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fill_header(pwmx_format_header &h,
|
||||||
|
pwmx_format_misc &m,
|
||||||
|
const SLAPrint &print,
|
||||||
|
std::uint32_t layer_count)
|
||||||
|
{
|
||||||
|
CNumericLocalesSetter locales_setter;
|
||||||
|
|
||||||
|
std::float_t bottle_weight_g;
|
||||||
|
std::float_t bottle_volume_ml;
|
||||||
|
std::float_t bottle_cost;
|
||||||
|
std::float_t material_density;
|
||||||
|
auto &cfg = print.full_print_config();
|
||||||
|
auto mat_opt = cfg.option("material_notes");
|
||||||
|
std::string mnotes = mat_opt? cfg.option("material_notes")->serialize() : "";
|
||||||
|
// create a config parser from the material notes
|
||||||
|
Slic3r::PwmxFormatDynamicConfig mat_cfg;
|
||||||
|
SLAPrintStatistics stats = print.print_statistics();
|
||||||
|
|
||||||
|
// sanitize the string config
|
||||||
|
boost::replace_all(mnotes, "\\n", "\n");
|
||||||
|
boost::replace_all(mnotes, "\\r", "\r");
|
||||||
|
mat_cfg.load_from_ini_string(mnotes,
|
||||||
|
ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
|
|
||||||
|
h.layer_height_mm = get_cfg_value_f(cfg, "layer_height");
|
||||||
|
m.bottom_layer_height_mm = get_cfg_value_f(cfg, "initial_layer_height");
|
||||||
|
h.exposure_time_s = get_cfg_value_f(cfg, "exposure_time");
|
||||||
|
h.bottom_exposure_time_s = get_cfg_value_f(cfg, "initial_exposure_time");
|
||||||
|
h.bottom_layer_count = get_cfg_value_i(cfg, "faded_layers");
|
||||||
|
if (layer_count < h.bottom_layer_count) {
|
||||||
|
h.bottom_layer_count = layer_count;
|
||||||
|
}
|
||||||
|
h.res_x = get_cfg_value_i(cfg, "display_pixels_x");
|
||||||
|
h.res_y = get_cfg_value_i(cfg, "display_pixels_y");
|
||||||
|
bottle_weight_g = get_cfg_value_f(cfg, "bottle_weight") * 1000.0f;
|
||||||
|
bottle_volume_ml = get_cfg_value_f(cfg, "bottle_volume");
|
||||||
|
bottle_cost = get_cfg_value_f(cfg, "bottle_cost");
|
||||||
|
material_density = bottle_weight_g / bottle_volume_ml;
|
||||||
|
|
||||||
|
h.volume_ml = (stats.objects_used_material + stats.support_used_material) / 1000;
|
||||||
|
h.weight_g = h.volume_ml * material_density;
|
||||||
|
h.price = (h.volume_ml * bottle_cost) / bottle_volume_ml;
|
||||||
|
h.price_currency = '$';
|
||||||
|
h.antialiasing = 1;
|
||||||
|
h.per_layer_override = 0;
|
||||||
|
|
||||||
|
// TODO - expose these variables to the UI rather than using material notes
|
||||||
|
h.delay_before_exposure_s = get_cfg_value_f(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, 0.5f);
|
||||||
|
crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f);
|
||||||
|
|
||||||
|
h.lift_distance_mm = get_cfg_value_f(mat_cfg, CFG_LIFT_DISTANCE, 8.0f);
|
||||||
|
crop_value(h.lift_distance_mm, 0.0f, 100.0f);
|
||||||
|
|
||||||
|
if (mat_cfg.has(CFG_BOTTOM_LIFT_DISTANCE)) {
|
||||||
|
m.bottom_lift_distance_mm = get_cfg_value_f(mat_cfg,
|
||||||
|
CFG_BOTTOM_LIFT_DISTANCE,
|
||||||
|
8.0f);
|
||||||
|
crop_value(h.lift_distance_mm, 0.0f, 100.0f);
|
||||||
|
} else {
|
||||||
|
m.bottom_lift_distance_mm = h.lift_distance_mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
h.lift_speed_mms = get_cfg_value_f(mat_cfg, CFG_LIFT_SPEED, 2.0f);
|
||||||
|
crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f);
|
||||||
|
|
||||||
|
if (mat_cfg.has(CFG_BOTTOM_LIFT_SPEED)) {
|
||||||
|
m.bottom_lift_speed_mms = get_cfg_value_f(mat_cfg, CFG_BOTTOM_LIFT_SPEED, 2.0f);
|
||||||
|
crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f);
|
||||||
|
} else {
|
||||||
|
m.bottom_lift_speed_mms = h.lift_speed_mms;
|
||||||
|
}
|
||||||
|
|
||||||
|
h.retract_speed_mms = get_cfg_value_f(mat_cfg, CFG_RETRACT_SPEED, 3.0f);
|
||||||
|
crop_value(h.lift_speed_mms, 0.1f, 20.0f);
|
||||||
|
|
||||||
|
h.print_time_s = (h.bottom_layer_count * h.bottom_exposure_time_s) +
|
||||||
|
((layer_count - h.bottom_layer_count) *
|
||||||
|
h.exposure_time_s) +
|
||||||
|
(layer_count * h.lift_distance_mm / h.retract_speed_mms) +
|
||||||
|
(layer_count * h.lift_distance_mm / h.lift_speed_mms) +
|
||||||
|
(layer_count * h.delay_before_exposure_s);
|
||||||
|
|
||||||
|
|
||||||
|
h.payload_size = sizeof(h) - sizeof(h.tag) - sizeof(h.payload_size);
|
||||||
|
h.pixel_size_um = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<sla::RasterBase> PwmxArchive::create_raster() const
|
||||||
|
{
|
||||||
|
sla::Resolution res;
|
||||||
|
sla::PixelDim pxdim;
|
||||||
|
std::array<bool, 2> mirror;
|
||||||
|
|
||||||
|
double w = m_cfg.display_width.getFloat();
|
||||||
|
double h = m_cfg.display_height.getFloat();
|
||||||
|
auto pw = size_t(m_cfg.display_pixels_x.getInt());
|
||||||
|
auto ph = size_t(m_cfg.display_pixels_y.getInt());
|
||||||
|
|
||||||
|
mirror[X] = m_cfg.display_mirror_x.getBool();
|
||||||
|
mirror[Y] = m_cfg.display_mirror_y.getBool();
|
||||||
|
|
||||||
|
auto ro = m_cfg.display_orientation.getInt();
|
||||||
|
sla::RasterBase::Orientation orientation =
|
||||||
|
ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait :
|
||||||
|
sla::RasterBase::roLandscape;
|
||||||
|
|
||||||
|
if (orientation == sla::RasterBase::roPortrait) {
|
||||||
|
std::swap(w, h);
|
||||||
|
std::swap(pw, ph);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = sla::Resolution{pw, ph};
|
||||||
|
pxdim = sla::PixelDim{w / pw, h / ph};
|
||||||
|
sla::RasterBase::Trafo tr{orientation, mirror};
|
||||||
|
|
||||||
|
double gamma = m_cfg.gamma_correction.getFloat();
|
||||||
|
|
||||||
|
return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
sla::RasterEncoder PwmxArchive::get_encoder() const
|
||||||
|
{
|
||||||
|
return PWXRasterEncoder{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endian safe write of little endian 32bit ints
|
||||||
|
static void pwmx_write_int32(std::ofstream &out, std::uint32_t val)
|
||||||
|
{
|
||||||
|
const char i1 = (val & 0xFF);
|
||||||
|
const char i2 = (val >> 8) & 0xFF;
|
||||||
|
const char i3 = (val >> 16) & 0xFF;
|
||||||
|
const char i4 = (val >> 24) & 0xFF;
|
||||||
|
|
||||||
|
out.write((const char *) &i1, 1);
|
||||||
|
out.write((const char *) &i2, 1);
|
||||||
|
out.write((const char *) &i3, 1);
|
||||||
|
out.write((const char *) &i4, 1);
|
||||||
|
}
|
||||||
|
static void pwmx_write_float(std::ofstream &out, std::float_t val)
|
||||||
|
{
|
||||||
|
std::uint32_t *f = (std::uint32_t *) &val;
|
||||||
|
pwmx_write_int32(out, *f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pwmx_write_intro(std::ofstream &out, pwmx_format_intro &i)
|
||||||
|
{
|
||||||
|
out.write(TAG_INTRO, sizeof(i.tag));
|
||||||
|
pwmx_write_int32(out, i.version);
|
||||||
|
pwmx_write_int32(out, i.area_num);
|
||||||
|
pwmx_write_int32(out, i.header_data_offset);
|
||||||
|
pwmx_write_int32(out, i.intro24);
|
||||||
|
pwmx_write_int32(out, i.preview_data_offset);
|
||||||
|
pwmx_write_int32(out, i.intro32);
|
||||||
|
pwmx_write_int32(out, i.layer_data_offset);
|
||||||
|
pwmx_write_int32(out, i.intro40);
|
||||||
|
pwmx_write_int32(out, i.image_data_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pwmx_write_header(std::ofstream &out, pwmx_format_header &h)
|
||||||
|
{
|
||||||
|
out.write(TAG_HEADER, sizeof(h.tag));
|
||||||
|
pwmx_write_int32(out, h.payload_size);
|
||||||
|
pwmx_write_float(out, h.pixel_size_um);
|
||||||
|
pwmx_write_float(out, h.layer_height_mm);
|
||||||
|
pwmx_write_float(out, h.exposure_time_s);
|
||||||
|
pwmx_write_float(out, h.delay_before_exposure_s);
|
||||||
|
pwmx_write_float(out, h.bottom_exposure_time_s);
|
||||||
|
pwmx_write_float(out, h.bottom_layer_count);
|
||||||
|
pwmx_write_float(out, h.lift_distance_mm);
|
||||||
|
pwmx_write_float(out, h.lift_speed_mms);
|
||||||
|
pwmx_write_float(out, h.retract_speed_mms);
|
||||||
|
pwmx_write_float(out, h.volume_ml);
|
||||||
|
pwmx_write_int32(out, h.antialiasing);
|
||||||
|
pwmx_write_int32(out, h.res_x);
|
||||||
|
pwmx_write_int32(out, h.res_y);
|
||||||
|
pwmx_write_float(out, h.weight_g);
|
||||||
|
pwmx_write_float(out, h.price);
|
||||||
|
pwmx_write_int32(out, h.price_currency);
|
||||||
|
pwmx_write_int32(out, h.per_layer_override);
|
||||||
|
pwmx_write_int32(out, h.print_time_s);
|
||||||
|
pwmx_write_int32(out, h.transition_layer_count);
|
||||||
|
pwmx_write_int32(out, h.unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pwmx_write_preview(std::ofstream &out, pwmx_format_preview &p)
|
||||||
|
{
|
||||||
|
out.write(TAG_PREVIEW, sizeof(p.tag));
|
||||||
|
pwmx_write_int32(out, p.payload_size);
|
||||||
|
pwmx_write_int32(out, p.preview_w);
|
||||||
|
pwmx_write_int32(out, p.preview_dpi);
|
||||||
|
pwmx_write_int32(out, p.preview_h);
|
||||||
|
out.write((const char*) p.pixels, sizeof(p.pixels));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pwmx_write_layers_header(std::ofstream &out, pwmx_format_layers_header &h)
|
||||||
|
{
|
||||||
|
out.write(TAG_LAYERS, sizeof(h.tag));
|
||||||
|
pwmx_write_int32(out, h.payload_size);
|
||||||
|
pwmx_write_int32(out, h.layer_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pwmx_write_layer(std::ofstream &out, pwmx_format_layer &l)
|
||||||
|
{
|
||||||
|
pwmx_write_int32(out, l.image_offset);
|
||||||
|
pwmx_write_int32(out, l.image_size);
|
||||||
|
pwmx_write_float(out, l.lift_distance_mm);
|
||||||
|
pwmx_write_float(out, l.lift_speed_mms);
|
||||||
|
pwmx_write_float(out, l.exposure_time_s);
|
||||||
|
pwmx_write_float(out, l.layer_height_mm);
|
||||||
|
pwmx_write_float(out, l.layer44);
|
||||||
|
pwmx_write_float(out, l.layer48);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwmxArchive::export_print(const std::string fname,
|
||||||
|
const SLAPrint &print,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &/*projectname*/)
|
||||||
|
{
|
||||||
|
std::uint32_t layer_count = m_layers.size();
|
||||||
|
|
||||||
|
pwmx_format_intro intro = {};
|
||||||
|
pwmx_format_header header = {};
|
||||||
|
pwmx_format_preview preview = {};
|
||||||
|
pwmx_format_layers_header layers_header = {};
|
||||||
|
pwmx_format_misc misc = {};
|
||||||
|
std::vector<uint8_t> layer_images;
|
||||||
|
std::uint32_t image_offset;
|
||||||
|
|
||||||
|
intro.version = 1;
|
||||||
|
intro.area_num = 4;
|
||||||
|
intro.header_data_offset = sizeof(intro);
|
||||||
|
intro.preview_data_offset = sizeof(intro) + sizeof(header);
|
||||||
|
intro.layer_data_offset = intro.preview_data_offset + sizeof(preview);
|
||||||
|
intro.image_data_offset = intro.layer_data_offset +
|
||||||
|
sizeof(layers_header) +
|
||||||
|
(sizeof(pwmx_format_layer) * layer_count);
|
||||||
|
|
||||||
|
fill_header(header, misc, print, layer_count);
|
||||||
|
fill_preview(preview, misc, thumbnails);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// open the file and write the contents
|
||||||
|
std::ofstream out;
|
||||||
|
out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
|
pwmx_write_intro(out, intro);
|
||||||
|
pwmx_write_header(out, header);
|
||||||
|
pwmx_write_preview(out, preview);
|
||||||
|
|
||||||
|
layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset -
|
||||||
|
sizeof(layers_header.tag) - sizeof(layers_header.payload_size);
|
||||||
|
layers_header.layer_count = layer_count;
|
||||||
|
pwmx_write_layers_header(out, layers_header);
|
||||||
|
|
||||||
|
//layers
|
||||||
|
layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE);
|
||||||
|
image_offset = intro.image_data_offset;
|
||||||
|
size_t i = 0;
|
||||||
|
for (const sla::EncodedRaster &rst : m_layers) {
|
||||||
|
pwmx_format_layer l;
|
||||||
|
std::memset(&l, 0, sizeof(l));
|
||||||
|
l.image_offset = image_offset;
|
||||||
|
l.image_size = rst.size();
|
||||||
|
if (i < header.bottom_layer_count) {
|
||||||
|
l.exposure_time_s = header.bottom_exposure_time_s;
|
||||||
|
l.layer_height_mm = misc.bottom_layer_height_mm;
|
||||||
|
l.lift_distance_mm = misc.bottom_lift_distance_mm;
|
||||||
|
l.lift_speed_mms = misc.bottom_lift_speed_mms;
|
||||||
|
} else {
|
||||||
|
l.exposure_time_s = header.exposure_time_s;
|
||||||
|
l.layer_height_mm = header.layer_height_mm;
|
||||||
|
l.lift_distance_mm = header.lift_distance_mm;
|
||||||
|
l.lift_speed_mms = header.lift_speed_mms;
|
||||||
|
}
|
||||||
|
image_offset += l.image_size;
|
||||||
|
pwmx_write_layer(out, l);
|
||||||
|
// add the rle encoded layer image into the buffer
|
||||||
|
const char* img_start = reinterpret_cast<const char*>(rst.data());
|
||||||
|
const char* img_end = img_start + rst.size();
|
||||||
|
std::copy(img_start, img_end, std::back_inserter(layer_images));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
const char* img_buffer = reinterpret_cast<const char*>(layer_images.data());
|
||||||
|
out.write(img_buffer, layer_images.size());
|
||||||
|
out.close();
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||||
|
// Rethrow the exception
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
37
src/libslic3r/Format/pwmx.hpp
Normal file
37
src/libslic3r/Format/pwmx.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef _SLIC3R_FORMAT_PWMX_HPP_
|
||||||
|
#define _SLIC3R_FORMAT_PWMX_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "SLAArchive.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class PwmxArchive: public SLAArchive {
|
||||||
|
SLAPrinterConfig m_cfg;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<sla::RasterBase> create_raster() const override;
|
||||||
|
sla::RasterEncoder get_encoder() const override;
|
||||||
|
|
||||||
|
SLAPrinterConfig & cfg() { return m_cfg; }
|
||||||
|
const SLAPrinterConfig & cfg() const { return m_cfg; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
PwmxArchive() = default;
|
||||||
|
explicit PwmxArchive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
|
||||||
|
explicit PwmxArchive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
|
||||||
|
|
||||||
|
void export_print(const std::string fname,
|
||||||
|
const SLAPrint &print,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &projectname = "") override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Slic3r::sla
|
||||||
|
|
||||||
|
#endif // _SLIC3R_FORMAT_PWMX_HPP_
|
@ -982,7 +982,7 @@ bool SupportTreeBuildsteps::connect_to_model_body(Head &head)
|
|||||||
double w = dist - 2 * head.r_pin_mm - head.r_back_mm;
|
double w = dist - 2 * head.r_pin_mm - head.r_back_mm;
|
||||||
|
|
||||||
if (w < 0.) {
|
if (w < 0.) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!";
|
BOOST_LOG_TRIVIAL(warning) << "Pinhead width is negative!";
|
||||||
w = 0.;
|
w = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "Format/SL1.hpp"
|
#include "Format/SL1.hpp"
|
||||||
#include "Format/SL1_SVG.hpp"
|
#include "Format/SL1_SVG.hpp"
|
||||||
|
#include "Format/pwmx.hpp"
|
||||||
|
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
@ -244,12 +245,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
|
|||||||
// Handle changes to object config defaults
|
// Handle changes to object config defaults
|
||||||
m_default_object_config.apply_only(config, object_diff, true);
|
m_default_object_config.apply_only(config, object_diff, true);
|
||||||
|
|
||||||
if (!m_archiver || !printer_diff.empty()) {
|
if (!m_archiver || !printer_diff.empty())
|
||||||
if (m_printer_config.sla_archive_format.value == "SL1")
|
m_archiver = SLAArchive::create(m_printer_config.sla_archive_format.value.c_str(), m_printer_config);
|
||||||
m_archiver = std::make_unique<SL1Archive>(m_printer_config);
|
|
||||||
else if (m_printer_config.sla_archive_format.value == "SL2")
|
|
||||||
m_archiver = std::make_unique<SL1_SVGArchive>(m_printer_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ModelObjectStatus {
|
struct ModelObjectStatus {
|
||||||
enum Status {
|
enum Status {
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "MTUtils.hpp"
|
#include "MTUtils.hpp"
|
||||||
#include "Zipper.hpp"
|
#include "Zipper.hpp"
|
||||||
|
#include "Format/SLAArchive.hpp"
|
||||||
|
#include "GCode/ThumbnailData.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
||||||
|
|
||||||
@ -389,47 +391,6 @@ struct SLAPrintStatistics
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SLAArchive {
|
|
||||||
protected:
|
|
||||||
std::vector<sla::EncodedRaster> m_layers;
|
|
||||||
|
|
||||||
virtual std::unique_ptr<sla::RasterBase> create_raster() const = 0;
|
|
||||||
virtual sla::RasterEncoder get_encoder() const = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~SLAArchive() = default;
|
|
||||||
|
|
||||||
// Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid);
|
|
||||||
template<class Fn, class CancelFn, class EP = ExecutionTBB>
|
|
||||||
void draw_layers(
|
|
||||||
size_t layer_num,
|
|
||||||
Fn && drawfn,
|
|
||||||
CancelFn cancelfn = []() { return false; },
|
|
||||||
const EP & ep = {})
|
|
||||||
{
|
|
||||||
m_layers.resize(layer_num);
|
|
||||||
execution::for_each(
|
|
||||||
ep, size_t(0), m_layers.size(),
|
|
||||||
[this, &drawfn, &cancelfn](size_t idx) {
|
|
||||||
if (cancelfn()) return;
|
|
||||||
|
|
||||||
sla::EncodedRaster &enc = m_layers[idx];
|
|
||||||
auto rst = create_raster();
|
|
||||||
drawfn(*rst, idx);
|
|
||||||
enc = rst->encode(get_encoder());
|
|
||||||
},
|
|
||||||
execution::max_concurrency(ep));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the print into an archive using the provided zipper.
|
|
||||||
// TODO: Use an archive writer interface instead of Zipper.
|
|
||||||
// This is quite limiting as the Zipper is a complete class, not an interface.
|
|
||||||
// The output can only be a zip archive.
|
|
||||||
virtual void export_print(Zipper &zipper,
|
|
||||||
const SLAPrint &print,
|
|
||||||
const std::string &projectname = "") = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This class is the high level FSM for the SLA printing process.
|
* @brief This class is the high level FSM for the SLA printing process.
|
||||||
*
|
*
|
||||||
@ -534,15 +495,17 @@ public:
|
|||||||
// TODO: use this structure for the preview in the future.
|
// TODO: use this structure for the preview in the future.
|
||||||
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
||||||
|
|
||||||
void export_print(Zipper &zipper, const std::string &projectname = "")
|
|
||||||
{
|
|
||||||
m_archiver->export_print(zipper, *this, projectname);
|
|
||||||
}
|
|
||||||
|
|
||||||
void export_print(const std::string &fname, const std::string &projectname = "")
|
void export_print(const std::string &fname, const std::string &projectname = "")
|
||||||
{
|
{
|
||||||
Zipper zipper(fname);
|
ThumbnailsList thumbnails; //empty thumbnail list
|
||||||
export_print(zipper, projectname);
|
export_print(fname, thumbnails, projectname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_print(const std::string &fname,
|
||||||
|
const ThumbnailsList &thumbnails,
|
||||||
|
const std::string &projectname = "")
|
||||||
|
{
|
||||||
|
m_archiver->export_print(fname, *this, thumbnails, projectname);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -288,7 +288,7 @@ endforeach()
|
|||||||
|
|
||||||
encoding_check(libslic3r_gui)
|
encoding_check(libslic3r_gui)
|
||||||
|
|
||||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES})
|
target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES})
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_libraries(libslic3r_gui Setupapi.lib)
|
target_link_libraries(libslic3r_gui Setupapi.lib)
|
||||||
|
@ -312,8 +312,7 @@ void Bed3D::init_triangles()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const GLModel::Geometry::EIndexType index_type = (triangles.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, GLModel::Geometry::index_type(triangles.size()) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2, index_type };
|
|
||||||
init_data.reserve_vertices(triangles.size());
|
init_data.reserve_vertices(triangles.size());
|
||||||
init_data.reserve_indices(triangles.size() / 3);
|
init_data.reserve_indices(triangles.size() / 3);
|
||||||
|
|
||||||
@ -335,10 +334,10 @@ void Bed3D::init_triangles()
|
|||||||
unsigned int vertices_counter = 0;
|
unsigned int vertices_counter = 0;
|
||||||
for (const Vec2f& v : triangles) {
|
for (const Vec2f& v : triangles) {
|
||||||
const Vec3f p = { v.x(), v.y(), GROUND_Z };
|
const Vec3f p = { v.x(), v.y(), GROUND_Z };
|
||||||
init_data.add_vertex(p, (Vec2f)v.cwiseProduct(inv_size).eval());
|
init_data.add_vertex(p, (Vec2f)(v - min).cwiseProduct(inv_size).eval());
|
||||||
++vertices_counter;
|
++vertices_counter;
|
||||||
if (vertices_counter % 3 == 0) {
|
if (vertices_counter % 3 == 0) {
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
|
init_data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
|
||||||
else
|
else
|
||||||
init_data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
init_data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
||||||
@ -381,8 +380,7 @@ void Bed3D::init_gridlines()
|
|||||||
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
|
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
|
||||||
|
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const GLModel::Geometry::EIndexType index_type = (2 * gridlines.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(2 * gridlines.size()) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type };
|
|
||||||
init_data.reserve_vertices(2 * gridlines.size());
|
init_data.reserve_vertices(2 * gridlines.size());
|
||||||
init_data.reserve_indices(2 * gridlines.size());
|
init_data.reserve_indices(2 * gridlines.size());
|
||||||
|
|
||||||
@ -390,7 +388,7 @@ void Bed3D::init_gridlines()
|
|||||||
init_data.add_vertex(Vec3f(unscale<float>(l.a.x()), unscale<float>(l.a.y()), GROUND_Z));
|
init_data.add_vertex(Vec3f(unscale<float>(l.a.x()), unscale<float>(l.a.y()), GROUND_Z));
|
||||||
init_data.add_vertex(Vec3f(unscale<float>(l.b.x()), unscale<float>(l.b.y()), GROUND_Z));
|
init_data.add_vertex(Vec3f(unscale<float>(l.b.x()), unscale<float>(l.b.y()), GROUND_Z));
|
||||||
const unsigned int vertices_counter = (unsigned int)init_data.vertices_count();
|
const unsigned int vertices_counter = (unsigned int)init_data.vertices_count();
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_line((unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
|
init_data.add_ushort_line((unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
|
||||||
else
|
else
|
||||||
init_data.add_uint_line(vertices_counter - 2, vertices_counter - 1);
|
init_data.add_uint_line(vertices_counter - 2, vertices_counter - 1);
|
||||||
|
@ -400,8 +400,7 @@ void GLVolume::NonManifoldEdges::update()
|
|||||||
if (!edges.empty()) {
|
if (!edges.empty()) {
|
||||||
GUI::GLModel::Geometry init_data;
|
GUI::GLModel::Geometry init_data;
|
||||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
const GUI::GLModel::Geometry::EIndexType index_type = (2 * edges.size() < 65536) ? GUI::GLModel::Geometry::EIndexType::USHORT : GUI::GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, GUI::GLModel::Geometry::index_type(2 * edges.size()) };
|
||||||
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Lines, GUI::GLModel::Geometry::EVertexLayout::P3, index_type };
|
|
||||||
init_data.reserve_vertices(2 * edges.size());
|
init_data.reserve_vertices(2 * edges.size());
|
||||||
init_data.reserve_indices(2 * edges.size());
|
init_data.reserve_indices(2 * edges.size());
|
||||||
|
|
||||||
@ -411,7 +410,7 @@ void GLVolume::NonManifoldEdges::update()
|
|||||||
init_data.add_vertex((Vec3f)mesh.its.vertices[edge.first].cast<float>());
|
init_data.add_vertex((Vec3f)mesh.its.vertices[edge.first].cast<float>());
|
||||||
init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast<float>());
|
init_data.add_vertex((Vec3f)mesh.its.vertices[edge.second].cast<float>());
|
||||||
vertices_count += 2;
|
vertices_count += 2;
|
||||||
if (index_type == GUI::GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GUI::GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
|
init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
|
||||||
else
|
else
|
||||||
init_data.add_uint_line(vertices_count - 2, vertices_count - 1);
|
init_data.add_uint_line(vertices_count - 2, vertices_count - 1);
|
||||||
@ -685,13 +684,12 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
const std::vector<int> &instance_idxs,
|
const std::vector<int> &instance_idxs,
|
||||||
const std::string &color_by,
|
|
||||||
bool opengl_initialized)
|
bool opengl_initialized)
|
||||||
{
|
{
|
||||||
std::vector<int> volumes_idx;
|
std::vector<int> volumes_idx;
|
||||||
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
|
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
|
||||||
for (int instance_idx : instance_idxs)
|
for (int instance_idx : instance_idxs)
|
||||||
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized));
|
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, opengl_initialized));
|
||||||
return volumes_idx;
|
return volumes_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,16 +698,13 @@ int GLVolumeCollection::load_object_volume(
|
|||||||
int obj_idx,
|
int obj_idx,
|
||||||
int volume_idx,
|
int volume_idx,
|
||||||
int instance_idx,
|
int instance_idx,
|
||||||
const std::string &color_by,
|
|
||||||
bool opengl_initialized)
|
bool opengl_initialized)
|
||||||
{
|
{
|
||||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||||
const int extruder_id = model_volume->extruder_id();
|
const int extruder_id = model_volume->extruder_id();
|
||||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
const ModelInstance *instance = model_object->instances[instance_idx];
|
||||||
const TriangleMesh &mesh = model_volume->mesh();
|
const TriangleMesh &mesh = model_volume->mesh();
|
||||||
ColorRGBA color = GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4];
|
this->volumes.emplace_back(new GLVolume());
|
||||||
color.a(model_volume->is_model_part() ? 1.0f : 0.5f);
|
|
||||||
this->volumes.emplace_back(new GLVolume(color));
|
|
||||||
GLVolume& v = *this->volumes.back();
|
GLVolume& v = *this->volumes.back();
|
||||||
v.set_color(color_from_model_volume(*model_volume));
|
v.set_color(color_from_model_volume(*model_volume));
|
||||||
#if ENABLE_SMOOTH_NORMALS
|
#if ENABLE_SMOOTH_NORMALS
|
||||||
|
@ -593,7 +593,6 @@ public:
|
|||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
const std::vector<int> &instance_idxs,
|
const std::vector<int> &instance_idxs,
|
||||||
const std::string &color_by,
|
|
||||||
bool opengl_initialized);
|
bool opengl_initialized);
|
||||||
|
|
||||||
int load_object_volume(
|
int load_object_volume(
|
||||||
@ -601,7 +600,6 @@ public:
|
|||||||
int obj_idx,
|
int obj_idx,
|
||||||
int volume_idx,
|
int volume_idx,
|
||||||
int instance_idx,
|
int instance_idx,
|
||||||
const std::string &color_by,
|
|
||||||
bool opengl_initialized);
|
bool opengl_initialized);
|
||||||
|
|
||||||
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
||||||
|
@ -165,17 +165,6 @@ void BackgroundSlicingProcess::process_fff()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_thumbnail(Zipper& zipper, const ThumbnailData& data)
|
|
||||||
{
|
|
||||||
size_t png_size = 0;
|
|
||||||
void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1);
|
|
||||||
if (png_data != nullptr)
|
|
||||||
{
|
|
||||||
zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size);
|
|
||||||
mz_free(png_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackgroundSlicingProcess::process_sla()
|
void BackgroundSlicingProcess::process_sla()
|
||||||
{
|
{
|
||||||
assert(m_print == m_sla_print);
|
assert(m_print == m_sla_print);
|
||||||
@ -189,12 +178,7 @@ void BackgroundSlicingProcess::process_sla()
|
|||||||
ThumbnailsList thumbnails = this->render_thumbnails(
|
ThumbnailsList thumbnails = this->render_thumbnails(
|
||||||
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
|
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
|
||||||
|
|
||||||
Zipper zipper(export_path);
|
m_sla_print->export_print(export_path, thumbnails);
|
||||||
m_sla_print->export_print(zipper);
|
|
||||||
for (const ThumbnailData& data : thumbnails)
|
|
||||||
if (data.is_valid())
|
|
||||||
write_thumbnail(zipper, data);
|
|
||||||
zipper.finalize();
|
|
||||||
|
|
||||||
m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
|
m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
|
||||||
} else if (! m_upload_job.empty()) {
|
} else if (! m_upload_job.empty()) {
|
||||||
@ -739,13 +723,7 @@ void BackgroundSlicingProcess::prepare_upload()
|
|||||||
|
|
||||||
ThumbnailsList thumbnails = this->render_thumbnails(
|
ThumbnailsList thumbnails = this->render_thumbnails(
|
||||||
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
|
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
|
||||||
// true, false, true, true); // renders also supports and pad
|
m_sla_print->export_print(source_path.string(),thumbnails, m_upload_job.upload_data.upload_path.string());
|
||||||
Zipper zipper{source_path.string()};
|
|
||||||
m_sla_print->export_print(zipper, m_upload_job.upload_data.upload_path.string());
|
|
||||||
for (const ThumbnailData& data : thumbnails)
|
|
||||||
if (data.is_valid())
|
|
||||||
write_thumbnail(zipper, data);
|
|
||||||
zipper.finalize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str());
|
m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str());
|
||||||
|
@ -2306,7 +2306,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t current_volumes_count = m_shells.volumes.volumes.size();
|
size_t current_volumes_count = m_shells.volumes.volumes.size();
|
||||||
m_shells.volumes.load_object(model_obj, object_id, instance_ids, "object", initialized);
|
m_shells.volumes.load_object(model_obj, object_id, instance_ids, initialized);
|
||||||
|
|
||||||
// adjust shells' z if raft is present
|
// adjust shells' z if raft is present
|
||||||
const SlicingParameters& slicing_parameters = obj->slicing_parameters();
|
const SlicingParameters& slicing_parameters = obj->slicing_parameters();
|
||||||
|
@ -447,8 +447,7 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect)
|
|||||||
m_profile.profile.reset();
|
m_profile.profile.reset();
|
||||||
|
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const GLModel::Geometry::EIndexType index_type = (m_layer_height_profile.size() / 2 < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, GLModel::Geometry::index_type(m_layer_height_profile.size() / 2) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P2, index_type };
|
|
||||||
init_data.color = ColorRGBA::BLUE();
|
init_data.color = ColorRGBA::BLUE();
|
||||||
init_data.reserve_vertices(m_layer_height_profile.size() / 2);
|
init_data.reserve_vertices(m_layer_height_profile.size() / 2);
|
||||||
init_data.reserve_indices(m_layer_height_profile.size() / 2);
|
init_data.reserve_indices(m_layer_height_profile.size() / 2);
|
||||||
@ -457,7 +456,7 @@ void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect)
|
|||||||
for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) {
|
for (unsigned int i = 0; i < (unsigned int)m_layer_height_profile.size(); i += 2) {
|
||||||
init_data.add_vertex(Vec2f(bar_rect.get_left() + float(m_layer_height_profile[i + 1]) * scale_x,
|
init_data.add_vertex(Vec2f(bar_rect.get_left() + float(m_layer_height_profile[i + 1]) * scale_x,
|
||||||
bar_rect.get_bottom() + float(m_layer_height_profile[i]) * scale_y));
|
bar_rect.get_bottom() + float(m_layer_height_profile[i]) * scale_y));
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_index((unsigned short)i / 2);
|
init_data.add_ushort_index((unsigned short)i / 2);
|
||||||
else
|
else
|
||||||
init_data.add_uint_index(i / 2);
|
init_data.add_uint_index(i / 2);
|
||||||
@ -496,17 +495,17 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
|||||||
{
|
{
|
||||||
assert(this->is_allowed());
|
assert(this->is_allowed());
|
||||||
assert(this->last_object_id != -1);
|
assert(this->last_object_id != -1);
|
||||||
|
|
||||||
|
GLShaderProgram* current_shader = wxGetApp().get_current_shader();
|
||||||
|
ScopeGuard guard([current_shader]() { if (current_shader != nullptr) current_shader->start_using(); });
|
||||||
|
if (current_shader != nullptr)
|
||||||
|
current_shader->stop_using();
|
||||||
|
|
||||||
GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height");
|
GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height");
|
||||||
if (shader == nullptr)
|
if (shader == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GLShaderProgram* current_shader = wxGetApp().get_current_shader();
|
shader->start_using();
|
||||||
if (shader->get_id() != current_shader->get_id())
|
|
||||||
// The layer editing shader is not yet active. Activate it.
|
|
||||||
shader->start_using();
|
|
||||||
else
|
|
||||||
// The layer editing shader was already active.
|
|
||||||
current_shader = nullptr;
|
|
||||||
|
|
||||||
generate_layer_height_texture();
|
generate_layer_height_texture();
|
||||||
|
|
||||||
@ -517,10 +516,10 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
|||||||
shader->set_uniform("z_cursor_band_width", float(this->band_width));
|
shader->set_uniform("z_cursor_band_width", float(this->band_width));
|
||||||
|
|
||||||
// Initialize the layer height texture mapping.
|
// Initialize the layer height texture mapping.
|
||||||
GLsizei w = (GLsizei)m_layers_texture.width;
|
const GLsizei w = (GLsizei)m_layers_texture.width;
|
||||||
GLsizei h = (GLsizei)m_layers_texture.height;
|
const GLsizei h = (GLsizei)m_layers_texture.height;
|
||||||
GLsizei half_w = w / 2;
|
const GLsizei half_w = w / 2;
|
||||||
GLsizei half_h = h / 2;
|
const GLsizei half_h = h / 2;
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||||
@ -529,17 +528,15 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
|||||||
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
|
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
|
||||||
for (const GLVolume* glvolume : volumes.volumes) {
|
for (const GLVolume* glvolume : volumes.volumes) {
|
||||||
// Render the object using the layer editing shader and texture.
|
// Render the object using the layer editing shader and texture.
|
||||||
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
shader->set_uniform("volume_world_matrix", glvolume->world_matrix());
|
shader->set_uniform("volume_world_matrix", glvolume->world_matrix());
|
||||||
shader->set_uniform("object_max_z", GLfloat(0));
|
shader->set_uniform("object_max_z", 0.0f);
|
||||||
glvolume->render();
|
glvolume->render();
|
||||||
}
|
}
|
||||||
// Revert back to the previous shader.
|
// Revert back to the previous shader.
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
if (current_shader != nullptr)
|
|
||||||
current_shader->start_using();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
|
void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
|
||||||
@ -1107,7 +1104,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
|||||||
, m_moving(false)
|
, m_moving(false)
|
||||||
, m_tab_down(false)
|
, m_tab_down(false)
|
||||||
, m_cursor_type(Standard)
|
, m_cursor_type(Standard)
|
||||||
, m_color_by("volume")
|
|
||||||
, m_reload_delayed(false)
|
, m_reload_delayed(false)
|
||||||
#if ENABLE_RENDER_PICKING_PASS
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
, m_show_picking_texture(false)
|
, m_show_picking_texture(false)
|
||||||
@ -1158,6 +1154,7 @@ bool GLCanvas3D::init()
|
|||||||
glsafe(::glEnable(GL_BLEND));
|
glsafe(::glEnable(GL_BLEND));
|
||||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||||
|
|
||||||
|
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
// Set antialiasing / multisampling
|
// Set antialiasing / multisampling
|
||||||
glsafe(::glDisable(GL_LINE_SMOOTH));
|
glsafe(::glDisable(GL_LINE_SMOOTH));
|
||||||
glsafe(::glDisable(GL_POLYGON_SMOOTH));
|
glsafe(::glDisable(GL_POLYGON_SMOOTH));
|
||||||
@ -1187,6 +1184,7 @@ bool GLCanvas3D::init()
|
|||||||
// A handy trick -- have surface material mirror the color.
|
// A handy trick -- have surface material mirror the color.
|
||||||
glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE));
|
glsafe(::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE));
|
||||||
glsafe(::glEnable(GL_COLOR_MATERIAL));
|
glsafe(::glEnable(GL_COLOR_MATERIAL));
|
||||||
|
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
|
||||||
if (m_multisample_allowed)
|
if (m_multisample_allowed)
|
||||||
glsafe(::glEnable(GL_MULTISAMPLE));
|
glsafe(::glEnable(GL_MULTISAMPLE));
|
||||||
@ -1357,11 +1355,6 @@ void GLCanvas3D::bed_shape_changed()
|
|||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_color_by(const std::string& value)
|
|
||||||
{
|
|
||||||
m_color_by = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::refresh_camera_scene_box()
|
void GLCanvas3D::refresh_camera_scene_box()
|
||||||
{
|
{
|
||||||
wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box());
|
wxGetApp().plater()->get_camera().set_scene_box(scene_bounding_box());
|
||||||
@ -1806,7 +1799,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
|
|||||||
instance_idxs.emplace_back(i);
|
instance_idxs.emplace_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized);
|
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
|
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
|
||||||
@ -2031,7 +2024,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
// Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
|
// Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
|
||||||
// later in this function.
|
// later in this function.
|
||||||
it->volume_idx = m_volumes.volumes.size();
|
it->volume_idx = m_volumes.volumes.size();
|
||||||
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized);
|
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized);
|
||||||
m_volumes.volumes.back()->geometry_id = key.geometry_id;
|
m_volumes.volumes.back()->geometry_id = key.geometry_id;
|
||||||
update_object_list = true;
|
update_object_list = true;
|
||||||
} else {
|
} else {
|
||||||
@ -5032,9 +5025,9 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
|
|||||||
|
|
||||||
// clamp max bb size with respect to bed bb size
|
// clamp max bb size with respect to bed bb size
|
||||||
if (!m_picking_enabled) {
|
if (!m_picking_enabled) {
|
||||||
static const double max_scale_factor = 1.5;
|
static const double max_scale_factor = 2.0;
|
||||||
const Vec3d bb_size = bb.size();
|
const Vec3d bb_size = bb.size();
|
||||||
const Vec3d bed_bb_size = bed_bb.size();
|
const Vec3d bed_bb_size = m_bed.build_volume().bounding_volume().size();
|
||||||
if (bb_size.x() > max_scale_factor * bed_bb_size.x() ||
|
if (bb_size.x() > max_scale_factor * bed_bb_size.x() ||
|
||||||
bb_size.y() > max_scale_factor * bed_bb_size.y() ||
|
bb_size.y() > max_scale_factor * bed_bb_size.y() ||
|
||||||
bb_size.z() > max_scale_factor * bed_bb_size.z()) {
|
bb_size.z() > max_scale_factor * bed_bb_size.z()) {
|
||||||
@ -5801,32 +5794,81 @@ void GLCanvas3D::_render_sla_slices()
|
|||||||
if (!obj->is_step_done(slaposSliceSupports))
|
if (!obj->is_step_done(slaposSliceSupports))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
SlaCap::ObjectIdToModelsMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i);
|
||||||
|
SlaCap::ObjectIdToModelsMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i);
|
||||||
|
#else
|
||||||
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i);
|
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i);
|
||||||
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i);
|
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i);
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
{
|
{
|
||||||
if (it_caps_bottom == m_sla_caps[0].triangles.end())
|
if (it_caps_bottom == m_sla_caps[0].triangles.end())
|
||||||
it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
|
it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
|
||||||
if (!m_sla_caps[0].matches(clip_min_z)) {
|
if (!m_sla_caps[0].matches(clip_min_z)) {
|
||||||
m_sla_caps[0].z = clip_min_z;
|
m_sla_caps[0].z = clip_min_z;
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
it_caps_bottom->second.object.reset();
|
||||||
|
it_caps_bottom->second.supports.reset();
|
||||||
|
#else
|
||||||
it_caps_bottom->second.object.clear();
|
it_caps_bottom->second.object.clear();
|
||||||
it_caps_bottom->second.supports.clear();
|
it_caps_bottom->second.supports.clear();
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
}
|
}
|
||||||
if (it_caps_top == m_sla_caps[1].triangles.end())
|
if (it_caps_top == m_sla_caps[1].triangles.end())
|
||||||
it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
|
it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
|
||||||
if (!m_sla_caps[1].matches(clip_max_z)) {
|
if (!m_sla_caps[1].matches(clip_max_z)) {
|
||||||
m_sla_caps[1].z = clip_max_z;
|
m_sla_caps[1].z = clip_max_z;
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
it_caps_top->second.object.reset();
|
||||||
|
it_caps_top->second.supports.reset();
|
||||||
|
#else
|
||||||
it_caps_top->second.object.clear();
|
it_caps_top->second.object.clear();
|
||||||
it_caps_top->second.supports.clear();
|
it_caps_top->second.supports.clear();
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
GLModel& bottom_obj_triangles = it_caps_bottom->second.object;
|
||||||
|
GLModel& bottom_sup_triangles = it_caps_bottom->second.supports;
|
||||||
|
GLModel& top_obj_triangles = it_caps_top->second.object;
|
||||||
|
GLModel& top_sup_triangles = it_caps_top->second.supports;
|
||||||
|
#else
|
||||||
Pointf3s &bottom_obj_triangles = it_caps_bottom->second.object;
|
Pointf3s &bottom_obj_triangles = it_caps_bottom->second.object;
|
||||||
Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports;
|
Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports;
|
||||||
Pointf3s &top_obj_triangles = it_caps_top->second.object;
|
Pointf3s &top_obj_triangles = it_caps_top->second.object;
|
||||||
Pointf3s &top_sup_triangles = it_caps_top->second.supports;
|
Pointf3s &top_sup_triangles = it_caps_top->second.supports;
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
auto init_model = [](GLModel& model, const Pointf3s& triangles, const ColorRGBA& color) {
|
||||||
|
GLModel::Geometry init_data;
|
||||||
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(triangles.size()) };
|
||||||
|
init_data.reserve_vertices(triangles.size());
|
||||||
|
init_data.reserve_indices(triangles.size() / 3);
|
||||||
|
init_data.color = color;
|
||||||
|
|
||||||
|
unsigned int vertices_count = 0;
|
||||||
|
for (const Vec3d& v : triangles) {
|
||||||
|
init_data.add_vertex((Vec3f)v.cast<float>());
|
||||||
|
++vertices_count;
|
||||||
|
if (vertices_count % 3 == 0) {
|
||||||
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
|
init_data.add_ushort_triangle((unsigned short)vertices_count - 3, (unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
|
||||||
|
else
|
||||||
|
init_data.add_uint_triangle(vertices_count - 3, vertices_count - 2, vertices_count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!init_data.is_empty())
|
||||||
|
model.init_from(std::move(init_data));
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((!bottom_obj_triangles.is_initialized() || !bottom_sup_triangles.is_initialized() ||
|
||||||
|
!top_obj_triangles.is_initialized() || !top_sup_triangles.is_initialized()) && !obj->get_slice_index().empty()) {
|
||||||
|
#else
|
||||||
if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) &&
|
if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) &&
|
||||||
!obj->get_slice_index().empty())
|
!obj->get_slice_index().empty()) {
|
||||||
{
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
double layer_height = print->default_object_config().layer_height.value;
|
double layer_height = print->default_object_config().layer_height.value;
|
||||||
double initial_layer_height = print->material_config().initial_layer_height.value;
|
double initial_layer_height = print->material_config().initial_layer_height.value;
|
||||||
bool left_handed = obj->is_left_handed();
|
bool left_handed = obj->is_left_handed();
|
||||||
@ -5841,39 +5883,81 @@ void GLCanvas3D::_render_sla_slices()
|
|||||||
const SliceRecord& slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON));
|
const SliceRecord& slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON));
|
||||||
|
|
||||||
// Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts.
|
// Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts.
|
||||||
double plane_shift_z = 0.002;
|
const double plane_shift_z = 0.002;
|
||||||
|
|
||||||
if (slice_low.is_valid()) {
|
if (slice_low.is_valid()) {
|
||||||
const ExPolygons& obj_bottom = slice_low.get_slice(soModel);
|
const ExPolygons& obj_bottom = slice_low.get_slice(soModel);
|
||||||
const ExPolygons& sup_bottom = slice_low.get_slice(soSupport);
|
const ExPolygons& sup_bottom = slice_low.get_slice(soSupport);
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
// calculate model bottom cap
|
||||||
|
if (!bottom_obj_triangles.is_initialized() && !obj_bottom.empty())
|
||||||
|
init_model(bottom_obj_triangles, triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.37f, 0.0f, 1.0f });
|
||||||
|
// calculate support bottom cap
|
||||||
|
if (!bottom_sup_triangles.is_initialized() && !sup_bottom.empty())
|
||||||
|
init_model(bottom_sup_triangles, triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, !left_handed), { 1.0f, 0.0f, 0.37f, 1.0f });
|
||||||
|
#else
|
||||||
// calculate model bottom cap
|
// calculate model bottom cap
|
||||||
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
|
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
|
||||||
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed);
|
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, ! left_handed);
|
||||||
// calculate support bottom cap
|
// calculate support bottom cap
|
||||||
if (bottom_sup_triangles.empty() && !sup_bottom.empty())
|
if (bottom_sup_triangles.empty() && !sup_bottom.empty())
|
||||||
bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, ! left_handed);
|
bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, !left_handed);
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slice_high.is_valid()) {
|
if (slice_high.is_valid()) {
|
||||||
const ExPolygons& obj_top = slice_high.get_slice(soModel);
|
const ExPolygons& obj_top = slice_high.get_slice(soModel);
|
||||||
const ExPolygons& sup_top = slice_high.get_slice(soSupport);
|
const ExPolygons& sup_top = slice_high.get_slice(soSupport);
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
// calculate model top cap
|
||||||
|
if (!top_obj_triangles.is_initialized() && !obj_top.empty())
|
||||||
|
init_model(top_obj_triangles, triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.37f, 0.0f, 1.0f });
|
||||||
|
// calculate support top cap
|
||||||
|
if (!top_sup_triangles.is_initialized() && !sup_top.empty())
|
||||||
|
init_model(top_sup_triangles, triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed), { 1.0f, 0.0f, 0.37f, 1.0f });
|
||||||
|
#else
|
||||||
// calculate model top cap
|
// calculate model top cap
|
||||||
if (top_obj_triangles.empty() && !obj_top.empty())
|
if (top_obj_triangles.empty() && !obj_top.empty())
|
||||||
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed);
|
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, left_handed);
|
||||||
// calculate support top cap
|
// calculate support top cap
|
||||||
if (top_sup_triangles.empty() && !sup_top.empty())
|
if (top_sup_triangles.empty() && !sup_top.empty())
|
||||||
top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed);
|
top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z + plane_shift_z, left_handed);
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||||
|
if (shader != nullptr) {
|
||||||
|
shader->start_using();
|
||||||
|
|
||||||
|
for (const SLAPrintObject::Instance& inst : obj->instances()) {
|
||||||
|
glsafe(::glPushMatrix());
|
||||||
|
glsafe(::glTranslated(unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0.0));
|
||||||
|
glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0f, 0.0f, 1.0f));
|
||||||
|
if (obj->is_left_handed())
|
||||||
|
// The polygons are mirrored by X.
|
||||||
|
glsafe(::glScalef(-1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
|
bottom_obj_triangles.render();
|
||||||
|
top_obj_triangles.render();
|
||||||
|
bottom_sup_triangles.render();
|
||||||
|
top_sup_triangles.render();
|
||||||
|
|
||||||
|
glsafe(::glPopMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
shader->stop_using();
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) {
|
if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty()) {
|
||||||
for (const SLAPrintObject::Instance& inst : obj->instances()) {
|
for (const SLAPrintObject::Instance& inst : obj->instances()) {
|
||||||
glsafe(::glPushMatrix());
|
glsafe(::glPushMatrix());
|
||||||
glsafe(::glTranslated(unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0));
|
glsafe(::glTranslated(unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0.0));
|
||||||
glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0, 0.0, 1.0));
|
glsafe(::glRotatef(Geometry::rad2deg(inst.rotation), 0.0f, 0.0f, 1.0f));
|
||||||
if (obj->is_left_handed())
|
if (obj->is_left_handed())
|
||||||
// The polygons are mirrored by X.
|
// The polygons are mirrored by X.
|
||||||
glsafe(::glScalef(-1.0, 1.0, 1.0));
|
glsafe(::glScalef(-1.0f, 1.0f, 1.0f));
|
||||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||||
glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
|
glsafe(::glColor3f(1.0f, 0.37f, 0.0f));
|
||||||
if (!bottom_obj_triangles.empty()) {
|
if (!bottom_obj_triangles.empty()) {
|
||||||
@ -5897,6 +5981,7 @@ void GLCanvas3D::_render_sla_slices()
|
|||||||
glsafe(::glPopMatrix());
|
glsafe(::glPopMatrix());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +335,16 @@ class GLCanvas3D
|
|||||||
|
|
||||||
struct SlaCap
|
struct SlaCap
|
||||||
{
|
{
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
struct Triangles
|
||||||
|
{
|
||||||
|
GLModel object;
|
||||||
|
GLModel supports;
|
||||||
|
};
|
||||||
|
typedef std::map<unsigned int, Triangles> ObjectIdToModelsMap;
|
||||||
|
double z;
|
||||||
|
ObjectIdToModelsMap triangles;
|
||||||
|
#else
|
||||||
struct Triangles
|
struct Triangles
|
||||||
{
|
{
|
||||||
Pointf3s object;
|
Pointf3s object;
|
||||||
@ -343,6 +353,7 @@ class GLCanvas3D
|
|||||||
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
||||||
double z;
|
double z;
|
||||||
ObjectIdToTrianglesMap triangles;
|
ObjectIdToTrianglesMap triangles;
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
|
||||||
SlaCap() { reset(); }
|
SlaCap() { reset(); }
|
||||||
void reset() { z = DBL_MAX; triangles.clear(); }
|
void reset() { z = DBL_MAX; triangles.clear(); }
|
||||||
@ -473,7 +484,7 @@ private:
|
|||||||
std::array<ClippingPlane, 2> m_clipping_planes;
|
std::array<ClippingPlane, 2> m_clipping_planes;
|
||||||
ClippingPlane m_camera_clipping_plane;
|
ClippingPlane m_camera_clipping_plane;
|
||||||
bool m_use_clipping_planes;
|
bool m_use_clipping_planes;
|
||||||
SlaCap m_sla_caps[2];
|
std::array<SlaCap, 2> m_sla_caps;
|
||||||
std::string m_sidebar_field;
|
std::string m_sidebar_field;
|
||||||
// when true renders an extra frame by not resetting m_dirty to false
|
// when true renders an extra frame by not resetting m_dirty to false
|
||||||
// see request_extra_frame()
|
// see request_extra_frame()
|
||||||
@ -511,8 +522,6 @@ private:
|
|||||||
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)
|
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)
|
||||||
bool m_render_sla_auxiliaries;
|
bool m_render_sla_auxiliaries;
|
||||||
|
|
||||||
std::string m_color_by;
|
|
||||||
|
|
||||||
bool m_reload_delayed;
|
bool m_reload_delayed;
|
||||||
|
|
||||||
#if ENABLE_RENDER_PICKING_PASS
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
@ -681,8 +690,6 @@ public:
|
|||||||
bool get_use_clipping_planes() const { return m_use_clipping_planes; }
|
bool get_use_clipping_planes() const { return m_use_clipping_planes; }
|
||||||
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
|
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
|
||||||
|
|
||||||
void set_color_by(const std::string& value);
|
|
||||||
|
|
||||||
void refresh_camera_scene_box();
|
void refresh_camera_scene_box();
|
||||||
|
|
||||||
BoundingBoxf3 volumes_bounding_box() const;
|
BoundingBoxf3 volumes_bounding_box() const;
|
||||||
|
@ -328,6 +328,11 @@ size_t GLModel::Geometry::index_stride_bytes(const Format& format)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLModel::Geometry::EIndexType GLModel::Geometry::index_type(size_t vertices_count)
|
||||||
|
{
|
||||||
|
return (vertices_count < 65536) ? EIndexType::USHORT : EIndexType::UINT;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLModel::Geometry::has_position(const Format& format)
|
bool GLModel::Geometry::has_position(const Format& format)
|
||||||
{
|
{
|
||||||
switch (format.vertex_layout)
|
switch (format.vertex_layout)
|
||||||
|
@ -83,11 +83,11 @@ namespace GUI {
|
|||||||
void reserve_vertices(size_t vertices_count);
|
void reserve_vertices(size_t vertices_count);
|
||||||
void reserve_indices(size_t indices_count);
|
void reserve_indices(size_t indices_count);
|
||||||
|
|
||||||
void add_vertex(const Vec2f& position);
|
void add_vertex(const Vec2f& position); // EVertexLayout::P2
|
||||||
void add_vertex(const Vec2f& position, const Vec2f& tex_coord);
|
void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2
|
||||||
void add_vertex(const Vec3f& position);
|
void add_vertex(const Vec3f& position); // EVertexLayout::P3
|
||||||
void add_vertex(const Vec3f& position, const Vec2f& tex_coord);
|
void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2
|
||||||
void add_vertex(const Vec3f& position, const Vec3f& normal);
|
void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
|
||||||
|
|
||||||
void add_ushort_index(unsigned short id);
|
void add_ushort_index(unsigned short id);
|
||||||
void add_uint_index(unsigned int id);
|
void add_uint_index(unsigned int id);
|
||||||
@ -135,6 +135,8 @@ namespace GUI {
|
|||||||
|
|
||||||
static size_t index_stride_bytes(const Format& format);
|
static size_t index_stride_bytes(const Format& format);
|
||||||
|
|
||||||
|
static EIndexType index_type(size_t vertices_count);
|
||||||
|
|
||||||
static bool has_position(const Format& format);
|
static bool has_position(const Format& format);
|
||||||
static bool has_normal(const Format& format);
|
static bool has_normal(const Format& format);
|
||||||
static bool has_tex_coord(const Format& format);
|
static bool has_tex_coord(const Format& format);
|
||||||
|
@ -497,7 +497,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
|
|||||||
|
|
||||||
/* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } },
|
/* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } },
|
||||||
|
|
||||||
/* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } },
|
/* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } },
|
||||||
};
|
};
|
||||||
|
|
||||||
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
|
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
|
||||||
|
@ -362,14 +362,13 @@ void GLGizmoFlatten::update_planes()
|
|||||||
for (auto& plane : m_planes) {
|
for (auto& plane : m_planes) {
|
||||||
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
|
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const GLModel::Geometry::EIndexType index_type = (plane.vertices.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(plane.vertices.size()) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3, index_type };
|
|
||||||
init_data.reserve_vertices(plane.vertices.size());
|
init_data.reserve_vertices(plane.vertices.size());
|
||||||
init_data.reserve_indices(plane.vertices.size());
|
init_data.reserve_indices(plane.vertices.size());
|
||||||
// vertices + indices
|
// vertices + indices
|
||||||
for (size_t i = 0; i < plane.vertices.size(); ++i) {
|
for (size_t i = 0; i < plane.vertices.size(); ++i) {
|
||||||
init_data.add_vertex((Vec3f)plane.vertices[i].cast<float>(), (Vec3f)plane.normal.cast<float>());
|
init_data.add_vertex((Vec3f)plane.vertices[i].cast<float>(), (Vec3f)plane.normal.cast<float>());
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_index((unsigned short)i);
|
init_data.add_ushort_index((unsigned short)i);
|
||||||
else
|
else
|
||||||
init_data.add_uint_index((unsigned int)i);
|
init_data.add_uint_index((unsigned int)i);
|
||||||
|
@ -1189,8 +1189,7 @@ void TriangleSelectorGUI::update_paint_contour()
|
|||||||
|
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
|
const std::vector<Vec2i> contour_edges = this->get_seed_fill_contour();
|
||||||
const GLModel::Geometry::EIndexType index_type = (2 * contour_edges.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::index_type(2 * contour_edges.size()) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, index_type };
|
|
||||||
init_data.reserve_vertices(2 * contour_edges.size());
|
init_data.reserve_vertices(2 * contour_edges.size());
|
||||||
init_data.reserve_indices(2 * contour_edges.size());
|
init_data.reserve_indices(2 * contour_edges.size());
|
||||||
// vertices + indices
|
// vertices + indices
|
||||||
@ -1199,7 +1198,7 @@ void TriangleSelectorGUI::update_paint_contour()
|
|||||||
init_data.add_vertex(m_vertices[edge(0)].v);
|
init_data.add_vertex(m_vertices[edge(0)].v);
|
||||||
init_data.add_vertex(m_vertices[edge(1)].v);
|
init_data.add_vertex(m_vertices[edge(1)].v);
|
||||||
vertices_count += 2;
|
vertices_count += 2;
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
|
init_data.add_ushort_line((unsigned short)vertices_count - 2, (unsigned short)vertices_count - 1);
|
||||||
else
|
else
|
||||||
init_data.add_uint_line(vertices_count - 2, vertices_count - 1);
|
init_data.add_uint_line(vertices_count - 2, vertices_count - 1);
|
||||||
|
@ -147,15 +147,14 @@ void GLGizmoRotate::on_render()
|
|||||||
if (shader != nullptr) {
|
if (shader != nullptr) {
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
|
|
||||||
const float radius = Offset + m_parent.get_selection().get_bounding_box().radius();
|
const bool radius_changed = std::abs(m_old_radius - m_radius) > EPSILON;
|
||||||
const bool radius_changed = std::abs(m_old_radius - radius) > EPSILON;
|
m_old_radius = m_radius;
|
||||||
m_old_radius = radius;
|
|
||||||
|
|
||||||
ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color);
|
ColorRGBA color((m_hover_id != -1) ? m_drag_color : m_highlight_color);
|
||||||
render_circle(color, radius_changed);
|
render_circle(color, radius_changed);
|
||||||
if (m_hover_id != -1) {
|
if (m_hover_id != -1) {
|
||||||
const bool hover_radius_changed = std::abs(m_old_hover_radius - radius) > EPSILON;
|
const bool hover_radius_changed = std::abs(m_old_hover_radius - m_radius) > EPSILON;
|
||||||
m_old_hover_radius = radius;
|
m_old_hover_radius = m_radius;
|
||||||
|
|
||||||
render_scale(color, hover_radius_changed);
|
render_scale(color, hover_radius_changed);
|
||||||
render_snap_radii(color, hover_radius_changed);
|
render_snap_radii(color, hover_radius_changed);
|
||||||
|
@ -127,6 +127,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||||||
if (! has_points && ! has_holes)
|
if (! has_points && ! has_holes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
GLShaderProgram* shader = wxGetApp().get_shader(picking ? "flat" : "gouraud_light");
|
||||||
|
if (shader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
shader->start_using();
|
||||||
|
ScopeGuard guard([shader]() { shader->stop_using(); });
|
||||||
|
#else
|
||||||
GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
|
GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
|
||||||
if (shader != nullptr)
|
if (shader != nullptr)
|
||||||
shader->start_using();
|
shader->start_using();
|
||||||
@ -134,6 +142,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||||||
if (shader != nullptr)
|
if (shader != nullptr)
|
||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
});
|
});
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
|
|
||||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
||||||
@ -177,11 +186,12 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||||||
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
#if ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
m_cone.set_color(render_color);
|
m_cone.set_color(render_color);
|
||||||
m_sphere.set_color(render_color);
|
m_sphere.set_color(render_color);
|
||||||
|
if (!picking)
|
||||||
#else
|
#else
|
||||||
m_cone.set_color(-1, render_color);
|
m_cone.set_color(-1, render_color);
|
||||||
m_sphere.set_color(-1, render_color);
|
m_sphere.set_color(-1, render_color);
|
||||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
|
||||||
if (shader && !picking)
|
if (shader && !picking)
|
||||||
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
shader->set_uniform("emission_factor", 0.5f);
|
shader->set_uniform("emission_factor", 0.5f);
|
||||||
|
|
||||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||||
@ -236,9 +246,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||||||
m_cylinder.set_color(render_color);
|
m_cylinder.set_color(render_color);
|
||||||
#else
|
#else
|
||||||
m_cylinder.set_color(-1, render_color);
|
m_cylinder.set_color(-1, render_color);
|
||||||
|
if (shader != nu)
|
||||||
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
|
||||||
if (shader)
|
shader->set_uniform("emission_factor", 0.5f);
|
||||||
shader->set_uniform("emission_factor", 0.5f);
|
|
||||||
for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
|
for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
|
||||||
if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
|
if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
|
||||||
continue;
|
continue;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "SLAImportJob.hpp"
|
#include "SLAImportJob.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/SLAPrint.hpp"
|
||||||
#include "libslic3r/Format/SL1.hpp"
|
#include "libslic3r/Format/SL1.hpp"
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
@ -189,8 +189,7 @@ void MeshClipper::recalculate_triangles()
|
|||||||
m_model.reset();
|
m_model.reset();
|
||||||
|
|
||||||
GLModel::Geometry init_data;
|
GLModel::Geometry init_data;
|
||||||
const GLModel::Geometry::EIndexType index_type = (m_triangles2d.size() < 65536) ? GLModel::Geometry::EIndexType::USHORT : GLModel::Geometry::EIndexType::UINT;
|
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(m_triangles2d.size()) };
|
||||||
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, index_type };
|
|
||||||
init_data.reserve_vertices(m_triangles2d.size());
|
init_data.reserve_vertices(m_triangles2d.size());
|
||||||
init_data.reserve_indices(m_triangles2d.size());
|
init_data.reserve_indices(m_triangles2d.size());
|
||||||
|
|
||||||
@ -200,7 +199,7 @@ void MeshClipper::recalculate_triangles()
|
|||||||
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
|
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
|
||||||
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
|
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
|
||||||
const size_t idx = it - m_triangles2d.cbegin();
|
const size_t idx = it - m_triangles2d.cbegin();
|
||||||
if (index_type == GLModel::Geometry::EIndexType::USHORT)
|
if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
|
||||||
init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2);
|
init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2);
|
||||||
else
|
else
|
||||||
init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);
|
init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
#include "libslic3r/SLA/IndexedMesh.hpp"
|
#include "libslic3r/SLA/IndexedMesh.hpp"
|
||||||
#include "admesh/stl.h"
|
#include "admesh/stl.h"
|
||||||
|
|
||||||
|
@ -233,8 +233,9 @@ OpenGLManager::~OpenGLManager()
|
|||||||
bool OpenGLManager::init_gl()
|
bool OpenGLManager::init_gl()
|
||||||
{
|
{
|
||||||
if (!m_gl_initialized) {
|
if (!m_gl_initialized) {
|
||||||
if (glewInit() != GLEW_OK) {
|
GLenum err = glewInit();
|
||||||
BOOST_LOG_TRIVIAL(error) << "Unable to init glew library";
|
if (err != GLEW_OK) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Unable to init glew library: " << glewGetErrorString(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_gl_initialized = true;
|
m_gl_initialized = true;
|
||||||
|
@ -4,4 +4,6 @@ target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
|
|||||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]")
|
set(_catch_args "exclude:[NotWorking]")
|
||||||
|
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
|
||||||
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})
|
||||||
|
@ -3,9 +3,14 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
|
|||||||
sla_print_tests.cpp
|
sla_print_tests.cpp
|
||||||
sla_test_utils.hpp sla_test_utils.cpp
|
sla_test_utils.hpp sla_test_utils.cpp
|
||||||
sla_supptgen_tests.cpp
|
sla_supptgen_tests.cpp
|
||||||
sla_raycast_tests.cpp)
|
sla_raycast_tests.cpp
|
||||||
|
sla_archive_export_tests.cpp)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
prusaslicer_copy_dlls(${_TEST_NAME}_tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})
|
||||||
|
40
tests/sla_print/sla_archive_export_tests.cpp
Normal file
40
tests/sla_print/sla_archive_export_tests.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <test_utils.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/SLAPrint.hpp"
|
||||||
|
#include "libslic3r/Format/SLAArchive.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
TEST_CASE("Archive export test", "[sla_archives]") {
|
||||||
|
constexpr const char *PNAME = "20mm_cube";
|
||||||
|
|
||||||
|
for (auto &archname : SLAArchive::registered_archives()) {
|
||||||
|
INFO(std::string("Testing archive type: ") + archname);
|
||||||
|
SLAPrint print;
|
||||||
|
SLAFullPrintConfig fullcfg;
|
||||||
|
|
||||||
|
auto m = Model::read_from_file(TEST_DATA_DIR PATH_SEPARATOR + std::string(PNAME) + ".obj", nullptr);
|
||||||
|
|
||||||
|
fullcfg.set("sla_archive_format", archname);
|
||||||
|
fullcfg.set("supports_enable", false);
|
||||||
|
fullcfg.set("pad_enable", false);
|
||||||
|
|
||||||
|
DynamicPrintConfig cfg;
|
||||||
|
cfg.apply(fullcfg);
|
||||||
|
|
||||||
|
print.set_status_callback([](const PrintBase::SlicingStatus&) {});
|
||||||
|
print.apply(m, cfg);
|
||||||
|
print.process();
|
||||||
|
|
||||||
|
ThumbnailsList thumbnails;
|
||||||
|
auto outputfname = std::string("output.") + SLAArchive::get_extension(archname);
|
||||||
|
|
||||||
|
print.export_print(outputfname, thumbnails, PNAME);
|
||||||
|
|
||||||
|
// Not much can be checked about the archives...
|
||||||
|
REQUIRE(boost::filesystem::exists(outputfname));
|
||||||
|
}
|
||||||
|
}
|
@ -15,4 +15,6 @@ if (WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
|
||||||
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]")
|
set(_catch_args "exclude:[NotWorking]")
|
||||||
|
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
|
||||||
|
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})
|
||||||
|
Loading…
Reference in New Issue
Block a user