Merge branch 'master' into lm_sla_supports_auto
@ -82,6 +82,13 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "OS X Deployment target (SDK version)" FORCE)
|
||||||
|
endif ()
|
||||||
|
message(STATUS "Mac OS deployment target (SDK version): ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
# Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD.
|
# Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD.
|
||||||
add_compile_options(-std=c++11 -Wall -Wno-reorder)
|
add_compile_options(-std=c++11 -Wall -Wno-reorder)
|
||||||
|
4
deps/deps-macos.cmake
vendored
@ -23,8 +23,8 @@ ExternalProject_Add(dep_boost
|
|||||||
variant=release
|
variant=release
|
||||||
threading=multi
|
threading=multi
|
||||||
boost.locale.icu=off
|
boost.locale.icu=off
|
||||||
"cflags=cflags=-fPIC -mmacosx-version-min=${DEPS_OSX_TARGET}"
|
"cflags=-fPIC -mmacosx-version-min=${DEPS_OSX_TARGET}"
|
||||||
"cxxflags=cxxflags=-fPIC -mmacosx-version-min=${DEPS_OSX_TARGET}"
|
"cxxflags=-fPIC -mmacosx-version-min=${DEPS_OSX_TARGET}"
|
||||||
install
|
install
|
||||||
INSTALL_COMMAND "" # b2 does that already
|
INSTALL_COMMAND "" # b2 does that already
|
||||||
)
|
)
|
||||||
|
BIN
resources/icons/move_hover.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/icons/move_off.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/icons/move_on.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_hover.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_off.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_on.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 15 KiB |
BIN
resources/localization/ko_KR/Slic3rPE.mo
Normal file
4679
resources/localization/ko_KR/Slic3rPE.po
Normal file
@ -1,10 +1,10 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <libslic3r.h>
|
#include <libslic3r/libslic3r.h>
|
||||||
#include "TriangleMesh.hpp"
|
#include <libslic3r/TriangleMesh.hpp>
|
||||||
#include "SLABasePool.hpp"
|
#include <libslic3r/SLA/SLABasePool.hpp>
|
||||||
#include "benchmark.h"
|
#include <libnest2d/tools/benchmark.h>
|
||||||
|
|
||||||
const std::string USAGE_STR = {
|
const std::string USAGE_STR = {
|
||||||
"Usage: slabasebed stlfilename.stl"
|
"Usage: slabasebed stlfilename.stl"
|
||||||
@ -28,7 +28,7 @@ int main(const int argc, const char *argv[]) {
|
|||||||
ExPolygons ground_slice;
|
ExPolygons ground_slice;
|
||||||
TriangleMesh basepool;
|
TriangleMesh basepool;
|
||||||
|
|
||||||
sla::ground_layer(model, ground_slice, 0.1f);
|
sla::base_plate(model, ground_slice, 0.1f);
|
||||||
|
|
||||||
bench.start();
|
bench.start();
|
||||||
sla::create_base_pool(ground_slice, basepool);
|
sla::create_base_pool(ground_slice, basepool);
|
||||||
|
104
src/libslic3r/Channel.hpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#ifndef slic3r_Channel_hpp_
|
||||||
|
#define slic3r_Channel_hpp_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <utility>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
template<class T> class Channel
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using UniqueLock = std::unique_lock<std::mutex>;
|
||||||
|
using Queue = std::deque<T>;
|
||||||
|
public:
|
||||||
|
class Guard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {}
|
||||||
|
Guard(const Guard &other) = delete;
|
||||||
|
Guard(Guard &&other) = delete;
|
||||||
|
~Guard() {}
|
||||||
|
|
||||||
|
// Access trampolines
|
||||||
|
size_t size() const noexcept { return m_queue.size(); }
|
||||||
|
bool empty() const noexcept { return m_queue.empty(); }
|
||||||
|
typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); }
|
||||||
|
typename Queue::const_iterator end() const noexcept { return m_queue.end(); }
|
||||||
|
typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; }
|
||||||
|
|
||||||
|
Guard& operator=(const Guard &other) = delete;
|
||||||
|
Guard& operator=(Guard &&other) = delete;
|
||||||
|
private:
|
||||||
|
UniqueLock m_lock;
|
||||||
|
const Queue &m_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Channel() {}
|
||||||
|
~Channel() {}
|
||||||
|
|
||||||
|
void push(const T& item, bool silent = false)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
UniqueLock lock(m_mutex);
|
||||||
|
m_queue.push_back(item);
|
||||||
|
}
|
||||||
|
if (! silent) { m_condition.notify_one(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T &&item, bool silent = false)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
UniqueLock lock(m_mutex);
|
||||||
|
m_queue.push_back(std::forward(item));
|
||||||
|
}
|
||||||
|
if (! silent) { m_condition.notify_one(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop()
|
||||||
|
{
|
||||||
|
UniqueLock lock(m_mutex);
|
||||||
|
m_condition.wait(lock, [this]() { return !m_queue.empty(); });
|
||||||
|
auto item = std::move(m_queue.front());
|
||||||
|
m_queue.pop_front();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<T> try_pop()
|
||||||
|
{
|
||||||
|
UniqueLock lock(m_mutex);
|
||||||
|
if (m_queue.empty()) {
|
||||||
|
return boost::none;
|
||||||
|
} else {
|
||||||
|
auto item = std::move(m_queue.front());
|
||||||
|
m_queue.pop();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlocked observers
|
||||||
|
// Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock!
|
||||||
|
size_t size() const noexcept { return m_queue.size(); }
|
||||||
|
bool empty() const noexcept { return m_queue.empty(); }
|
||||||
|
|
||||||
|
Guard read() const
|
||||||
|
{
|
||||||
|
return Guard(UniqueLock(m_mutex), m_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Queue m_queue;
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::condition_variable m_condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_Channel_hpp_
|
@ -336,7 +336,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
|
|||||||
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
|
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::setenv_()
|
void ConfigBase::setenv_() const
|
||||||
{
|
{
|
||||||
t_config_option_keys opt_keys = this->keys();
|
t_config_option_keys opt_keys = this->keys();
|
||||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
||||||
|
@ -1113,7 +1113,7 @@ public:
|
|||||||
|
|
||||||
double get_abs_value(const t_config_option_key &opt_key) const;
|
double get_abs_value(const t_config_option_key &opt_key) const;
|
||||||
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
||||||
void setenv_();
|
void setenv_() const;
|
||||||
void load(const std::string &file);
|
void load(const std::string &file);
|
||||||
void load_from_ini(const std::string &file);
|
void load_from_ini(const std::string &file);
|
||||||
void load_from_gcode_file(const std::string &file);
|
void load_from_gcode_file(const std::string &file);
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
#endif /* SLIC3R_GUI */
|
#endif /* SLIC3R_GUI */
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
#include "EdgeGrid.hpp"
|
#include "EdgeGrid.hpp"
|
||||||
|
#include "SVG.hpp"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Enable debugging and assert in this file.
|
// Enable debugging and assert in this file.
|
||||||
@ -756,8 +758,8 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||||||
float search_radius = float(m_resolution<<1);
|
float search_radius = float(m_resolution<<1);
|
||||||
m_signed_distance_field.assign(nrows * ncols, search_radius);
|
m_signed_distance_field.assign(nrows * ncols, search_radius);
|
||||||
// For each cell:
|
// For each cell:
|
||||||
for (size_t r = 0; r < m_rows; ++ r) {
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
for (size_t c = 0; c < m_cols; ++ c) {
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
const Cell &cell = m_cells[r * m_cols + c];
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
// For each segment in the cell:
|
// For each segment in the cell:
|
||||||
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
@ -842,6 +844,8 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||||||
#if 0
|
#if 0
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
++ iRun;
|
++ iRun;
|
||||||
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
|
wxImage::AddHandler(new wxPNGHandler);
|
||||||
//#ifdef SLIC3R_GUI
|
//#ifdef SLIC3R_GUI
|
||||||
{
|
{
|
||||||
wxImage img(ncols, nrows);
|
wxImage img(ncols, nrows);
|
||||||
@ -1356,9 +1360,101 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int segments_could_intersect(
|
||||||
|
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
|
||||||
|
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
|
||||||
|
{
|
||||||
|
Vec2i64 iv = (ip2 - ip1).cast<int64_t>();
|
||||||
|
Vec2i64 vij1 = (jp1 - ip1).cast<int64_t>();
|
||||||
|
Vec2i64 vij2 = (jp2 - ip1).cast<int64_t>();
|
||||||
|
int64_t tij1 = cross2(iv, vij1);
|
||||||
|
int64_t tij2 = cross2(iv, vij2);
|
||||||
|
int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum
|
||||||
|
int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0);
|
||||||
|
return sij1 * sij2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool segments_intersect(
|
||||||
|
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
|
||||||
|
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
|
||||||
|
{
|
||||||
|
return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 &&
|
||||||
|
segments_could_intersect(jp1, jp2, ip1, ip2) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<ContourEdge, ContourEdge>> out;
|
||||||
|
// For each cell:
|
||||||
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
|
// For each pair of segments in the cell:
|
||||||
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
|
const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
|
||||||
|
size_t ipt = m_cell_data[i].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &ip1 = ipts[ipt];
|
||||||
|
const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
|
||||||
|
for (size_t j = i + 1; j != cell.end; ++ j) {
|
||||||
|
const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
|
||||||
|
size_t jpt = m_cell_data[j].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &jp1 = jpts[jpt];
|
||||||
|
const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
|
||||||
|
if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2))
|
||||||
|
// Segments of the same contour share a common vertex.
|
||||||
|
continue;
|
||||||
|
if (segments_intersect(ip1, ip2, jp1, jp2)) {
|
||||||
|
// The two segments intersect. Add them to the output.
|
||||||
|
int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt);
|
||||||
|
out.emplace_back(jfirst ?
|
||||||
|
std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) :
|
||||||
|
std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Slic3r::sort_remove_duplicates(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeGrid::Grid::has_intersecting_edges() const
|
||||||
|
{
|
||||||
|
// For each cell:
|
||||||
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
|
// For each pair of segments in the cell:
|
||||||
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
|
const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
|
||||||
|
size_t ipt = m_cell_data[i].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &ip1 = ipts[ipt];
|
||||||
|
const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
|
||||||
|
for (size_t j = i + 1; j != cell.end; ++ j) {
|
||||||
|
const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
|
||||||
|
size_t jpt = m_cell_data[j].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &jp1 = jpts[jpt];
|
||||||
|
const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
|
||||||
|
if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) &&
|
||||||
|
segments_intersect(ip1, ip2, jp1, jp2))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
||||||
{
|
{
|
||||||
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
|
wxImage::AddHandler(new wxPNGHandler);
|
||||||
|
|
||||||
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
||||||
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
||||||
wxImage img(w, h);
|
wxImage img(w, h);
|
||||||
@ -1450,4 +1546,59 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
|
|||||||
}
|
}
|
||||||
#endif /* SLIC3R_GUI */
|
#endif /* SLIC3R_GUI */
|
||||||
|
|
||||||
|
// Find all pairs of intersectiong edges from the set of polygons.
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons)
|
||||||
|
{
|
||||||
|
double len = 0;
|
||||||
|
size_t cnt = 0;
|
||||||
|
BoundingBox bbox;
|
||||||
|
for (const Polygon &poly : polygons) {
|
||||||
|
if (poly.points.size() < 2)
|
||||||
|
continue;
|
||||||
|
for (size_t i = 0; i < poly.points.size(); ++ i) {
|
||||||
|
bbox.merge(poly.points[i]);
|
||||||
|
size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1;
|
||||||
|
len += (poly.points[j] - poly.points[i]).cast<double>().norm();
|
||||||
|
++ cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len /= double(cnt);
|
||||||
|
bbox.offset(20);
|
||||||
|
EdgeGrid::Grid grid;
|
||||||
|
grid.set_bbox(bbox);
|
||||||
|
grid.create(polygons, len);
|
||||||
|
return grid.intersecting_edges();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG.
|
||||||
|
void export_intersections_to_svg(const std::string &filename, const Polygons &polygons)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = intersecting_edges(polygons);
|
||||||
|
BoundingBox bbox = get_extents(polygons);
|
||||||
|
SVG svg(filename.c_str(), bbox);
|
||||||
|
svg.draw(union_ex(polygons), "gray", 0.25f);
|
||||||
|
svg.draw_outline(polygons, "black");
|
||||||
|
std::set<const Points*> intersecting_contours;
|
||||||
|
for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &ie : intersections) {
|
||||||
|
intersecting_contours.insert(ie.first.first);
|
||||||
|
intersecting_contours.insert(ie.second.first);
|
||||||
|
}
|
||||||
|
// Highlight the contours with intersections.
|
||||||
|
coord_t line_width = coord_t(scale_(0.01));
|
||||||
|
for (const Points *ic : intersecting_contours) {
|
||||||
|
svg.draw_outline(Polygon(*ic), "green");
|
||||||
|
svg.draw_outline(Polygon(*ic), "black", line_width);
|
||||||
|
}
|
||||||
|
// Paint the intersections.
|
||||||
|
for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &intersecting_edges : intersections) {
|
||||||
|
auto edge = [](const EdgeGrid::Grid::ContourEdge &e) {
|
||||||
|
return Line(e.first->at(e.second),
|
||||||
|
e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1));
|
||||||
|
};
|
||||||
|
svg.draw(edge(intersecting_edges.first), "red", line_width);
|
||||||
|
svg.draw(edge(intersecting_edges.second), "red", line_width);
|
||||||
|
}
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -133,7 +133,7 @@ void FillGyroid::_fill_surface_single(
|
|||||||
// no rotation is supported for this infill pattern (yet)
|
// no rotation is supported for this infill pattern (yet)
|
||||||
BoundingBox bb = expolygon.contour.bounding_box();
|
BoundingBox bb = expolygon.contour.bounding_box();
|
||||||
// Density adjusted to have a good %of weight.
|
// Density adjusted to have a good %of weight.
|
||||||
double density_adjusted = std::max(0., params.density * 2.);
|
double density_adjusted = std::max(0., params.density * 2.44);
|
||||||
// Distance between the gyroid waves in scaled coordinates.
|
// Distance between the gyroid waves in scaled coordinates.
|
||||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "PostProcessor.hpp"
|
#include "PostProcessor.hpp"
|
||||||
|
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -25,9 +27,10 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||||||
{
|
{
|
||||||
if (config.post_process.values.empty())
|
if (config.post_process.values.empty())
|
||||||
return;
|
return;
|
||||||
//config.setenv_();
|
|
||||||
|
config.setenv_();
|
||||||
auto gcode_file = boost::filesystem::path(path);
|
auto gcode_file = boost::filesystem::path(path);
|
||||||
if (!boost::filesystem::exists(gcode_file))
|
if (! boost::filesystem::exists(gcode_file))
|
||||||
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
|
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
|
||||||
|
|
||||||
for (std::string script: config.post_process.values) {
|
for (std::string script: config.post_process.values) {
|
||||||
|
@ -545,6 +545,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||||||
m_print_brim = true;
|
m_print_brim = true;
|
||||||
|
|
||||||
// Ask our writer about how much material was consumed:
|
// Ask our writer about how much material was consumed:
|
||||||
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
@ -698,6 +699,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||||||
m_print_brim = false; // Mark the brim as extruded
|
m_print_brim = false; // Mark the brim as extruded
|
||||||
|
|
||||||
// Ask our writer about how much material was consumed:
|
// Ask our writer about how much material was consumed:
|
||||||
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
@ -868,6 +870,7 @@ void WipeTowerPrusaMM::toolchange_Change(
|
|||||||
material_type new_material)
|
material_type new_material)
|
||||||
{
|
{
|
||||||
// Ask the writer about how much of the old filament we consumed:
|
// Ask the writer about how much of the old filament we consumed:
|
||||||
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
// Speed override for the material. Go slow for flex and soluble materials.
|
// Speed override for the material. Go slow for flex and soluble materials.
|
||||||
|
@ -521,10 +521,14 @@ void Model::adjust_min_z()
|
|||||||
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
|
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
|
||||||
{
|
{
|
||||||
unsigned int id = s_auto_extruder_id;
|
unsigned int id = s_auto_extruder_id;
|
||||||
|
if (id > max_extruders) {
|
||||||
if (++s_auto_extruder_id > max_extruders)
|
// The current counter is invalid, likely due to switching the printer profiles
|
||||||
|
// to a profile with a lower number of extruders.
|
||||||
reset_auto_extruder_id();
|
reset_auto_extruder_id();
|
||||||
|
id = s_auto_extruder_id;
|
||||||
|
} else if (++ s_auto_extruder_id > max_extruders) {
|
||||||
|
reset_auto_extruder_id();
|
||||||
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,23 @@ extern BoundingBox get_extents(const MultiPoint &mp);
|
|||||||
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
|
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
|
||||||
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
|
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
|
||||||
|
|
||||||
|
inline double length(const Points &pts) {
|
||||||
|
double total = 0;
|
||||||
|
if (! pts.empty()) {
|
||||||
|
auto it = pts.begin();
|
||||||
|
for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev)
|
||||||
|
total += (*it - *it_prev).cast<double>().norm();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double area(const Points &polygon) {
|
||||||
|
double area = 0.;
|
||||||
|
for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++)
|
||||||
|
area += double(polygon[i](0) + polygon[j](0)) * double(polygon[i](1) - polygon[j](1));
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -187,6 +187,24 @@ public:
|
|||||||
m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value)));
|
m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Erase a data point equal to value. (ValueType has to declare the operator==).
|
||||||
|
// Returns true if the data point equal to value was found and removed.
|
||||||
|
bool erase(const ValueType &value) {
|
||||||
|
const Point *pt = m_point_accessor(value);
|
||||||
|
if (pt != nullptr) {
|
||||||
|
// Range of fragment starts around grid_corner, close to pt.
|
||||||
|
auto range = m_map.equal_range(Point((*pt)(0)>>m_grid_log2, (*pt)(1)>>m_grid_log2));
|
||||||
|
// Remove the first item.
|
||||||
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
|
if (it->second == value) {
|
||||||
|
m_map.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Return a pair of <ValueType*, distance_squared>
|
// Return a pair of <ValueType*, distance_squared>
|
||||||
std::pair<const ValueType*, double> find(const Vec2crd &pt) {
|
std::pair<const ValueType*, double> find(const Vec2crd &pt) {
|
||||||
// Iterate over 4 closest grid cells around pt,
|
// Iterate over 4 closest grid cells around pt,
|
||||||
@ -214,7 +232,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (value_min != nullptr && dist_min < coordf_t(m_search_radius * m_search_radius)) ?
|
return (value_min != nullptr && dist_min < coordf_t(m_search_radius) * coordf_t(m_search_radius)) ?
|
||||||
std::make_pair(value_min, dist_min) :
|
std::make_pair(value_min, dist_min) :
|
||||||
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,8 @@ extern BoundingBox get_extents(const Polylines &polylines);
|
|||||||
|
|
||||||
inline double total_length(const Polylines &polylines) {
|
inline double total_length(const Polylines &polylines) {
|
||||||
double total = 0;
|
double total = 0;
|
||||||
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
for (const Polyline &pl : polylines)
|
||||||
total += it->length();
|
total += pl.length();
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "PrintExport.hpp"
|
#include "PrintExport.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
//! macro used to mark string used at localization,
|
//! macro used to mark string used at localization,
|
||||||
//! return same string
|
//! return same string
|
||||||
#define L(s) Slic3r::I18N::translate(s)
|
#define L(s) Slic3r::I18N::translate(s)
|
||||||
@ -281,16 +283,17 @@ bool Print::is_step_done(PrintObjectStep step) const
|
|||||||
std::vector<unsigned int> Print::object_extruders() const
|
std::vector<unsigned int> Print::object_extruders() const
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
|
extruders.reserve(m_regions.size() * 3);
|
||||||
|
|
||||||
for (PrintRegion* region : m_regions) {
|
for (const PrintRegion *region : m_regions) {
|
||||||
// these checks reflect the same logic used in the GUI for enabling/disabling
|
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||||
// extruder selection fields
|
// extruder selection fields
|
||||||
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
||||||
extruders.push_back(region->config().perimeter_extruder - 1);
|
extruders.emplace_back(region->config().perimeter_extruder - 1);
|
||||||
if (region->config().fill_density.value > 0)
|
if (region->config().fill_density.value > 0)
|
||||||
extruders.push_back(region->config().infill_extruder - 1);
|
extruders.emplace_back(region->config().infill_extruder - 1);
|
||||||
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
||||||
extruders.push_back(region->config().solid_infill_extruder - 1);
|
extruders.emplace_back(region->config().solid_infill_extruder - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_remove_duplicates(extruders);
|
sort_remove_duplicates(extruders);
|
||||||
@ -480,14 +483,6 @@ bool Print::apply_config(DynamicPrintConfig config)
|
|||||||
PrintObjectConfig new_config = this->default_object_config();
|
PrintObjectConfig new_config = this->default_object_config();
|
||||||
// we override the new config with object-specific options
|
// we override the new config with object-specific options
|
||||||
normalize_and_apply_config(new_config, object->model_object()->config);
|
normalize_and_apply_config(new_config, object->model_object()->config);
|
||||||
// Force a refresh of a variable layer height profile at the PrintObject if it is not valid.
|
|
||||||
if (! object->layer_height_profile_valid) {
|
|
||||||
// The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change).
|
|
||||||
// Invalidate the slicing step, which in turn invalidates everything.
|
|
||||||
object->invalidate_step(posSlice);
|
|
||||||
// Trigger recalculation.
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
// check whether the new config is different from the current one
|
// check whether the new config is different from the current one
|
||||||
t_config_option_keys diff = object->config().diff(new_config);
|
t_config_option_keys diff = object->config().diff(new_config);
|
||||||
object->config_apply_only(new_config, diff, true);
|
object->config_apply_only(new_config, diff, true);
|
||||||
@ -567,7 +562,6 @@ exit_for_rearrange_regions:
|
|||||||
|
|
||||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
if (! object->layer_height_profile_valid)
|
|
||||||
object->update_layer_height_profile();
|
object->update_layer_height_profile();
|
||||||
|
|
||||||
return invalidated;
|
return invalidated;
|
||||||
@ -1141,6 +1135,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
if (! object->layer_height_profile_valid)
|
if (! object->layer_height_profile_valid)
|
||||||
|
// No need to call the next line as the step should already be invalidated above.
|
||||||
|
// update_apply_status(object->invalidate_step(posSlice));
|
||||||
object->update_layer_height_profile();
|
object->update_layer_height_profile();
|
||||||
|
|
||||||
//FIXME there may be a race condition with the G-code export running at the background thread.
|
//FIXME there may be a race condition with the G-code export running at the background thread.
|
||||||
@ -1165,6 +1161,7 @@ bool Print::has_skirt() const
|
|||||||
|| this->has_infinite_skirt();
|
|| this->has_infinite_skirt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||||
std::string Print::validate() const
|
std::string Print::validate() const
|
||||||
{
|
{
|
||||||
if (m_objects.empty())
|
if (m_objects.empty())
|
||||||
@ -1231,8 +1228,8 @@ std::string Print::validate() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
||||||
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfMarlin)
|
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
|
||||||
return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.");
|
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
|
||||||
if (! m_config.use_relative_e_distances)
|
if (! m_config.use_relative_e_distances)
|
||||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||||
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||||
@ -1253,12 +1250,10 @@ std::string Print::validate() const
|
|||||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||||
if (! equal_layering(slicing_params, slicing_params0))
|
if (! equal_layering(slicing_params, slicing_params0))
|
||||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||||
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
|
|
||||||
object->update_layer_height_profile();
|
|
||||||
object->layer_height_profile_valid = was_layer_height_profile_valid;
|
|
||||||
|
|
||||||
if ( m_config.variable_layer_height ) { // comparing layer height profiles
|
if ( m_config.variable_layer_height ) { // comparing layer height profiles
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
// layer_height_profile should be set by Print::apply().
|
||||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
|
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
||||||
@ -1867,5 +1862,96 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion
|
|||||||
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
|
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
std::string Print::output_filename() const
|
||||||
|
{
|
||||||
|
// Set the placeholders for the data know first after the G-code export is finished.
|
||||||
|
// These values will be just propagated into the output file name.
|
||||||
|
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||||||
|
return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
|
||||||
|
// and removing spaces.
|
||||||
|
static std::string short_time(const std::string &time)
|
||||||
|
{
|
||||||
|
// Parse the dhms time format.
|
||||||
|
int days = 0;
|
||||||
|
int hours = 0;
|
||||||
|
int minutes = 0;
|
||||||
|
int seconds = 0;
|
||||||
|
if (time.find('d') != std::string::npos)
|
||||||
|
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
|
||||||
|
else if (time.find('h') != std::string::npos)
|
||||||
|
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
|
||||||
|
else if (time.find('m') != std::string::npos)
|
||||||
|
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
|
||||||
|
else if (time.find('s') != std::string::npos)
|
||||||
|
::sscanf(time.c_str(), "%ds", &seconds);
|
||||||
|
// Round to full minutes.
|
||||||
|
if (days + hours + minutes > 0 && seconds >= 30) {
|
||||||
|
if (++ minutes == 60) {
|
||||||
|
minutes = 0;
|
||||||
|
if (++ hours == 24) {
|
||||||
|
hours = 0;
|
||||||
|
++ days;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Format the dhm time.
|
||||||
|
char buffer[64];
|
||||||
|
if (days > 0)
|
||||||
|
::sprintf(buffer, "%dd%dh%dm", days, hours, minutes);
|
||||||
|
else if (hours > 0)
|
||||||
|
::sprintf(buffer, "%dh%dm", hours, minutes);
|
||||||
|
else if (minutes > 0)
|
||||||
|
::sprintf(buffer, "%dm", minutes);
|
||||||
|
else
|
||||||
|
::sprintf(buffer, "%ds", seconds);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicConfig PrintStatistics::config() const
|
||||||
|
{
|
||||||
|
DynamicConfig config;
|
||||||
|
std::string normal_print_time = short_time(this->estimated_normal_print_time);
|
||||||
|
std::string silent_print_time = short_time(this->estimated_silent_print_time);
|
||||||
|
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||||||
|
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||||||
|
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||||||
|
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament));
|
||||||
|
config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume));
|
||||||
|
config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost));
|
||||||
|
config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight));
|
||||||
|
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat (this->total_wipe_tower_cost));
|
||||||
|
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat (this->total_wipe_tower_filament));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicConfig PrintStatistics::placeholders()
|
||||||
|
{
|
||||||
|
DynamicConfig config;
|
||||||
|
for (const std::string &key : {
|
||||||
|
"print_time", "normal_print_time", "silent_print_time",
|
||||||
|
"used_filament", "extruded_volume", "total_cost", "total_weight",
|
||||||
|
"total_wipe_tower_cost", "total_wipe_tower_filament"})
|
||||||
|
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrintStatistics::finalize_output_path(const std::string &path_in) const
|
||||||
|
{
|
||||||
|
std::string final_path;
|
||||||
|
try {
|
||||||
|
boost::filesystem::path path(path_in);
|
||||||
|
DynamicConfig cfg = this->config();
|
||||||
|
PlaceholderParser pp;
|
||||||
|
std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
|
||||||
|
final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
|
||||||
|
final_path = path_in;
|
||||||
|
}
|
||||||
|
return final_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
@ -78,7 +78,6 @@ private: // Prevents erroneous use by other classes.
|
|||||||
public:
|
public:
|
||||||
// vector of (vectors of volume ids), indexed by region_id
|
// vector of (vectors of volume ids), indexed by region_id
|
||||||
std::vector<std::vector<int>> region_volumes;
|
std::vector<std::vector<int>> region_volumes;
|
||||||
t_layer_height_ranges layer_height_ranges;
|
|
||||||
|
|
||||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||||
@ -250,6 +249,13 @@ struct PrintStatistics
|
|||||||
double total_wipe_tower_filament;
|
double total_wipe_tower_filament;
|
||||||
std::map<size_t, float> filament_stats;
|
std::map<size_t, float> filament_stats;
|
||||||
|
|
||||||
|
// Config with the filled in print statistics.
|
||||||
|
DynamicConfig config() const;
|
||||||
|
// Config with the statistics keys populated with placeholder strings.
|
||||||
|
static DynamicConfig placeholders();
|
||||||
|
// Replace the print statistics placeholders in the path.
|
||||||
|
std::string finalize_output_path(const std::string &path_in) const;
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
estimated_normal_print_time.clear();
|
estimated_normal_print_time.clear();
|
||||||
estimated_silent_print_time.clear();
|
estimated_silent_print_time.clear();
|
||||||
@ -298,7 +304,10 @@ public:
|
|||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||||
|
// Returns true if an object step is done on all objects and there's at least one object.
|
||||||
bool is_step_done(PrintObjectStep step) const;
|
bool is_step_done(PrintObjectStep step) const;
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
bool finished() const override { return this->is_step_done(psGCodeExport); }
|
||||||
|
|
||||||
bool has_infinite_skirt() const;
|
bool has_infinite_skirt() const;
|
||||||
bool has_skirt() const;
|
bool has_skirt() const;
|
||||||
@ -343,8 +352,7 @@ public:
|
|||||||
bool has_wipe_tower() const;
|
bool has_wipe_tower() const;
|
||||||
const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; }
|
const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; }
|
||||||
|
|
||||||
std::string output_filename() const override
|
std::string output_filename() const override;
|
||||||
{ return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode"); }
|
|
||||||
|
|
||||||
// Accessed by SupportMaterial
|
// Accessed by SupportMaterial
|
||||||
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
|
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
|
||||||
|
@ -48,12 +48,14 @@ void PrintBase::update_object_placeholders()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext) const
|
std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext, const DynamicConfig *config_override) const
|
||||||
{
|
{
|
||||||
DynamicConfig cfg_timestamp;
|
DynamicConfig cfg;
|
||||||
PlaceholderParser::update_timestamp(cfg_timestamp);
|
if (config_override != nullptr)
|
||||||
|
cfg = *config_override;
|
||||||
|
PlaceholderParser::update_timestamp(cfg);
|
||||||
try {
|
try {
|
||||||
boost::filesystem::path filename = this->placeholder_parser().process(format, 0, &cfg_timestamp);
|
boost::filesystem::path filename = this->placeholder_parser().process(format, 0, &cfg);
|
||||||
if (filename.extension().empty())
|
if (filename.extension().empty())
|
||||||
filename = boost::filesystem::change_extension(filename, default_ext);
|
filename = boost::filesystem::change_extension(filename, default_ext);
|
||||||
return filename.string();
|
return filename.string();
|
||||||
|
@ -285,6 +285,8 @@ public:
|
|||||||
void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
|
void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
|
||||||
// Cancel the running computation. Stop execution of all the background threads.
|
// Cancel the running computation. Stop execution of all the background threads.
|
||||||
void restart() { m_cancel_status = NOT_CANCELED; }
|
void restart() { m_cancel_status = NOT_CANCELED; }
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
virtual bool finished() const = 0;
|
||||||
|
|
||||||
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
|
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
|
||||||
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
||||||
@ -305,7 +307,7 @@ protected:
|
|||||||
void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }
|
void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); }
|
||||||
|
|
||||||
// To be called by this->output_filename() with the format string pulled from the configuration layer.
|
// To be called by this->output_filename() with the format string pulled from the configuration layer.
|
||||||
std::string output_filename(const std::string &format, const std::string &default_ext) const;
|
std::string output_filename(const std::string &format, const std::string &default_ext, const DynamicConfig *config_override = nullptr) const;
|
||||||
// Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects.
|
// Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects.
|
||||||
void update_object_placeholders();
|
void update_object_placeholders();
|
||||||
|
|
||||||
|
@ -1297,10 +1297,11 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->default_value = new ConfigOptionString("");
|
def->default_value = new ConfigOptionString("");
|
||||||
|
|
||||||
def = this->add("printhost_cafile", coString);
|
def = this->add("printhost_cafile", coString);
|
||||||
def->label = "HTTPS CA file";
|
def->label = "HTTPS CA File";
|
||||||
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
|
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
|
||||||
"If left blank, the default OS CA certificate repository is used.";
|
"If left blank, the default OS CA certificate repository is used.";
|
||||||
def->cli = "printhost-cafile=s";
|
def->cli = "printhost-cafile=s";
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionString("");
|
def->default_value = new ConfigOptionString("");
|
||||||
|
|
||||||
def = this->add("print_host", coString);
|
def = this->add("print_host", coString);
|
||||||
@ -2388,6 +2389,15 @@ void PrintConfigDef::init_sla_params()
|
|||||||
def->min = 100;
|
def->min = 100;
|
||||||
def->default_value = new ConfigOptionInt(1440);
|
def->default_value = new ConfigOptionInt(1440);
|
||||||
|
|
||||||
|
def = this->add("display_orientation", coEnum);
|
||||||
|
def->label = L("Display orientation");
|
||||||
|
def->tooltip = L("Display orientation");
|
||||||
|
def->cli = "display-orientation=s";
|
||||||
|
def->enum_keys_map = &ConfigOptionEnum<SLADisplayOrientation>::get_enum_values();
|
||||||
|
def->enum_values.push_back("Landscape");
|
||||||
|
def->enum_values.push_back("Portrait");
|
||||||
|
def->default_value = new ConfigOptionEnum<SLADisplayOrientation>(sladoPortrait);
|
||||||
|
|
||||||
def = this->add("printer_correction", coFloats);
|
def = this->add("printer_correction", coFloats);
|
||||||
def->full_label = L("Printer scaling correction");
|
def->full_label = L("Printer scaling correction");
|
||||||
def->tooltip = L("Printer scaling correction");
|
def->tooltip = L("Printer scaling correction");
|
||||||
|
@ -56,6 +56,11 @@ enum FilamentType {
|
|||||||
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
|
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SLADisplayOrientation {
|
||||||
|
sladoLandscape,
|
||||||
|
sladoPortrait
|
||||||
|
};
|
||||||
|
|
||||||
template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
|
template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
|
||||||
static t_config_enum_values keys_map;
|
static t_config_enum_values keys_map;
|
||||||
if (keys_map.empty()) {
|
if (keys_map.empty()) {
|
||||||
@ -148,6 +153,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::ge
|
|||||||
return keys_map;
|
return keys_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> inline const t_config_enum_values& ConfigOptionEnum<SLADisplayOrientation>::get_enum_values() {
|
||||||
|
static const t_config_enum_values keys_map = {
|
||||||
|
{ "Landscape", sladoLandscape},
|
||||||
|
{ "Portrait", sladoPortrait}
|
||||||
|
};
|
||||||
|
|
||||||
|
return keys_map;
|
||||||
|
}
|
||||||
|
|
||||||
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
|
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
|
||||||
// Does not store the actual values, but defines default values.
|
// Does not store the actual values, but defines default values.
|
||||||
class PrintConfigDef : public ConfigDef
|
class PrintConfigDef : public ConfigDef
|
||||||
@ -1035,6 +1049,7 @@ public:
|
|||||||
ConfigOptionFloat display_height;
|
ConfigOptionFloat display_height;
|
||||||
ConfigOptionInt display_pixels_x;
|
ConfigOptionInt display_pixels_x;
|
||||||
ConfigOptionInt display_pixels_y;
|
ConfigOptionInt display_pixels_y;
|
||||||
|
ConfigOptionEnum<SLADisplayOrientation> display_orientation;
|
||||||
ConfigOptionFloats printer_correction;
|
ConfigOptionFloats printer_correction;
|
||||||
protected:
|
protected:
|
||||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||||
@ -1046,6 +1061,7 @@ protected:
|
|||||||
OPT_PTR(display_height);
|
OPT_PTR(display_height);
|
||||||
OPT_PTR(display_pixels_x);
|
OPT_PTR(display_pixels_x);
|
||||||
OPT_PTR(display_pixels_y);
|
OPT_PTR(display_pixels_y);
|
||||||
|
OPT_PTR(display_orientation);
|
||||||
OPT_PTR(printer_correction);
|
OPT_PTR(printer_correction);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -31,8 +31,6 @@ template<FilePrinterFormat format>
|
|||||||
class FilePrinter {
|
class FilePrinter {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void print_config(const Print&);
|
|
||||||
|
|
||||||
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
||||||
void draw_polygon(const ExPolygon& p, unsigned lyr);
|
void draw_polygon(const ExPolygon& p, unsigned lyr);
|
||||||
|
|
||||||
@ -118,6 +116,7 @@ template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
|
|||||||
Raster::PixelDim m_pxdim;
|
Raster::PixelDim m_pxdim;
|
||||||
double m_exp_time_s = .0, m_exp_time_first_s = .0;
|
double m_exp_time_s = .0, m_exp_time_first_s = .0;
|
||||||
double m_layer_height = .0;
|
double m_layer_height = .0;
|
||||||
|
Raster::Origin m_o = Raster::Origin::TOP_LEFT;
|
||||||
|
|
||||||
std::string createIniContent(const std::string& projectname) {
|
std::string createIniContent(const std::string& projectname) {
|
||||||
double layer_height = m_layer_height;
|
double layer_height = m_layer_height;
|
||||||
@ -147,19 +146,41 @@ template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
|
|||||||
+layerh_str+"+printer=DWARF3\n";
|
+layerh_str+"+printer=DWARF3\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change this to TOP_LEFT if you want correct PNG orientation
|
|
||||||
static const Raster::Origin ORIGIN = Raster::Origin::BOTTOM_LEFT;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum RasterOrientation {
|
||||||
|
RO_LANDSCAPE,
|
||||||
|
RO_PORTRAIT
|
||||||
|
};
|
||||||
|
|
||||||
|
// We will play with the raster's coordinate origin parameter. When the
|
||||||
|
// printer should print in landscape mode it should have the Y axis flipped
|
||||||
|
// because the layers should be displayed upside down. PNG has its
|
||||||
|
// coordinate origin in the top-left corner so normally the Raster objects
|
||||||
|
// should be instantiated with the TOP_LEFT flag. However, in landscape mode
|
||||||
|
// we do want the pictures to be upside down so we will make BOTTOM_LEFT
|
||||||
|
// type rasters and the PNG format will do the flipping automatically.
|
||||||
|
|
||||||
|
// In case of portrait images, we have to rotate the image by a 90 degrees
|
||||||
|
// and flip the y axis. To get the correct upside-down orientation of the
|
||||||
|
// slice images, we can flip the x and y coordinates of the input polygons
|
||||||
|
// and do the Y flipping of the image. This will generate the correct
|
||||||
|
// orientation in portrait mode.
|
||||||
|
|
||||||
inline FilePrinter(double width_mm, double height_mm,
|
inline FilePrinter(double width_mm, double height_mm,
|
||||||
unsigned width_px, unsigned height_px,
|
unsigned width_px, unsigned height_px,
|
||||||
double layer_height,
|
double layer_height,
|
||||||
double exp_time, double exp_time_first):
|
double exp_time, double exp_time_first,
|
||||||
|
RasterOrientation ro = RO_PORTRAIT):
|
||||||
m_res(width_px, height_px),
|
m_res(width_px, height_px),
|
||||||
m_pxdim(width_mm/width_px, height_mm/height_px),
|
m_pxdim(width_mm/width_px, height_mm/height_px),
|
||||||
m_exp_time_s(exp_time),
|
m_exp_time_s(exp_time),
|
||||||
m_exp_time_first_s(exp_time_first),
|
m_exp_time_first_s(exp_time_first),
|
||||||
m_layer_height(layer_height)
|
m_layer_height(layer_height),
|
||||||
|
|
||||||
|
// Here is the trick with the orientation.
|
||||||
|
m_o(ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT :
|
||||||
|
Raster::Origin::TOP_LEFT )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,12 +200,12 @@ public:
|
|||||||
|
|
||||||
inline void begin_layer(unsigned lyr) {
|
inline void begin_layer(unsigned lyr) {
|
||||||
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
|
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
|
||||||
m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN);
|
m_layers_rst[lyr].first.reset(m_res, m_pxdim, m_o);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void begin_layer() {
|
inline void begin_layer() {
|
||||||
m_layers_rst.emplace_back();
|
m_layers_rst.emplace_back();
|
||||||
m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN);
|
m_layers_rst.front().first.reset(m_res, m_pxdim, m_o);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void finish_layer(unsigned lyr_id) {
|
inline void finish_layer(unsigned lyr_id) {
|
||||||
@ -206,29 +227,35 @@ public:
|
|||||||
inline void save(const std::string& path) {
|
inline void save(const std::string& path) {
|
||||||
try {
|
try {
|
||||||
LayerWriter<LyrFmt> writer(path);
|
LayerWriter<LyrFmt> writer(path);
|
||||||
|
if(!writer.is_ok()) return;
|
||||||
|
|
||||||
std::string project = writer.get_name();
|
std::string project = writer.get_name();
|
||||||
|
|
||||||
writer.next_entry("config.ini");
|
writer.next_entry("config.ini");
|
||||||
|
if(!writer.is_ok()) return;
|
||||||
|
|
||||||
writer << createIniContent(project);
|
writer << createIniContent(project);
|
||||||
|
|
||||||
for(unsigned i = 0; i < m_layers_rst.size(); i++) {
|
for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++)
|
||||||
|
{
|
||||||
if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
|
if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
|
||||||
char lyrnum[6];
|
char lyrnum[6];
|
||||||
std::sprintf(lyrnum, "%.5d", i);
|
std::sprintf(lyrnum, "%.5d", i);
|
||||||
auto zfilename = project + lyrnum + ".png";
|
auto zfilename = project + lyrnum + ".png";
|
||||||
writer.next_entry(zfilename);
|
writer.next_entry(zfilename);
|
||||||
|
|
||||||
|
if(!writer.is_ok()) break;
|
||||||
|
|
||||||
writer << m_layers_rst[i].second.str();
|
writer << m_layers_rst[i].second.str();
|
||||||
// writer << m_layers_rst[i].second.rdbuf();
|
// writer << m_layers_rst[i].second.rdbuf();
|
||||||
// we can keep the date for later calls of this method
|
// we can keep the date for later calls of this method
|
||||||
//m_layers_rst[i].second.str("");
|
//m_layers_rst[i].second.str("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.close();
|
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||||
return;
|
// Rethrow the exception
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||||||
this->set_copies(copies);
|
this->set_copies(copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->layer_height_ranges = model_object->layer_height_ranges;
|
|
||||||
this->layer_height_profile = model_object->layer_height_profile;
|
this->layer_height_profile = model_object->layer_height_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,7 +1108,7 @@ void PrintObject::discover_vertical_shells()
|
|||||||
#if 1
|
#if 1
|
||||||
// Intentionally inflate a bit more than how much the region has been shrunk,
|
// Intentionally inflate a bit more than how much the region has been shrunk,
|
||||||
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
||||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
shell = offset(offset_ex(union_ex(shell), - 0.5f * min_perimeter_infill_spacing), 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
||||||
if (shell.empty())
|
if (shell.empty())
|
||||||
continue;
|
continue;
|
||||||
#else
|
#else
|
||||||
@ -1330,7 +1329,7 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh
|
|||||||
bool updated = false;
|
bool updated = false;
|
||||||
|
|
||||||
// If the layer height profile is not set, try to use the one stored at the ModelObject.
|
// If the layer height profile is not set, try to use the one stored at the ModelObject.
|
||||||
if (layer_height_profile.empty() && layer_height_profile.data() != this->model_object()->layer_height_profile.data()) {
|
if (layer_height_profile.empty()) {
|
||||||
layer_height_profile = this->model_object()->layer_height_profile;
|
layer_height_profile = this->model_object()->layer_height_profile;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
@ -1347,10 +1346,9 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh
|
|||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
if (0)
|
if (0)
|
||||||
// if (this->layer_height_profile.empty())
|
// if (this->layer_height_profile.empty())
|
||||||
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->layer_height_ranges,
|
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes);
|
||||||
this->model_object()->volumes);
|
|
||||||
else
|
else
|
||||||
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->layer_height_ranges);
|
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges);
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -45,15 +45,20 @@ private:
|
|||||||
TRawRenderer m_raw_renderer;
|
TRawRenderer m_raw_renderer;
|
||||||
TRendererAA m_renderer;
|
TRendererAA m_renderer;
|
||||||
Origin m_o;
|
Origin m_o;
|
||||||
std::function<void(agg::path_storage&)> m_flipy = [](agg::path_storage&) {};
|
|
||||||
|
inline void flipy(agg::path_storage& path) const {
|
||||||
|
path.flip_y(0, m_resolution.height_px);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
|
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
|
||||||
Origin o):
|
Origin o):
|
||||||
m_resolution(res), m_pxdim(pd),
|
m_resolution(res), m_pxdim(pd),
|
||||||
m_buf(res.pixels()),
|
m_buf(res.pixels()),
|
||||||
m_rbuf(reinterpret_cast<TPixelRenderer::value_type*>(m_buf.data()),
|
m_rbuf(reinterpret_cast<TPixelRenderer::value_type*>(m_buf.data()),
|
||||||
res.width_px, res.height_px,
|
res.width_px, res.height_px,
|
||||||
res.width_px*TPixelRenderer::num_components),
|
int(res.width_px*TPixelRenderer::num_components)),
|
||||||
m_pixfmt(m_rbuf),
|
m_pixfmt(m_rbuf),
|
||||||
m_raw_renderer(m_pixfmt),
|
m_raw_renderer(m_pixfmt),
|
||||||
m_renderer(m_raw_renderer),
|
m_renderer(m_raw_renderer),
|
||||||
@ -65,10 +70,6 @@ public:
|
|||||||
// ras.gamma(agg::gamma_power(1.0));
|
// ras.gamma(agg::gamma_power(1.0));
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) {
|
|
||||||
path.flip_y(0, m_resolution.height_px);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(const ExPolygon &poly) {
|
void draw(const ExPolygon &poly) {
|
||||||
@ -76,12 +77,14 @@ public:
|
|||||||
agg::scanline_p8 scanlines;
|
agg::scanline_p8 scanlines;
|
||||||
|
|
||||||
auto&& path = to_path(poly.contour);
|
auto&& path = to_path(poly.contour);
|
||||||
m_flipy(path);
|
|
||||||
|
if(m_o == Origin::TOP_LEFT) flipy(path);
|
||||||
|
|
||||||
ras.add_path(path);
|
ras.add_path(path);
|
||||||
|
|
||||||
for(auto h : poly.holes) {
|
for(auto h : poly.holes) {
|
||||||
auto&& holepath = to_path(h);
|
auto&& holepath = to_path(h);
|
||||||
m_flipy(holepath);
|
if(m_o == Origin::TOP_LEFT) flipy(holepath);
|
||||||
ras.add_path(holepath);
|
ras.add_path(holepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +208,9 @@ void Raster::save(std::ostream& stream, Compression comp)
|
|||||||
<< m_impl->resolution().height_px << " "
|
<< m_impl->resolution().height_px << " "
|
||||||
<< "255 ";
|
<< "255 ";
|
||||||
|
|
||||||
|
auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type);
|
||||||
stream.write(reinterpret_cast<const char*>(m_impl->buffer().data()),
|
stream.write(reinterpret_cast<const char*>(m_impl->buffer().data()),
|
||||||
m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type));
|
std::streamsize(sz));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,13 @@ public:
|
|||||||
PNG //!> PNG compression
|
PNG //!> PNG compression
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The Rasterizer expects the input polygons to have their coordinate
|
||||||
|
/// system origin in the bottom left corner. If the raster is then
|
||||||
|
/// configured with the TOP_LEFT origin parameter (in the constructor) than
|
||||||
|
/// it will flip the Y axis in output to maintain the correct orientation.
|
||||||
|
/// This is the default case with PNG images. They have the origin in the
|
||||||
|
/// top left corner. Without the flipping, the image would be upside down
|
||||||
|
/// with the scaled (clipper) coordinate system of the input polygons.
|
||||||
enum class Origin {
|
enum class Origin {
|
||||||
TOP_LEFT,
|
TOP_LEFT,
|
||||||
BOTTOM_LEFT
|
BOTTOM_LEFT
|
||||||
|
186
src/libslic3r/Rasterizer/bicubic.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#ifndef BICUBIC_HPP
|
||||||
|
#define BICUBIC_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
namespace BicubicInternal {
|
||||||
|
// Linear kernel, to be able to test cubic methods with hat kernels.
|
||||||
|
template<typename T>
|
||||||
|
struct LinearKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return T(0.); }
|
||||||
|
static T a01() { return T(0.); }
|
||||||
|
static T a02() { return T(0.); }
|
||||||
|
static T a03() { return T(0.); }
|
||||||
|
static T a10() { return T(1.); }
|
||||||
|
static T a11() { return T(-1.); }
|
||||||
|
static T a12() { return T(0.); }
|
||||||
|
static T a13() { return T(0.); }
|
||||||
|
static T a20() { return T(0.); }
|
||||||
|
static T a21() { return T(1.); }
|
||||||
|
static T a22() { return T(0.); }
|
||||||
|
static T a23() { return T(0.); }
|
||||||
|
static T a30() { return T(0.); }
|
||||||
|
static T a31() { return T(0.); }
|
||||||
|
static T a32() { return T(0.); }
|
||||||
|
static T a33() { return T(0.); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interpolation kernel aka Catmul-Rom aka Keyes kernel.
|
||||||
|
template<typename T>
|
||||||
|
struct CubicCatmulRomKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return 0; }
|
||||||
|
static T a01() { return (T)-0.5; }
|
||||||
|
static T a02() { return (T) 1.; }
|
||||||
|
static T a03() { return (T)-0.5; }
|
||||||
|
static T a10() { return (T) 1.; }
|
||||||
|
static T a11() { return 0; }
|
||||||
|
static T a12() { return (T)-5./2.; }
|
||||||
|
static T a13() { return (T) 3./2.; }
|
||||||
|
static T a20() { return 0; }
|
||||||
|
static T a21() { return (T) 0.5; }
|
||||||
|
static T a22() { return (T) 2.; }
|
||||||
|
static T a23() { return (T)-3./2.; }
|
||||||
|
static T a30() { return 0; }
|
||||||
|
static T a31() { return 0; }
|
||||||
|
static T a32() { return (T)-0.5; }
|
||||||
|
static T a33() { return (T) 0.5; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// B-spline kernel
|
||||||
|
template<typename T>
|
||||||
|
struct CubicBSplineKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return (T) 1./6.; }
|
||||||
|
static T a01() { return (T) -3./6.; }
|
||||||
|
static T a02() { return (T) 3./6.; }
|
||||||
|
static T a03() { return (T) -1./6.; }
|
||||||
|
static T a10() { return (T) 4./6.; }
|
||||||
|
static T a11() { return 0; }
|
||||||
|
static T a12() { return (T) -6./6.; }
|
||||||
|
static T a13() { return (T) 3./6.; }
|
||||||
|
static T a20() { return (T) 1./6.; }
|
||||||
|
static T a21() { return (T) 3./6.; }
|
||||||
|
static T a22() { return (T) 3./6.; }
|
||||||
|
static T a23() { return (T)- 3./6.; }
|
||||||
|
static T a30() { return 0; }
|
||||||
|
static T a31() { return 0; }
|
||||||
|
static T a32() { return 0; }
|
||||||
|
static T a33() { return (T) 1./6.; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T clamp(T a, T lower, T upper)
|
||||||
|
{
|
||||||
|
return (a < lower) ? lower :
|
||||||
|
(a > upper) ? upper : a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KERNEL>
|
||||||
|
struct CubicKernel
|
||||||
|
{
|
||||||
|
typedef typename KERNEL KernelInternal;
|
||||||
|
typedef typename KERNEL::FloatType FloatType;
|
||||||
|
|
||||||
|
static FloatType kernel(FloatType x)
|
||||||
|
{
|
||||||
|
x = fabs(x);
|
||||||
|
if (x >= (FloatType)2.)
|
||||||
|
return 0.0f;
|
||||||
|
if (x <= (FloatType)1.) {
|
||||||
|
FloatType x2 = x * x;
|
||||||
|
FloatType x3 = x2 * x;
|
||||||
|
return KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3;
|
||||||
|
}
|
||||||
|
assert(x > (FloatType)1. && x < (FloatType)2.);
|
||||||
|
x -= (FloatType)1.;
|
||||||
|
FloatType x2 = x * x;
|
||||||
|
FloatType x3 = x2 * x;
|
||||||
|
return KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x)
|
||||||
|
{
|
||||||
|
const FloatType x2 = x*x;
|
||||||
|
const FloatType x3 = x*x*x;
|
||||||
|
return f0*(KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3) +
|
||||||
|
f1*(KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3) +
|
||||||
|
f2*(KERNEL::a20() + KERNEL::a21() * x + KERNEL::a22() * x2 + KERNEL::a23() * x3) +
|
||||||
|
f3*(KERNEL::a30() + KERNEL::a31() * x + KERNEL::a32() * x2 + KERNEL::a33() * x3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Linear splines
|
||||||
|
typedef CubicKernel<BicubicInternal::LinearKernel<float>> LinearKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::LinearKernel<double>> LinearKerneld;
|
||||||
|
// Catmul-Rom splines
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicCatmulRomKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicCatmulRomKerneld;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicInterpolationKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicInterpolationKerneld;
|
||||||
|
// Cubic B-splines
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicBSplineKernel<float>> CubicBSplineKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicBSplineKernel<double>> CubicBSplineKerneld;
|
||||||
|
|
||||||
|
template<typename KERNEL, typename Derived>
|
||||||
|
static float cubic_interpolate(const Eigen::ArrayBase<Derived> &F, const typename KERNEL::FloatType pt, const typename KERNEL::FloatType dx)
|
||||||
|
{
|
||||||
|
typedef typename KERNEL::FloatType T;
|
||||||
|
const int w = int(F.size());
|
||||||
|
const int ix = (int)floor(pt);
|
||||||
|
const T s = pt - (T)ix;
|
||||||
|
|
||||||
|
if (ix > 1 && ix + 2 < w) {
|
||||||
|
// Inside the fully interpolated region.
|
||||||
|
return KERNEL::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s);
|
||||||
|
}
|
||||||
|
// Transition region. Extend with a constant function.
|
||||||
|
auto f = [&F, w](x) { return F[BicubicInternal::clamp(x, 0, w - 1)]; }
|
||||||
|
return KERNEL::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KERNEL, typename Derived>
|
||||||
|
static float bicubic_interpolate(const Eigen::MatrixBase<Derived> &F, const Eigen::Matrix<typename KERNEL::FloatType, 2, 1, Eigen::DontAlign> &pt, const typename KERNEL::FloatType dx)
|
||||||
|
{
|
||||||
|
typedef typename KERNEL::FloatType T;
|
||||||
|
const int w = F.cols();
|
||||||
|
const int h = F.rows();
|
||||||
|
const int ix = (int)floor(pt[0]);
|
||||||
|
const int iy = (int)floor(pt[1]);
|
||||||
|
const T s = pt[0] - (T)ix;
|
||||||
|
const T t = pt[1] - (T)iy;
|
||||||
|
|
||||||
|
if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) {
|
||||||
|
// Inside the fully interpolated region.
|
||||||
|
return KERNEL::interpolate(
|
||||||
|
KERNEL::interpolate(F(ix-1,iy-1),F(ix ,iy-1),F(ix+1,iy-1),F(ix+2,iy-1),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy ),F(ix ,iy ),F(ix+1,iy ),F(ix+2,iy ),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy+1),F(ix ,iy+1),F(ix+1,iy+1),F(ix+2,iy+1),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy+2),F(ix ,iy+2),F(ix+1,iy+2),F(ix+2,iy+2),s),t);
|
||||||
|
}
|
||||||
|
// Transition region. Extend with a constant function.
|
||||||
|
auto f = [&f, w, h](int x, int y) { return F(BicubicInternal::clamp(x,0,w-1),BicubicInternal::clamp(y,0,h-1)); }
|
||||||
|
return KERNEL::interpolate(
|
||||||
|
KERNEL::interpolate(f(ix-1,iy-1),f(ix ,iy-1),f(ix+1,iy-1),f(ix+2,iy-1),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy ),f(ix ,iy ),f(ix+1,iy ),f(ix+2,iy ),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy+1),f(ix ,iy+1),f(ix+1,iy+1),f(ix+2,iy+1),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy+2),f(ix ,iy+2),f(ix+1,iy+2),f(ix+2,iy+2),s),t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* BICUBIC_HPP */
|
@ -31,7 +31,9 @@ Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
||||||
double floor_z_mm, double ceiling_z_mm) {
|
double floor_z_mm, double ceiling_z_mm,
|
||||||
|
ThrowOnCancel thr)
|
||||||
|
{
|
||||||
using std::transform; using std::back_inserter;
|
using std::transform; using std::back_inserter;
|
||||||
|
|
||||||
ExPolygon poly;
|
ExPolygon poly;
|
||||||
@ -61,8 +63,10 @@ Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::for_each(tri.begin(), tri.end(),
|
std::for_each(tri.begin(), tri.end(),
|
||||||
[&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp)
|
[&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp)
|
||||||
{
|
{
|
||||||
|
thr(); // may throw if cancellation was requested
|
||||||
|
|
||||||
for(auto& p : pp.points)
|
for(auto& p : pp.points)
|
||||||
if(is_upper(p))
|
if(is_upper(p))
|
||||||
rp.emplace_back(unscale(x(p), y(p), mm(cz)));
|
rp.emplace_back(unscale(x(p), y(p), mm(cz)));
|
||||||
@ -217,6 +221,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
double degrees,
|
double degrees,
|
||||||
double ceilheight_mm,
|
double ceilheight_mm,
|
||||||
bool dir,
|
bool dir,
|
||||||
|
ThrowOnCancel throw_on_cancel,
|
||||||
ExP&& last_offset = ExP(), D&& last_height = D())
|
ExP&& last_offset = ExP(), D&& last_height = D())
|
||||||
{
|
{
|
||||||
auto ob = base_plate;
|
auto ob = base_plate;
|
||||||
@ -231,6 +236,8 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
|
|
||||||
if(degrees >= 90) {
|
if(degrees >= 90) {
|
||||||
for(int i = 1; i <= steps; ++i) {
|
for(int i = 1; i <= steps; ++i) {
|
||||||
|
throw_on_cancel();
|
||||||
|
|
||||||
ob = base_plate;
|
ob = base_plate;
|
||||||
|
|
||||||
double r2 = radius_mm * radius_mm;
|
double r2 = radius_mm * radius_mm;
|
||||||
@ -242,7 +249,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
wh = ceilheight_mm - radius_mm + stepy;
|
wh = ceilheight_mm - radius_mm + stepy;
|
||||||
|
|
||||||
Contour3D pwalls;
|
Contour3D pwalls;
|
||||||
pwalls = walls(ob, ob_prev, wh, wh_prev);
|
pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel);
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
curvedwalls.merge(pwalls);
|
||||||
ob_prev = ob;
|
ob_prev = ob;
|
||||||
@ -254,6 +261,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
int tos = int(tox / stepx);
|
int tos = int(tox / stepx);
|
||||||
|
|
||||||
for(int i = 1; i <= tos; ++i) {
|
for(int i = 1; i <= tos; ++i) {
|
||||||
|
throw_on_cancel();
|
||||||
ob = base_plate;
|
ob = base_plate;
|
||||||
|
|
||||||
double r2 = radius_mm * radius_mm;
|
double r2 = radius_mm * radius_mm;
|
||||||
@ -264,7 +272,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
wh = ceilheight_mm - radius_mm - stepy;
|
wh = ceilheight_mm - radius_mm - stepy;
|
||||||
|
|
||||||
Contour3D pwalls;
|
Contour3D pwalls;
|
||||||
pwalls = walls(ob_prev, ob, wh_prev, wh);
|
pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel);
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
curvedwalls.merge(pwalls);
|
||||||
ob_prev = ob;
|
ob_prev = ob;
|
||||||
@ -348,7 +356,8 @@ inline Point centroid(const ExPolygon& poly) {
|
|||||||
/// with explicit bridges. Bridges are generated from each shape's centroid
|
/// with explicit bridges. Bridges are generated from each shape's centroid
|
||||||
/// to the center of the "scene" which is the centroid calculated from the shape
|
/// to the center of the "scene" which is the centroid calculated from the shape
|
||||||
/// centroids (a star is created...)
|
/// centroids (a star is created...)
|
||||||
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
|
||||||
|
ThrowOnCancel throw_on_cancel = [](){})
|
||||||
{
|
{
|
||||||
namespace bgi = boost::geometry::index;
|
namespace bgi = boost::geometry::index;
|
||||||
using SpatElement = std::pair<BoundingBox, unsigned>;
|
using SpatElement = std::pair<BoundingBox, unsigned>;
|
||||||
@ -383,9 +392,10 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
|||||||
idx = 0;
|
idx = 0;
|
||||||
std::transform(centroids.begin(), centroids.end(),
|
std::transform(centroids.begin(), centroids.end(),
|
||||||
std::back_inserter(punion),
|
std::back_inserter(punion),
|
||||||
[&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c)
|
[&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
|
||||||
|
(const Point& c)
|
||||||
{
|
{
|
||||||
|
throw_on_cancel();
|
||||||
double dx = x(c) - x(cc), dy = y(c) - y(cc);
|
double dx = x(c) - x(cc), dy = y(c) - y(cc);
|
||||||
double l = std::sqrt(dx * dx + dy * dy);
|
double l = std::sqrt(dx * dx + dy * dy);
|
||||||
double nx = dx / l, ny = dy / l;
|
double nx = dx / l, ny = dy / l;
|
||||||
@ -419,7 +429,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
||||||
float layerh)
|
float layerh, ThrowOnCancel thrfn)
|
||||||
{
|
{
|
||||||
TriangleMesh m = mesh;
|
TriangleMesh m = mesh;
|
||||||
TriangleMeshSlicer slicer(&m);
|
TriangleMeshSlicer slicer(&m);
|
||||||
@ -431,14 +441,17 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
|||||||
heights.emplace_back(hi);
|
heights.emplace_back(hi);
|
||||||
|
|
||||||
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
|
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
|
||||||
slicer.slice(heights, &out, [](){});
|
slicer.slice(heights, &out, thrfn);
|
||||||
|
|
||||||
size_t count = 0; for(auto& o : out) count += o.size();
|
size_t count = 0; for(auto& o : out) count += o.size();
|
||||||
ExPolygons tmp; tmp.reserve(count);
|
ExPolygons tmp; tmp.reserve(count);
|
||||||
for(auto& o : out) for(auto& e : o) tmp.emplace_back(std::move(e));
|
for(auto& o : out) for(auto& e : o) tmp.emplace_back(std::move(e));
|
||||||
|
|
||||||
output = unify(tmp);
|
ExPolygons utmp = unify(tmp);
|
||||||
for(auto& o : output) o = o.simplify(0.1/SCALING_FACTOR).front();
|
for(auto& o : utmp) {
|
||||||
|
auto&& smp = o.simplify(0.1/SCALING_FACTOR);
|
||||||
|
output.insert(output.end(), smp.begin(), smp.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
||||||
@ -447,7 +460,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
|
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
|
||||||
cfg.max_merge_distance_mm;
|
cfg.max_merge_distance_mm;
|
||||||
|
|
||||||
auto concavehs = concave_hull(ground_layer, mdist);
|
auto concavehs = concave_hull(ground_layer, mdist, cfg.throw_on_cancel);
|
||||||
for(ExPolygon& concaveh : concavehs) {
|
for(ExPolygon& concaveh : concavehs) {
|
||||||
if(concaveh.contour.points.empty()) return;
|
if(concaveh.contour.points.empty()) return;
|
||||||
concaveh.holes.clear();
|
concaveh.holes.clear();
|
||||||
@ -505,6 +518,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
phi, // 170 degrees
|
phi, // 170 degrees
|
||||||
0, // z position of the input plane
|
0, // z position of the input plane
|
||||||
true,
|
true,
|
||||||
|
cfg.throw_on_cancel,
|
||||||
ob, wh);
|
ob, wh);
|
||||||
|
|
||||||
pool.merge(curvedwalls);
|
pool.merge(curvedwalls);
|
||||||
@ -512,7 +526,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
ExPolygon ob_contr = ob;
|
ExPolygon ob_contr = ob;
|
||||||
ob_contr.holes.clear();
|
ob_contr.holes.clear();
|
||||||
|
|
||||||
auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm);
|
auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm,
|
||||||
|
cfg.throw_on_cancel);
|
||||||
pool.merge(pwalls);
|
pool.merge(pwalls);
|
||||||
|
|
||||||
Polygons top_triangles, bottom_triangles;
|
Polygons top_triangles, bottom_triangles;
|
||||||
@ -528,6 +543,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
90, // 90 degrees
|
90, // 90 degrees
|
||||||
0, // z position of the input plane
|
0, // z position of the input plane
|
||||||
false,
|
false,
|
||||||
|
cfg.throw_on_cancel,
|
||||||
ob, wh);
|
ob, wh);
|
||||||
pool.merge(curvedwalls);
|
pool.merge(curvedwalls);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define SLABASEPOOL_HPP
|
#define SLABASEPOOL_HPP
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -11,12 +12,14 @@ class TriangleMesh;
|
|||||||
namespace sla {
|
namespace sla {
|
||||||
|
|
||||||
using ExPolygons = std::vector<ExPolygon>;
|
using ExPolygons = std::vector<ExPolygon>;
|
||||||
|
using ThrowOnCancel = std::function<void(void)>;
|
||||||
|
|
||||||
/// Calculate the polygon representing the silhouette from the specified height
|
/// Calculate the polygon representing the silhouette from the specified height
|
||||||
void base_plate(const TriangleMesh& mesh,
|
void base_plate(const TriangleMesh& mesh, // input mesh
|
||||||
ExPolygons& output,
|
ExPolygons& output, // Output will be merged with
|
||||||
float zlevel = 0.1f,
|
float zlevel = 0.1f, // Plate creation level
|
||||||
float layerheight = 0.05f);
|
float layerheight = 0.05f, // The sampling height
|
||||||
|
ThrowOnCancel thrfn = [](){}); // Will be called frequently
|
||||||
|
|
||||||
struct PoolConfig {
|
struct PoolConfig {
|
||||||
double min_wall_thickness_mm = 2;
|
double min_wall_thickness_mm = 2;
|
||||||
@ -24,6 +27,8 @@ struct PoolConfig {
|
|||||||
double max_merge_distance_mm = 50;
|
double max_merge_distance_mm = 50;
|
||||||
double edge_radius_mm = 1;
|
double edge_radius_mm = 1;
|
||||||
|
|
||||||
|
ThrowOnCancel throw_on_cancel = [](){};
|
||||||
|
|
||||||
inline PoolConfig() {}
|
inline PoolConfig() {}
|
||||||
inline PoolConfig(double wt, double wh, double md, double er):
|
inline PoolConfig(double wt, double wh, double md, double er):
|
||||||
min_wall_thickness_mm(wt),
|
min_wall_thickness_mm(wt),
|
||||||
@ -35,8 +40,7 @@ struct PoolConfig {
|
|||||||
/// Calculate the pool for the mesh for SLA printing
|
/// Calculate the pool for the mesh for SLA printing
|
||||||
void create_base_pool(const ExPolygons& base_plate,
|
void create_base_pool(const ExPolygons& base_plate,
|
||||||
TriangleMesh& output_mesh,
|
TriangleMesh& output_mesh,
|
||||||
const PoolConfig& = PoolConfig()
|
const PoolConfig& = PoolConfig());
|
||||||
);
|
|
||||||
|
|
||||||
/// TODO: Currently the base plate of the pool will have half the height of the
|
/// TODO: Currently the base plate of the pool will have half the height of the
|
||||||
/// whole pool. So the carved out space has also half the height. This is not
|
/// whole pool. So the carved out space has also half the height. This is not
|
||||||
|
@ -58,7 +58,7 @@ struct Contour3D {
|
|||||||
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
||||||
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
||||||
|
|
||||||
for(auto n = s; n < indices.size(); n++) {
|
for(size_t n = s; n < indices.size(); n++) {
|
||||||
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj,
|
|||||||
using libnest2d::opt::Optimizer;
|
using libnest2d::opt::Optimizer;
|
||||||
using libnest2d::opt::TOptimizer;
|
using libnest2d::opt::TOptimizer;
|
||||||
using libnest2d::opt::StopCriteria;
|
using libnest2d::opt::StopCriteria;
|
||||||
using Quaternion = Eigen::Quaternion<double>;
|
|
||||||
|
|
||||||
static const unsigned MAX_TRIES = 100000;
|
static const unsigned MAX_TRIES = 100000;
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||||||
auto steps = int(ssteps);
|
auto steps = int(ssteps);
|
||||||
auto& points = ret.points;
|
auto& points = ret.points;
|
||||||
auto& indices = ret.indices;
|
auto& indices = ret.indices;
|
||||||
points.reserve(2*steps);
|
points.reserve(2*ssteps);
|
||||||
double a = 2*PI/steps;
|
double a = 2*PI/steps;
|
||||||
|
|
||||||
Vec3d jp = {0, 0, 0};
|
Vec3d jp = {0, 0, 0};
|
||||||
@ -189,7 +189,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||||||
points.emplace_back(x, y, jp(Z));
|
points.emplace_back(x, y, jp(Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.reserve(2*steps);
|
indices.reserve(2*ssteps);
|
||||||
auto offs = steps;
|
auto offs = steps;
|
||||||
for(int i = 0; i < steps - 1; ++i) {
|
for(int i = 0; i < steps - 1; ++i) {
|
||||||
indices.emplace_back(i, i + offs, offs + i + 1);
|
indices.emplace_back(i, i + offs, offs + i + 1);
|
||||||
@ -347,19 +347,22 @@ struct Pillar {
|
|||||||
double radius = 1, size_t st = 45):
|
double radius = 1, size_t st = 45):
|
||||||
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
||||||
{
|
{
|
||||||
|
assert(steps > 0);
|
||||||
|
int steps_1 = int(steps - 1);
|
||||||
|
|
||||||
auto& points = mesh.points;
|
auto& points = mesh.points;
|
||||||
auto& indices = mesh.indices;
|
auto& indices = mesh.indices;
|
||||||
points.reserve(2*steps);
|
points.reserve(2*steps);
|
||||||
double a = 2*PI/steps;
|
double a = 2*PI/steps;
|
||||||
|
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(size_t i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double x = jp(X) + r*std::cos(phi);
|
double x = jp(X) + r*std::cos(phi);
|
||||||
double y = jp(Y) + r*std::sin(phi);
|
double y = jp(Y) + r*std::sin(phi);
|
||||||
points.emplace_back(x, y, jp(Z));
|
points.emplace_back(x, y, jp(Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(size_t i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double ex = endp(X) + r*std::cos(phi);
|
double ex = endp(X) + r*std::cos(phi);
|
||||||
double ey = endp(Y) + r*std::sin(phi);
|
double ey = endp(Y) + r*std::sin(phi);
|
||||||
@ -368,14 +371,13 @@ struct Pillar {
|
|||||||
|
|
||||||
indices.reserve(2*steps);
|
indices.reserve(2*steps);
|
||||||
int offs = int(steps);
|
int offs = int(steps);
|
||||||
for(int i = 0; i < steps - 1; ++i) {
|
for(int i = 0; i < steps_1 ; ++i) {
|
||||||
indices.emplace_back(i, i + offs, offs + i + 1);
|
indices.emplace_back(i, i + offs, offs + i + 1);
|
||||||
indices.emplace_back(i, offs + i + 1, i + 1);
|
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int last = int(steps) - 1;
|
indices.emplace_back(0, steps_1, offs);
|
||||||
indices.emplace_back(0, last, offs);
|
indices.emplace_back(steps_1, offs + steps_1, offs);
|
||||||
indices.emplace_back(last, offs + last, offs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pillar(const Junction& junc, const Vec3d& endp):
|
Pillar(const Junction& junc, const Vec3d& endp):
|
||||||
@ -390,19 +392,22 @@ struct Pillar {
|
|||||||
void add_base(double height = 3, double radius = 2) {
|
void add_base(double height = 3, double radius = 2) {
|
||||||
if(height <= 0) return;
|
if(height <= 0) return;
|
||||||
|
|
||||||
|
assert(steps >= 0);
|
||||||
|
auto last = int(steps - 1);
|
||||||
|
|
||||||
if(radius < r ) radius = r;
|
if(radius < r ) radius = r;
|
||||||
|
|
||||||
double a = 2*PI/steps;
|
double a = 2*PI/steps;
|
||||||
double z = endpoint(2) + height;
|
double z = endpoint(2) + height;
|
||||||
|
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(size_t i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double x = endpoint(0) + r*std::cos(phi);
|
double x = endpoint(0) + r*std::cos(phi);
|
||||||
double y = endpoint(1) + r*std::sin(phi);
|
double y = endpoint(1) + r*std::sin(phi);
|
||||||
base.points.emplace_back(x, y, z);
|
base.points.emplace_back(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(size_t i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double x = endpoint(0) + radius*std::cos(phi);
|
double x = endpoint(0) + radius*std::cos(phi);
|
||||||
double y = endpoint(1) + radius*std::sin(phi);
|
double y = endpoint(1) + radius*std::sin(phi);
|
||||||
@ -417,14 +422,13 @@ struct Pillar {
|
|||||||
auto hcenter = int(base.points.size() - 1);
|
auto hcenter = int(base.points.size() - 1);
|
||||||
auto lcenter = int(base.points.size() - 2);
|
auto lcenter = int(base.points.size() - 2);
|
||||||
auto offs = int(steps);
|
auto offs = int(steps);
|
||||||
for(int i = 0; i < steps - 1; ++i) {
|
for(int i = 0; i < last; ++i) {
|
||||||
indices.emplace_back(i, i + offs, offs + i + 1);
|
indices.emplace_back(i, i + offs, offs + i + 1);
|
||||||
indices.emplace_back(i, offs + i + 1, i + 1);
|
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||||
indices.emplace_back(i, i + 1, hcenter);
|
indices.emplace_back(i, i + 1, hcenter);
|
||||||
indices.emplace_back(lcenter, offs + i + 1, offs + i);
|
indices.emplace_back(lcenter, offs + i + 1, offs + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto last = int(steps - 1);
|
|
||||||
indices.emplace_back(0, last, offs);
|
indices.emplace_back(0, last, offs);
|
||||||
indices.emplace_back(last, offs + last, offs);
|
indices.emplace_back(last, offs + last, offs);
|
||||||
indices.emplace_back(hcenter, last, 0);
|
indices.emplace_back(hcenter, last, 0);
|
||||||
@ -462,8 +466,6 @@ struct Bridge {
|
|||||||
Bridge(const Junction& j1, const Junction& j2, double r_mm = 0.8):
|
Bridge(const Junction& j1, const Junction& j2, double r_mm = 0.8):
|
||||||
Bridge(j1.pos, j2.pos, r_mm, j1.steps) {}
|
Bridge(j1.pos, j2.pos, r_mm, j1.steps) {}
|
||||||
|
|
||||||
Bridge(const Junction& j, const Pillar& cl) {}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A bridge that spans from model surface to model surface with small connecting
|
// A bridge that spans from model surface to model surface with small connecting
|
||||||
@ -515,8 +517,13 @@ struct Pad {
|
|||||||
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
||||||
{
|
{
|
||||||
ExPolygons basep;
|
ExPolygons basep;
|
||||||
|
cfg.throw_on_cancel();
|
||||||
|
|
||||||
|
// The 0.1f is the layer height with which the mesh is sampled and then
|
||||||
|
// the layers are unified into one vector of polygons.
|
||||||
base_plate(object_support_mesh, basep,
|
base_plate(object_support_mesh, basep,
|
||||||
float(cfg.min_wall_height_mm)/*,layer_height*/);
|
float(cfg.min_wall_height_mm), 0.1f, pcfg.throw_on_cancel);
|
||||||
|
|
||||||
for(auto& bp : baseplate) basep.emplace_back(bp);
|
for(auto& bp : baseplate) basep.emplace_back(bp);
|
||||||
|
|
||||||
create_base_pool(basep, tmesh, cfg);
|
create_base_pool(basep, tmesh, cfg);
|
||||||
@ -532,12 +539,12 @@ EigenMesh3D to_eigenmesh(const Contour3D& cntr) {
|
|||||||
auto& V = emesh.V;
|
auto& V = emesh.V;
|
||||||
auto& F = emesh.F;
|
auto& F = emesh.F;
|
||||||
|
|
||||||
V.resize(cntr.points.size(), 3);
|
V.resize(Eigen::Index(cntr.points.size()), 3);
|
||||||
F.resize(cntr.indices.size(), 3);
|
F.resize(Eigen::Index(cntr.indices.size()), 3);
|
||||||
|
|
||||||
for (int i = 0; i < V.rows(); ++i) {
|
for (int i = 0; i < V.rows(); ++i) {
|
||||||
V.row(i) = cntr.points[i];
|
V.row(i) = cntr.points[size_t(i)];
|
||||||
F.row(i) = cntr.indices[i];
|
F.row(i) = cntr.indices[size_t(i)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return emesh;
|
return emesh;
|
||||||
@ -564,18 +571,23 @@ EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) {
|
|||||||
|
|
||||||
V.resize(3*stl.stats.number_of_facets, 3);
|
V.resize(3*stl.stats.number_of_facets, 3);
|
||||||
F.resize(stl.stats.number_of_facets, 3);
|
F.resize(stl.stats.number_of_facets, 3);
|
||||||
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
|
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
|
||||||
const stl_facet* facet = stl.facet_start+i;
|
const stl_facet* facet = stl.facet_start+i;
|
||||||
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) =
|
V(3*i+0, 0) = double(facet->vertex[0](0));
|
||||||
facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
|
V(3*i+0, 1) = double(facet->vertex[0](1));
|
||||||
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) =
|
V(3*i+0, 2) = double(facet->vertex[0](2));
|
||||||
facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
|
|
||||||
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) =
|
|
||||||
facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
|
|
||||||
|
|
||||||
F(i, 0) = 3*i+0;
|
V(3*i+1, 0) = double(facet->vertex[1](0));
|
||||||
F(i, 1) = 3*i+1;
|
V(3*i+1, 1) = double(facet->vertex[1](1));
|
||||||
F(i, 2) = 3*i+2;
|
V(3*i+1, 2) = double(facet->vertex[1](2));
|
||||||
|
|
||||||
|
V(3*i+2, 0) = double(facet->vertex[2](0));
|
||||||
|
V(3*i+2, 1) = double(facet->vertex[2](1));
|
||||||
|
V(3*i+2, 2) = double(facet->vertex[2](2));
|
||||||
|
|
||||||
|
F(i, 0) = int(3*i+0);
|
||||||
|
F(i, 1) = int(3*i+1);
|
||||||
|
F(i, 2) = int(3*i+2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return outmesh;
|
return outmesh;
|
||||||
@ -622,12 +634,19 @@ class SLASupportTree::Impl {
|
|||||||
std::vector<Junction> m_junctions;
|
std::vector<Junction> m_junctions;
|
||||||
std::vector<Bridge> m_bridges;
|
std::vector<Bridge> m_bridges;
|
||||||
std::vector<CompactBridge> m_compact_bridges;
|
std::vector<CompactBridge> m_compact_bridges;
|
||||||
|
Controller m_ctl;
|
||||||
|
|
||||||
Pad m_pad;
|
Pad m_pad;
|
||||||
mutable TriangleMesh meshcache; mutable bool meshcache_valid;
|
mutable TriangleMesh meshcache; mutable bool meshcache_valid = false;
|
||||||
mutable double model_height = 0; // the full height of the model
|
mutable double model_height = 0; // the full height of the model
|
||||||
public:
|
public:
|
||||||
double ground_level = 0;
|
double ground_level = 0;
|
||||||
|
|
||||||
|
Impl() = default;
|
||||||
|
inline Impl(const Controller& ctl): m_ctl(ctl) {}
|
||||||
|
|
||||||
|
const Controller& ctl() const { return m_ctl; }
|
||||||
|
|
||||||
template<class...Args> Head& add_head(Args&&... args) {
|
template<class...Args> Head& add_head(Args&&... args) {
|
||||||
m_heads.emplace_back(std::forward<Args>(args)...);
|
m_heads.emplace_back(std::forward<Args>(args)...);
|
||||||
m_heads.back().id = long(m_heads.size() - 1);
|
m_heads.back().id = long(m_heads.size() - 1);
|
||||||
@ -637,7 +656,7 @@ public:
|
|||||||
|
|
||||||
template<class...Args> Pillar& add_pillar(long headid, Args&&... args) {
|
template<class...Args> Pillar& add_pillar(long headid, Args&&... args) {
|
||||||
assert(headid >= 0 && headid < m_heads.size());
|
assert(headid >= 0 && headid < m_heads.size());
|
||||||
Head& head = m_heads[headid];
|
Head& head = m_heads[size_t(headid)];
|
||||||
m_pillars.emplace_back(head, std::forward<Args>(args)...);
|
m_pillars.emplace_back(head, std::forward<Args>(args)...);
|
||||||
Pillar& pillar = m_pillars.back();
|
Pillar& pillar = m_pillars.back();
|
||||||
pillar.id = long(m_pillars.size() - 1);
|
pillar.id = long(m_pillars.size() - 1);
|
||||||
@ -650,17 +669,17 @@ public:
|
|||||||
|
|
||||||
const Head& pillar_head(long pillar_id) const {
|
const Head& pillar_head(long pillar_id) const {
|
||||||
assert(pillar_id >= 0 && pillar_id < m_pillars.size());
|
assert(pillar_id >= 0 && pillar_id < m_pillars.size());
|
||||||
const Pillar& p = m_pillars[pillar_id];
|
const Pillar& p = m_pillars[size_t(pillar_id)];
|
||||||
assert(p.starts_from_head && p.start_junction_id >= 0 &&
|
assert(p.starts_from_head && p.start_junction_id >= 0 &&
|
||||||
p.start_junction_id < m_heads.size() );
|
p.start_junction_id < m_heads.size() );
|
||||||
return m_heads[p.start_junction_id];
|
return m_heads[size_t(p.start_junction_id)];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Pillar& head_pillar(long headid) const {
|
const Pillar& head_pillar(long headid) const {
|
||||||
assert(headid >= 0 && headid < m_heads.size());
|
assert(headid >= 0 && headid < m_heads.size());
|
||||||
const Head& h = m_heads[headid];
|
const Head& h = m_heads[size_t(headid)];
|
||||||
assert(h.pillar_id >= 0 && h.pillar_id < m_pillars.size());
|
assert(h.pillar_id >= 0 && h.pillar_id < m_pillars.size());
|
||||||
return m_pillars[h.pillar_id];
|
return m_pillars[size_t(h.pillar_id)];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args> const Junction& add_junction(Args&&... args) {
|
template<class...Args> const Junction& add_junction(Args&&... args) {
|
||||||
@ -710,27 +729,38 @@ public:
|
|||||||
meshcache = TriangleMesh();
|
meshcache = TriangleMesh();
|
||||||
|
|
||||||
for(auto& head : heads()) {
|
for(auto& head : heads()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
auto&& m = mesh(head.mesh);
|
auto&& m = mesh(head.mesh);
|
||||||
meshcache.merge(m);
|
meshcache.merge(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& stick : pillars()) {
|
for(auto& stick : pillars()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(stick.mesh));
|
meshcache.merge(mesh(stick.mesh));
|
||||||
meshcache.merge(mesh(stick.base));
|
meshcache.merge(mesh(stick.base));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& j : junctions()) {
|
for(auto& j : junctions()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(j.mesh));
|
meshcache.merge(mesh(j.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& cb : compact_bridges()) {
|
for(auto& cb : compact_bridges()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(cb.mesh));
|
meshcache.merge(mesh(cb.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& bs : bridges()) {
|
for(auto& bs : bridges()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(bs.mesh));
|
meshcache.merge(mesh(bs.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_ctl.stopcondition()) {
|
||||||
|
// In case of failure we have to return an empty mesh
|
||||||
|
meshcache = TriangleMesh();
|
||||||
|
return meshcache;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Is this necessary?
|
// TODO: Is this necessary?
|
||||||
meshcache.repair();
|
meshcache.repair();
|
||||||
|
|
||||||
@ -743,8 +773,11 @@ public:
|
|||||||
|
|
||||||
// WITH THE PAD
|
// WITH THE PAD
|
||||||
double full_height() const {
|
double full_height() const {
|
||||||
|
if(merged_mesh().empty() && !pad().empty())
|
||||||
|
return pad().cfg.min_wall_height_mm;
|
||||||
|
|
||||||
double h = mesh_height();
|
double h = mesh_height();
|
||||||
if(!pad().empty()) h += pad().cfg.min_wall_height_mm / 2;
|
if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -856,8 +889,8 @@ ClusterEl pts_convex_hull(const ClusterEl& inpts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the leftmost (bottom) point
|
// Find the leftmost (bottom) point
|
||||||
int l = 0;
|
size_t l = 0;
|
||||||
for (int i = 1; i < n; i++) {
|
for (size_t i = 1; i < n; i++) {
|
||||||
if(std::abs(points[i](X) - points[l](X)) < ERR) {
|
if(std::abs(points[i](X) - points[l](X)) < ERR) {
|
||||||
if(points[i](Y) < points[l](Y)) l = i;
|
if(points[i](Y) < points[l](Y)) l = i;
|
||||||
}
|
}
|
||||||
@ -868,7 +901,6 @@ ClusterEl pts_convex_hull(const ClusterEl& inpts,
|
|||||||
// fill the output with the spatially ordered set of points.
|
// fill the output with the spatially ordered set of points.
|
||||||
|
|
||||||
// find the direction
|
// find the direction
|
||||||
Vec2d dir = (points[l] - points[(l+1)%n]).normalized();
|
|
||||||
hull = inpts;
|
hull = inpts;
|
||||||
auto& lp = points[l];
|
auto& lp = points[l];
|
||||||
std::sort(hull.begin(), hull.end(),
|
std::sort(hull.begin(), hull.end(),
|
||||||
@ -886,7 +918,7 @@ ClusterEl pts_convex_hull(const ClusterEl& inpts,
|
|||||||
// Start from leftmost point, keep moving counterclockwise
|
// Start from leftmost point, keep moving counterclockwise
|
||||||
// until reach the start point again. This loop runs O(h)
|
// until reach the start point again. This loop runs O(h)
|
||||||
// times where h is number of points in result or output.
|
// times where h is number of points in result or output.
|
||||||
int p = l;
|
size_t p = l;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Add current point to result
|
// Add current point to result
|
||||||
@ -897,8 +929,8 @@ ClusterEl pts_convex_hull(const ClusterEl& inpts,
|
|||||||
// is to keep track of last visited most counterclock-
|
// is to keep track of last visited most counterclock-
|
||||||
// wise point in q. If any point 'i' is more counterclock-
|
// wise point in q. If any point 'i' is more counterclock-
|
||||||
// wise than q, then update q.
|
// wise than q, then update q.
|
||||||
int q = (p+1)%n;
|
size_t q = (p + 1) % n;
|
||||||
for (int i = 0; i < n; i++)
|
for (size_t i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
// If i is more counterclockwise than current q, then
|
// If i is more counterclockwise than current q, then
|
||||||
// update q
|
// update q
|
||||||
@ -980,7 +1012,10 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
// std::cout << "p " << pn << " " << points.row(pn) << std::endl;
|
// std::cout << "p " << pn << " " << points.row(pn) << std::endl;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
auto filterfn = [] (
|
|
||||||
|
auto& tifcl = ctl.cancelfn;
|
||||||
|
|
||||||
|
auto filterfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& points,
|
const PointSet& points,
|
||||||
const EigenMesh3D& mesh,
|
const EigenMesh3D& mesh,
|
||||||
@ -990,26 +1025,29 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
PointSet& headless_pos,
|
PointSet& headless_pos,
|
||||||
PointSet& headless_norm)
|
PointSet& headless_norm)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
/* Filtering step */
|
/* Filtering step */
|
||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
|
|
||||||
// Get the points that are too close to each other and keep only the
|
// Get the points that are too close to each other and keep only the
|
||||||
// first one
|
// first one
|
||||||
auto aliases = cluster(points,
|
auto aliases =
|
||||||
[cfg](const SpatElement& p,
|
cluster(points,
|
||||||
const SpatElement& se){
|
[tifcl](const SpatElement& p, const SpatElement& se)
|
||||||
|
{
|
||||||
|
tifcl();
|
||||||
return distance(p.first, se.first) < D_SP;
|
return distance(p.first, se.first) < D_SP;
|
||||||
}, 2);
|
}, 2);
|
||||||
|
|
||||||
filt_pts.resize(aliases.size(), 3);
|
filt_pts.resize(Eigen::Index(aliases.size()), 3);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for(auto& a : aliases) {
|
for(auto& a : aliases) {
|
||||||
// Here we keep only the front point of the cluster. TODO: centroid
|
// Here we keep only the front point of the cluster.
|
||||||
filt_pts.row(count++) = points.row(a.front());
|
filt_pts.row(count++) = points.row(a.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tifcl();
|
||||||
|
|
||||||
// calculate the normals to the triangles belonging to filtered points
|
// calculate the normals to the triangles belonging to filtered points
|
||||||
auto nmls = sla::normals(filt_pts, mesh);
|
auto nmls = sla::normals(filt_pts, mesh);
|
||||||
|
|
||||||
@ -1025,6 +1063,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
|
|
||||||
int pcount = 0, hlcount = 0;
|
int pcount = 0, hlcount = 0;
|
||||||
for(int i = 0; i < count; i++) {
|
for(int i = 0; i < count; i++) {
|
||||||
|
tifcl();
|
||||||
auto n = nmls.row(i);
|
auto n = nmls.row(i);
|
||||||
|
|
||||||
// for all normals we generate the spherical coordinates and
|
// for all normals we generate the spherical coordinates and
|
||||||
@ -1084,7 +1123,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Function to write the pinheads into the result
|
// Function to write the pinheads into the result
|
||||||
auto pinheadfn = [] (
|
auto pinheadfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
PointSet& head_pos,
|
PointSet& head_pos,
|
||||||
PointSet& nmls,
|
PointSet& nmls,
|
||||||
@ -1097,6 +1136,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
|
|
||||||
for (int i = 0; i < head_pos.rows(); ++i) {
|
for (int i = 0; i < head_pos.rows(); ++i) {
|
||||||
|
tifcl();
|
||||||
result.add_head(
|
result.add_head(
|
||||||
cfg.head_back_radius_mm,
|
cfg.head_back_radius_mm,
|
||||||
cfg.head_front_radius_mm,
|
cfg.head_front_radius_mm,
|
||||||
@ -1110,7 +1150,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
|
|
||||||
// &filtered_points, &head_positions, &result, &mesh,
|
// &filtered_points, &head_positions, &result, &mesh,
|
||||||
// &gndidx, &gndheight, &nogndidx, cfg
|
// &gndidx, &gndheight, &nogndidx, cfg
|
||||||
auto classifyfn = [] (
|
auto classifyfn = [tifcl] (
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const EigenMesh3D& mesh,
|
const EigenMesh3D& mesh,
|
||||||
PointSet& head_pos,
|
PointSet& head_pos,
|
||||||
@ -1126,11 +1166,12 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
/* ******************************************************** */
|
/* ******************************************************** */
|
||||||
|
|
||||||
// We should first get the heads that reach the ground directly
|
// We should first get the heads that reach the ground directly
|
||||||
gndheight.reserve(head_pos.rows());
|
gndheight.reserve(size_t(head_pos.rows()));
|
||||||
gndidx.reserve(head_pos.rows());
|
gndidx.reserve(size_t(head_pos.rows()));
|
||||||
nogndidx.reserve(head_pos.rows());
|
nogndidx.reserve(size_t(head_pos.rows()));
|
||||||
|
|
||||||
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
||||||
|
tifcl();
|
||||||
auto& head = result.heads()[i];
|
auto& head = result.heads()[i];
|
||||||
|
|
||||||
Vec3d dir(0, 0, -1);
|
Vec3d dir(0, 0, -1);
|
||||||
@ -1147,15 +1188,19 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
PointSet gnd(gndidx.size(), 3);
|
PointSet gnd(gndidx.size(), 3);
|
||||||
|
|
||||||
for(size_t i = 0; i < gndidx.size(); i++)
|
for(size_t i = 0; i < gndidx.size(); i++)
|
||||||
gnd.row(i) = head_pos.row(gndidx[i]);
|
gnd.row(long(i)) = head_pos.row(gndidx[i]);
|
||||||
|
|
||||||
// We want to search for clusters of points that are far enough from
|
// We want to search for clusters of points that are far enough from
|
||||||
// each other in the XY plane to not cross their pillar bases
|
// each other in the XY plane to not cross their pillar bases
|
||||||
// These clusters of support points will join in one pillar, possibly in
|
// These clusters of support points will join in one pillar, possibly in
|
||||||
// their centroid support point.
|
// their centroid support point.
|
||||||
auto d_base = 2*cfg.base_radius_mm;
|
auto d_base = 2*cfg.base_radius_mm;
|
||||||
ground_clusters = cluster(gnd,
|
ground_clusters =
|
||||||
[d_base, &cfg](const SpatElement& p, const SpatElement& s){
|
cluster(
|
||||||
|
gnd,
|
||||||
|
[d_base, tifcl](const SpatElement& p, const SpatElement& s)
|
||||||
|
{
|
||||||
|
tifcl();
|
||||||
return distance(Vec2d(p.first(X), p.first(Y)),
|
return distance(Vec2d(p.first(X), p.first(Y)),
|
||||||
Vec2d(s.first(X), s.first(Y))) < d_base;
|
Vec2d(s.first(X), s.first(Y))) < d_base;
|
||||||
}, 3); // max 3 heads to connect to one centroid
|
}, 3); // max 3 heads to connect to one centroid
|
||||||
@ -1171,9 +1216,6 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
const Head& phead = result.pillar_head(pillar.id);
|
const Head& phead = result.pillar_head(pillar.id);
|
||||||
const Head& nextphead = result.pillar_head(nextpillar.id);
|
const Head& nextphead = result.pillar_head(nextpillar.id);
|
||||||
|
|
||||||
// double d = 2*pillar.r;
|
|
||||||
// const Vec3d& pp = pillar.endpoint.cwiseProduct(Vec3d{1, 1, 0});
|
|
||||||
|
|
||||||
Vec3d sj = phead.junction_point();
|
Vec3d sj = phead.junction_point();
|
||||||
sj(Z) = std::min(sj(Z), nextphead.junction_point()(Z));
|
sj(Z) = std::min(sj(Z), nextphead.junction_point()(Z));
|
||||||
Vec3d ej = nextpillar.endpoint;
|
Vec3d ej = nextpillar.endpoint;
|
||||||
@ -1218,7 +1260,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto routing_ground_fn = [gnd_head_pt, interconnect](
|
auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const ClusteredPoints& gnd_clusters,
|
const ClusteredPoints& gnd_clusters,
|
||||||
const IndexSet& gndidx,
|
const IndexSet& gndidx,
|
||||||
@ -1234,22 +1276,27 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
cl_centroids.reserve(gnd_clusters.size());
|
cl_centroids.reserve(gnd_clusters.size());
|
||||||
|
|
||||||
SpatIndex pheadindex; // spatial index for the junctions
|
SpatIndex pheadindex; // spatial index for the junctions
|
||||||
for(auto cl : gnd_clusters) {
|
for(auto& cl : gnd_clusters) { tifcl();
|
||||||
// place all the centroid head positions into the index. We will
|
// place all the centroid head positions into the index. We will
|
||||||
// query for alternative pillar positions. If a sidehead cannot
|
// query for alternative pillar positions. If a sidehead cannot
|
||||||
// connect to the cluster centroid, we have to search for another
|
// connect to the cluster centroid, we have to search for another
|
||||||
// head with a full pillar. Also when there are two elements in the
|
// head with a full pillar. Also when there are two elements in the
|
||||||
// cluster, the centroid is arbitrary and the sidehead is allowed to
|
// cluster, the centroid is arbitrary and the sidehead is allowed to
|
||||||
// connect to a nearby pillar to increase structural stability.
|
// connect to a nearby pillar to increase structural stability.
|
||||||
|
if(cl.empty()) continue;
|
||||||
|
|
||||||
// get the current cluster centroid
|
// get the current cluster centroid
|
||||||
unsigned cid = cluster_centroid(cl, gnd_head_pt,
|
long lcid = cluster_centroid(cl, gnd_head_pt,
|
||||||
[](const Vec3d& p1, const Vec3d& p2)
|
[tifcl](const Vec3d& p1, const Vec3d& p2)
|
||||||
{
|
{
|
||||||
|
tifcl();
|
||||||
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
|
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
|
||||||
});
|
});
|
||||||
|
|
||||||
cl_centroids.push_back(cid);
|
assert(lcid >= 0);
|
||||||
|
auto cid = unsigned(lcid);
|
||||||
|
|
||||||
|
cl_centroids.push_back(unsigned(cid));
|
||||||
|
|
||||||
unsigned hid = gndidx[cl[cid]]; // Head index
|
unsigned hid = gndidx[cl[cid]]; // Head index
|
||||||
Head& h = result.head(hid);
|
Head& h = result.head(hid);
|
||||||
@ -1262,12 +1309,13 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
// now we will go through the clusters ones again and connect the
|
// now we will go through the clusters ones again and connect the
|
||||||
// sidepoints with the cluster centroid (which is a ground pillar)
|
// sidepoints with the cluster centroid (which is a ground pillar)
|
||||||
// or a nearby pillar if the centroid is unreachable.
|
// or a nearby pillar if the centroid is unreachable.
|
||||||
long ci = 0;
|
size_t ci = 0;
|
||||||
for(auto cl : gnd_clusters) {
|
for(auto cl : gnd_clusters) { tifcl();
|
||||||
|
|
||||||
auto cidx = cl_centroids[ci];
|
auto cidx = cl_centroids[ci];
|
||||||
cl_centroids[ci++] = cl[cidx];
|
cl_centroids[ci++] = cl[cidx];
|
||||||
|
|
||||||
long index_to_heads = gndidx[cl[cidx]];
|
size_t index_to_heads = gndidx[cl[cidx]];
|
||||||
auto& head = result.head(index_to_heads);
|
auto& head = result.head(index_to_heads);
|
||||||
|
|
||||||
Vec3d startpoint = head.junction_point();
|
Vec3d startpoint = head.junction_point();
|
||||||
@ -1275,7 +1323,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
|
|
||||||
// Create the central pillar of the cluster with its base on the
|
// Create the central pillar of the cluster with its base on the
|
||||||
// ground
|
// ground
|
||||||
result.add_pillar(index_to_heads, endpoint, pradius)
|
result.add_pillar(long(index_to_heads), endpoint, pradius)
|
||||||
.add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
.add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
||||||
|
|
||||||
// Process side point in current cluster
|
// Process side point in current cluster
|
||||||
@ -1326,12 +1374,11 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
return nearest_id;
|
return nearest_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
for(auto c : cl) {
|
for(auto c : cl) { tifcl();
|
||||||
auto& sidehead = result.head(gndidx[c]);
|
auto& sidehead = result.head(gndidx[c]);
|
||||||
sidehead.transform();
|
sidehead.transform();
|
||||||
|
|
||||||
Vec3d jsh = sidehead.junction_point();
|
Vec3d jsh = sidehead.junction_point();
|
||||||
// Vec3d jp2d = {jsh(X), jsh(Y), gndlvl};
|
|
||||||
SpatIndex spindex = pheadindex;
|
SpatIndex spindex = pheadindex;
|
||||||
long nearest_id = search_nearest(spindex, jsh);
|
long nearest_id = search_nearest(spindex, jsh);
|
||||||
|
|
||||||
@ -1344,11 +1391,11 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
||||||
|
|
||||||
// connects to ground, eligible for bridging
|
// connects to ground, eligible for bridging
|
||||||
cl_centroids.emplace_back(sidehead.id);
|
cl_centroids.emplace_back(c);
|
||||||
} else {
|
} else {
|
||||||
// Creating the bridge to the nearest pillar
|
// Creating the bridge to the nearest pillar
|
||||||
|
|
||||||
const Head& nearhead = result.heads()[nearest_id];
|
const Head& nearhead = result.heads()[size_t(nearest_id)];
|
||||||
Vec3d jp = jsh;
|
Vec3d jp = jsh;
|
||||||
Vec3d jh = nearhead.junction_point();
|
Vec3d jh = nearhead.junction_point();
|
||||||
|
|
||||||
@ -1393,6 +1440,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
ClusterEl ring;
|
ClusterEl ring;
|
||||||
|
|
||||||
while(!rem.empty()) { // loop until all the points belong to some ring
|
while(!rem.empty()) { // loop until all the points belong to some ring
|
||||||
|
tifcl();
|
||||||
std::sort(rem.begin(), rem.end());
|
std::sort(rem.begin(), rem.end());
|
||||||
|
|
||||||
auto newring = pts_convex_hull(rem,
|
auto newring = pts_convex_hull(rem,
|
||||||
@ -1406,7 +1454,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
SpatIndex innerring;
|
SpatIndex innerring;
|
||||||
for(unsigned i : newring) {
|
for(unsigned i : newring) {
|
||||||
const Pillar& pill = result.head_pillar(gndidx[i]);
|
const Pillar& pill = result.head_pillar(gndidx[i]);
|
||||||
innerring.insert(pill.endpoint, pill.id);
|
assert(pill.id >= 0);
|
||||||
|
innerring.insert(pill.endpoint, unsigned(pill.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all pillars in the outer ring find the closest in the
|
// For all pillars in the outer ring find the closest in the
|
||||||
@ -1452,14 +1501,14 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto routing_nongnd_fn = [](
|
auto routing_nongnd_fn = [tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const std::vector<double>& gndheight,
|
const std::vector<double>& gndheight,
|
||||||
const IndexSet& nogndidx,
|
const IndexSet& nogndidx,
|
||||||
Result& result)
|
Result& result)
|
||||||
{
|
{
|
||||||
// TODO: connect these to the ground pillars if possible
|
// TODO: connect these to the ground pillars if possible
|
||||||
for(auto idx : nogndidx) {
|
for(auto idx : nogndidx) { tifcl();
|
||||||
auto& head = result.head(idx);
|
auto& head = result.head(idx);
|
||||||
head.transform();
|
head.transform();
|
||||||
|
|
||||||
@ -1484,7 +1533,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto process_headless = [](
|
auto process_headless = [tifcl](
|
||||||
const SupportConfig& cfg,
|
const SupportConfig& cfg,
|
||||||
const PointSet& headless_pts,
|
const PointSet& headless_pts,
|
||||||
const PointSet& headless_norm,
|
const PointSet& headless_norm,
|
||||||
@ -1499,7 +1548,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
|
|
||||||
// We will sink the pins into the model surface for a distance of 1/3 of
|
// We will sink the pins into the model surface for a distance of 1/3 of
|
||||||
// HWIDTH_MM
|
// HWIDTH_MM
|
||||||
for(int i = 0; i < headless_pts.rows(); i++) {
|
for(int i = 0; i < headless_pts.rows(); i++) { tifcl();
|
||||||
Vec3d sp = headless_pts.row(i);
|
Vec3d sp = headless_pts.row(i);
|
||||||
|
|
||||||
Vec3d n = headless_norm.row(i);
|
Vec3d n = headless_norm.row(i);
|
||||||
@ -1608,6 +1657,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
case HALT: pc = pc_prev; break;
|
case HALT: pc = pc_prev; break;
|
||||||
case DONE:
|
case DONE:
|
||||||
case ABORT: break;
|
case ABORT: break;
|
||||||
|
default: ;
|
||||||
}
|
}
|
||||||
ctl.statuscb(stepstate[pc], stepstr[pc]);
|
ctl.statuscb(stepstate[pc], stepstr[pc]);
|
||||||
};
|
};
|
||||||
@ -1656,22 +1706,19 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
|
|||||||
fullmesh.merge(get_pad());
|
fullmesh.merge(get_pad());
|
||||||
TriangleMeshSlicer slicer(&fullmesh);
|
TriangleMeshSlicer slicer(&fullmesh);
|
||||||
SlicedSupports ret;
|
SlicedSupports ret;
|
||||||
slicer.slice(heights, &ret, m_ctl.cancelfn);
|
slicer.slice(heights, &ret, get().ctl().cancelfn);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
|
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
|
||||||
double min_wall_thickness_mm,
|
const PoolConfig& pcfg) const
|
||||||
double min_wall_height_mm,
|
|
||||||
double max_merge_distance_mm,
|
|
||||||
double edge_radius_mm) const
|
|
||||||
{
|
{
|
||||||
PoolConfig pcfg;
|
// PoolConfig pcfg;
|
||||||
pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
||||||
pcfg.min_wall_height_mm = min_wall_height_mm;
|
// pcfg.min_wall_height_mm = min_wall_height_mm;
|
||||||
pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
// pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
||||||
pcfg.edge_radius_mm = edge_radius_mm;
|
// pcfg.edge_radius_mm = edge_radius_mm;
|
||||||
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
|
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1684,14 +1731,14 @@ SLASupportTree::SLASupportTree(const PointSet &points,
|
|||||||
const EigenMesh3D& emesh,
|
const EigenMesh3D& emesh,
|
||||||
const SupportConfig &cfg,
|
const SupportConfig &cfg,
|
||||||
const Controller &ctl):
|
const Controller &ctl):
|
||||||
m_impl(new Impl()), m_ctl(ctl)
|
m_impl(new Impl(ctl))
|
||||||
{
|
{
|
||||||
m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
|
m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
|
||||||
generate(points, emesh, cfg, ctl);
|
generate(points, emesh, cfg, ctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
SLASupportTree::SLASupportTree(const SLASupportTree &c):
|
SLASupportTree::SLASupportTree(const SLASupportTree &c):
|
||||||
m_impl(new Impl(*c.m_impl)), m_ctl(c.m_ctl) {}
|
m_impl(new Impl(*c.m_impl)) {}
|
||||||
|
|
||||||
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
||||||
{
|
{
|
||||||
@ -1701,5 +1748,8 @@ SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
|||||||
|
|
||||||
SLASupportTree::~SLASupportTree() {}
|
SLASupportTree::~SLASupportTree() {}
|
||||||
|
|
||||||
|
SLASupportsStoppedException::SLASupportsStoppedException():
|
||||||
|
std::runtime_error("") {}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,8 @@ struct SupportConfig {
|
|||||||
double object_elevation_mm = 10;
|
double object_elevation_mm = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PoolConfig;
|
||||||
|
|
||||||
/// A Control structure for the support calculation. Consists of the status
|
/// A Control structure for the support calculation. Consists of the status
|
||||||
/// indicator callback and the stop condition predicate.
|
/// indicator callback and the stop condition predicate.
|
||||||
struct Controller {
|
struct Controller {
|
||||||
@ -112,14 +114,13 @@ PointSet to_point_set(const std::vector<Vec3d>&);
|
|||||||
class SLASupportsStoppedException: public std::runtime_error {
|
class SLASupportsStoppedException: public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
SLASupportsStoppedException(): std::runtime_error("") {}
|
SLASupportsStoppedException();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The class containing mesh data for the generated supports.
|
/// The class containing mesh data for the generated supports.
|
||||||
class SLASupportTree {
|
class SLASupportTree {
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_impl;
|
std::unique_ptr<Impl> m_impl;
|
||||||
Controller m_ctl;
|
|
||||||
|
|
||||||
Impl& get() { return *m_impl; }
|
Impl& get() { return *m_impl; }
|
||||||
const Impl& get() const { return *m_impl; }
|
const Impl& get() const { return *m_impl; }
|
||||||
@ -158,10 +159,7 @@ public:
|
|||||||
|
|
||||||
/// Adding the "pad" (base pool) under the supports
|
/// Adding the "pad" (base pool) under the supports
|
||||||
const TriangleMesh& add_pad(const SliceLayer& baseplate,
|
const TriangleMesh& add_pad(const SliceLayer& baseplate,
|
||||||
double min_wall_thickness_mm,
|
const PoolConfig& pcfg) const;
|
||||||
double min_wall_height_mm,
|
|
||||||
double max_merge_distance_mm,
|
|
||||||
double edge_radius_mm) const;
|
|
||||||
|
|
||||||
/// Get the pad geometry
|
/// Get the pad geometry
|
||||||
const TriangleMesh& get_pad() const;
|
const TriangleMesh& get_pad() const;
|
||||||
|
@ -89,8 +89,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh) {
|
|||||||
#ifdef IGL_COMPATIBLE
|
#ifdef IGL_COMPATIBLE
|
||||||
Eigen::VectorXd dists;
|
Eigen::VectorXd dists;
|
||||||
Eigen::VectorXi I;
|
Eigen::VectorXi I;
|
||||||
// Eigen::Matrix<double, Eigen::Dynamic, 1, Eigen::DontAlign> dists;
|
|
||||||
// Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::DontAlign> I;
|
|
||||||
PointSet C;
|
PointSet C;
|
||||||
|
|
||||||
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
||||||
@ -122,7 +120,7 @@ double ray_mesh_intersect(const Vec3d& s,
|
|||||||
igl::Hit hit;
|
igl::Hit hit;
|
||||||
hit.t = std::numeric_limits<float>::infinity();
|
hit.t = std::numeric_limits<float>::infinity();
|
||||||
igl::ray_mesh_intersect(s, dir, m.V, m.F, hit);
|
igl::ray_mesh_intersect(s, dir, m.V, m.F, hit);
|
||||||
return hit.t;
|
return double(hit.t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clustering a set of points by the given criteria
|
// Clustering a set of points by the given criteria
|
||||||
@ -208,7 +206,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs)
|
|||||||
{
|
{
|
||||||
Segments ret;
|
Segments ret;
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
pp.reserve(emesh.F.rows());
|
pp.reserve(size_t(emesh.F.rows()));
|
||||||
|
|
||||||
for (int i = 0; i < emesh.F.rows(); i++) {
|
for (int i = 0; i < emesh.F.rows(); i++) {
|
||||||
auto trindex = emesh.F.row(i);
|
auto trindex = emesh.F.row(i);
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
using SlicedModel = SlicedSupports;
|
|
||||||
using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
|
using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
|
||||||
|
|
||||||
class SLAPrintObject::SupportData {
|
class SLAPrintObject::SupportData {
|
||||||
@ -413,6 +412,12 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
|
|||||||
|
|
||||||
return scfg;
|
return scfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void swapXY(ExPolygon& expoly) {
|
||||||
|
for(auto& p : expoly.contour.points) std::swap(p(X), p(Y));
|
||||||
|
for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SLAPrint::process()
|
void SLAPrint::process()
|
||||||
@ -445,7 +450,7 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// Slicing the model object. This method is oversimplified and needs to
|
// Slicing the model object. This method is oversimplified and needs to
|
||||||
// be compared with the fff slicing algorithm for verification
|
// be compared with the fff slicing algorithm for verification
|
||||||
auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) {
|
auto slice_model = [this, ilh](SLAPrintObject& po) {
|
||||||
double lh = po.m_config.layer_height.getFloat();
|
double lh = po.m_config.layer_height.getFloat();
|
||||||
|
|
||||||
TriangleMesh mesh = po.transformed_mesh();
|
TriangleMesh mesh = po.transformed_mesh();
|
||||||
@ -469,7 +474,7 @@ void SLAPrint::process()
|
|||||||
for(float h = minZ + ilh; h < maxZ; h += flh)
|
for(float h = minZ + ilh; h < maxZ; h += flh)
|
||||||
if(h >= gnd) heights.emplace_back(h);
|
if(h >= gnd) heights.emplace_back(h);
|
||||||
|
|
||||||
auto& layers = po.m_model_slices;
|
auto& layers = po.m_model_slices; layers.clear();
|
||||||
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
|
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -504,8 +509,6 @@ void SLAPrint::process()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& emesh = po.m_supportdata->emesh;
|
|
||||||
auto& pts = po.m_supportdata->support_points; // nowhere filled yet
|
|
||||||
try {
|
try {
|
||||||
sla::SupportConfig scfg = make_support_cfg(po.m_config);
|
sla::SupportConfig scfg = make_support_cfg(po.m_config);
|
||||||
sla::Controller ctl;
|
sla::Controller ctl;
|
||||||
@ -528,8 +531,14 @@ void SLAPrint::process()
|
|||||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||||
|
|
||||||
po.m_supportdata->support_tree_ptr.reset(
|
po.m_supportdata->support_tree_ptr.reset(
|
||||||
new SLASupportTree(pts, emesh, scfg, ctl));
|
new SLASupportTree(po.m_supportdata->support_points,
|
||||||
|
po.m_supportdata->emesh, scfg, ctl));
|
||||||
|
|
||||||
|
// Create the unified mesh
|
||||||
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
|
set_status(-1, L("Visualizing supports"));
|
||||||
|
po.m_supportdata->support_tree_ptr->merged_mesh();
|
||||||
|
set_status(-1, L("Visualizing supports"), rc);
|
||||||
} catch(sla::SLASupportsStoppedException&) {
|
} catch(sla::SLASupportsStoppedException&) {
|
||||||
// no need to rethrow
|
// no need to rethrow
|
||||||
// throw_if_canceled();
|
// throw_if_canceled();
|
||||||
@ -560,18 +569,17 @@ void SLAPrint::process()
|
|||||||
auto&& trmesh = po.transformed_mesh();
|
auto&& trmesh = po.transformed_mesh();
|
||||||
|
|
||||||
// This call can get pretty time consuming
|
// This call can get pretty time consuming
|
||||||
if(elevation < pad_h) sla::base_plate(trmesh, bp,
|
auto thrfn = [this](){ throw_if_canceled(); };
|
||||||
float(pad_h), float(lh));
|
|
||||||
|
|
||||||
po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er);
|
if(elevation < pad_h)
|
||||||
|
sla::base_plate(trmesh, bp, float(pad_h), float(lh),
|
||||||
|
thrfn);
|
||||||
|
|
||||||
|
pcfg.throw_on_cancel = thrfn;
|
||||||
|
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the base pool (which means also the support tree) is
|
po.throw_if_canceled();
|
||||||
// done, do a refresh when indicating progress. Now the
|
|
||||||
// geometries for the supports and the optional base pad are
|
|
||||||
// ready. We can grant access for the control thread to read
|
|
||||||
// the geometries, but first we have to update the caches:
|
|
||||||
po.support_mesh(); /*po->pad_mesh();*/
|
|
||||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
set_status(-1, L("Visualizing supports"), rc);
|
set_status(-1, L("Visualizing supports"), rc);
|
||||||
};
|
};
|
||||||
@ -589,12 +597,11 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// We have the layer polygon collection but we need to unite them into
|
// We have the layer polygon collection but we need to unite them into
|
||||||
// an index where the key is the height level in discrete levels (clipper)
|
// an index where the key is the height level in discrete levels (clipper)
|
||||||
auto index_slices = [this, ilh, ilhd](SLAPrintObject& po) {
|
auto index_slices = [ilhd](SLAPrintObject& po) {
|
||||||
po.m_slice_index.clear();
|
po.m_slice_index.clear();
|
||||||
auto sih = LevelID(scale_(ilh));
|
auto sih = LevelID(scale_(ilhd));
|
||||||
|
|
||||||
// For all print objects, go through its initial layers and place them
|
// Establish the slice grid boundaries
|
||||||
// into the layers hash
|
|
||||||
auto bb = po.transformed_mesh().bounding_box();
|
auto bb = po.transformed_mesh().bounding_box();
|
||||||
double modelgnd = bb.min(Z);
|
double modelgnd = bb.min(Z);
|
||||||
double elevation = po.get_elevation();
|
double elevation = po.get_elevation();
|
||||||
@ -610,49 +617,46 @@ void SLAPrint::process()
|
|||||||
// It is important that the next levels match the levels in
|
// It is important that the next levels match the levels in
|
||||||
// model_slice method. Only difference is that here it works with
|
// model_slice method. Only difference is that here it works with
|
||||||
// scaled coordinates
|
// scaled coordinates
|
||||||
auto& levelids = po.m_level_ids; levelids.clear();
|
po.m_level_ids.clear();
|
||||||
if(sminZ >= smodelgnd) levelids.emplace_back(sminZ);
|
if(sminZ >= smodelgnd) po.m_level_ids.emplace_back(sminZ);
|
||||||
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
||||||
if(h >= smodelgnd) levelids.emplace_back(h);
|
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
|
||||||
|
|
||||||
SlicedModel & oslices = po.m_model_slices;
|
std::vector<ExPolygons>& oslices = po.m_model_slices;
|
||||||
|
|
||||||
// If everything went well this code should not run at all, but
|
// If everything went well this code should not run at all, but
|
||||||
// let's be robust...
|
// let's be robust...
|
||||||
// assert(levelids.size() == oslices.size());
|
// assert(levelids.size() == oslices.size());
|
||||||
if(levelids.size() < oslices.size()) { // extend the levels until...
|
if(po.m_level_ids.size() < oslices.size()) { // extend the levels until...
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning)
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
<< "Height level mismatch at rasterization!\n";
|
<< "Height level mismatch at rasterization!\n";
|
||||||
|
|
||||||
LevelID lastlvl = levelids.back();
|
LevelID lastlvl = po.m_level_ids.back();
|
||||||
while(levelids.size() < oslices.size()) {
|
while(po.m_level_ids.size() < oslices.size()) {
|
||||||
lastlvl += slh;
|
lastlvl += slh;
|
||||||
levelids.emplace_back(lastlvl);
|
po.m_level_ids.emplace_back(lastlvl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut for empty index into the slice vectors
|
for(size_t i = 0; i < oslices.size(); ++i) {
|
||||||
static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE;
|
LevelID h = po.m_level_ids[i];
|
||||||
|
|
||||||
for(int i = 0; i < oslices.size(); ++i) {
|
|
||||||
LevelID h = levelids[i];
|
|
||||||
|
|
||||||
float fh = float(double(h) * SCALING_FACTOR);
|
float fh = float(double(h) * SCALING_FACTOR);
|
||||||
|
|
||||||
// now for the public slice index:
|
// now for the public slice index:
|
||||||
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
||||||
// There should be only one slice layer for each print object
|
// There should be only one slice layer for each print object
|
||||||
assert(sr.model_slices_idx == EMPTY_SLICE);
|
assert(sr.model_slices_idx == SLAPrintObject::SliceRecord::NONE);
|
||||||
sr.model_slices_idx = i;
|
sr.model_slices_idx = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(po.m_supportdata) { // deal with the support slices if present
|
if(po.m_supportdata) { // deal with the support slices if present
|
||||||
auto& sslices = po.m_supportdata->support_slices;
|
std::vector<ExPolygons>& sslices = po.m_supportdata->support_slices;
|
||||||
po.m_supportdata->level_ids.clear();
|
po.m_supportdata->level_ids.clear();
|
||||||
po.m_supportdata->level_ids.reserve(sslices.size());
|
po.m_supportdata->level_ids.reserve(sslices.size());
|
||||||
|
|
||||||
for(int i = 0; i < sslices.size(); ++i) {
|
for(int i = 0; i < int(sslices.size()); ++i) {
|
||||||
int a = i == 0 ? 0 : 1;
|
int a = i == 0 ? 0 : 1;
|
||||||
int b = i == 0 ? 0 : i - 1;
|
int b = i == 0 ? 0 : i - 1;
|
||||||
LevelID h = sminZ + a * sih + b * slh;
|
LevelID h = sminZ + a * sih + b * slh;
|
||||||
@ -661,16 +665,14 @@ void SLAPrint::process()
|
|||||||
float fh = float(double(h) * SCALING_FACTOR);
|
float fh = float(double(h) * SCALING_FACTOR);
|
||||||
|
|
||||||
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
||||||
assert(sr.support_slices_idx == EMPTY_SLICE);
|
assert(sr.support_slices_idx == SLAPrintObject::SliceRecord::NONE);
|
||||||
sr.support_slices_idx = i;
|
sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& levels = m_printer_input;
|
|
||||||
|
|
||||||
// Rasterizing the model objects, and their supports
|
// Rasterizing the model objects, and their supports
|
||||||
auto rasterize = [this, ilh, ilhd, max_objstatus, &levels]() {
|
auto rasterize = [this, max_objstatus]() {
|
||||||
if(canceled()) return;
|
if(canceled()) return;
|
||||||
|
|
||||||
// clear the rasterizer input
|
// clear the rasterizer input
|
||||||
@ -678,32 +680,39 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
for(SLAPrintObject * o : m_objects) {
|
for(SLAPrintObject * o : m_objects) {
|
||||||
auto& po = *o;
|
auto& po = *o;
|
||||||
SlicedModel & oslices = po.m_model_slices;
|
std::vector<ExPolygons>& oslices = po.m_model_slices;
|
||||||
|
|
||||||
// We need to adjust the min Z level of the slices to be zero
|
// We need to adjust the min Z level of the slices to be zero
|
||||||
LevelID smfirst = po.m_supportdata? po.m_supportdata->level_ids.front() : 0;
|
LevelID smfirst =
|
||||||
LevelID mfirst = po.m_level_ids.front();
|
po.m_supportdata && !po.m_supportdata->level_ids.empty() ?
|
||||||
|
po.m_supportdata->level_ids.front() : 0;
|
||||||
|
LevelID mfirst = po.m_level_ids.empty()? 0 : po.m_level_ids.front();
|
||||||
LevelID gndlvl = -(std::min(smfirst, mfirst));
|
LevelID gndlvl = -(std::min(smfirst, mfirst));
|
||||||
|
|
||||||
// now merge this object's support and object slices with the rest
|
// now merge this object's support and object slices with the rest
|
||||||
// of the print object slices
|
// of the print object slices
|
||||||
|
|
||||||
for(int i = 0; i < oslices.size(); ++i) {
|
for(size_t i = 0; i < oslices.size(); ++i) {
|
||||||
auto& lyrs = levels[gndlvl + po.m_level_ids[i]];
|
auto& lyrs = m_printer_input[gndlvl + po.m_level_ids[i]];
|
||||||
lyrs.emplace_back(oslices[i], po.m_instances);
|
lyrs.emplace_back(oslices[i], po.m_instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!po.m_supportdata) continue;
|
if(!po.m_supportdata) continue;
|
||||||
auto& sslices = po.m_supportdata->support_slices;
|
std::vector<ExPolygons>& sslices = po.m_supportdata->support_slices;
|
||||||
for(int i = 0; i < sslices.size(); ++i) {
|
for(size_t i = 0; i < sslices.size(); ++i) {
|
||||||
auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]];
|
LayerRefs& lyrs =
|
||||||
|
m_printer_input[gndlvl + po.m_supportdata->level_ids[i]];
|
||||||
lyrs.emplace_back(sslices[i], po.m_instances);
|
lyrs.emplace_back(sslices[i], po.m_instances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the keys
|
// collect all the keys
|
||||||
std::vector<long long> keys; keys.reserve(levels.size());
|
std::vector<long long> keys; keys.reserve(m_printer_input.size());
|
||||||
for(auto& e : levels) keys.emplace_back(e.first);
|
for(auto& e : m_printer_input) keys.emplace_back(e.first);
|
||||||
|
|
||||||
|
// If the raster has vertical orientation, we will flip the coordinates
|
||||||
|
bool flpXY = m_printer_config.display_orientation.getInt() ==
|
||||||
|
SLADisplayOrientation::sladoPortrait;
|
||||||
|
|
||||||
{ // create a raster printer for the current print parameters
|
{ // create a raster printer for the current print parameters
|
||||||
// I don't know any better
|
// I don't know any better
|
||||||
@ -713,18 +722,22 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
double w = printcfg.display_width.getFloat();
|
double w = printcfg.display_width.getFloat();
|
||||||
double h = printcfg.display_height.getFloat();
|
double h = printcfg.display_height.getFloat();
|
||||||
unsigned pw = printcfg.display_pixels_x.getInt();
|
auto pw = unsigned(printcfg.display_pixels_x.getInt());
|
||||||
unsigned ph = printcfg.display_pixels_y.getInt();
|
auto ph = unsigned(printcfg.display_pixels_y.getInt());
|
||||||
double lh = ocfg.layer_height.getFloat();
|
double lh = ocfg.layer_height.getFloat();
|
||||||
double exp_t = matcfg.exposure_time.getFloat();
|
double exp_t = matcfg.exposure_time.getFloat();
|
||||||
double iexp_t = matcfg.initial_exposure_time.getFloat();
|
double iexp_t = matcfg.initial_exposure_time.getFloat();
|
||||||
|
|
||||||
m_printer.reset(new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t));
|
if(flpXY) { std::swap(w, h); std::swap(pw, ph); }
|
||||||
|
|
||||||
|
m_printer.reset(new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t,
|
||||||
|
flpXY? SLAPrinter::RO_PORTRAIT :
|
||||||
|
SLAPrinter::RO_LANDSCAPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate space for all the layers
|
// Allocate space for all the layers
|
||||||
SLAPrinter& printer = *m_printer;
|
SLAPrinter& printer = *m_printer;
|
||||||
auto lvlcnt = unsigned(levels.size());
|
auto lvlcnt = unsigned(m_printer_input.size());
|
||||||
printer.layers(lvlcnt);
|
printer.layers(lvlcnt);
|
||||||
|
|
||||||
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
||||||
@ -734,12 +747,12 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// procedure to process one height level. This will run in parallel
|
// procedure to process one height level. This will run in parallel
|
||||||
auto lvlfn =
|
auto lvlfn =
|
||||||
[this, &slck, &keys, &levels, &printer, slot, sd, ist, &pst]
|
[this, &slck, &keys, &printer, slot, sd, ist, &pst, flpXY]
|
||||||
(unsigned level_id)
|
(unsigned level_id)
|
||||||
{
|
{
|
||||||
if(canceled()) return;
|
if(canceled()) return;
|
||||||
|
|
||||||
LayerRefs& lrange = levels[keys[level_id]];
|
LayerRefs& lrange = m_printer_input[keys[level_id]];
|
||||||
|
|
||||||
// Switch to the appropriate layer in the printer
|
// Switch to the appropriate layer in the printer
|
||||||
printer.begin_layer(level_id);
|
printer.begin_layer(level_id);
|
||||||
@ -754,8 +767,9 @@ void SLAPrint::process()
|
|||||||
for(ExPolygon slice : sl) {
|
for(ExPolygon slice : sl) {
|
||||||
// The order is important here:
|
// The order is important here:
|
||||||
// apply rotation before translation...
|
// apply rotation before translation...
|
||||||
slice.rotate(cp.rotation);
|
slice.rotate(double(cp.rotation));
|
||||||
slice.translate(cp.shift(X), cp.shift(Y));
|
slice.translate(cp.shift(X), cp.shift(Y));
|
||||||
|
if(flpXY) swapXY(slice);
|
||||||
printer.draw_polygon(slice, level_id);
|
printer.draw_polygon(slice, level_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -765,7 +779,7 @@ void SLAPrint::process()
|
|||||||
printer.finish_layer(level_id);
|
printer.finish_layer(level_id);
|
||||||
|
|
||||||
// Status indication
|
// Status indication
|
||||||
auto st = ist + unsigned(sd*level_id*slot/levels.size());
|
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
|
||||||
{ std::lock_guard<SpinMutex> lck(slck);
|
{ std::lock_guard<SpinMutex> lck(slck);
|
||||||
if( st > pst) {
|
if( st > pst) {
|
||||||
set_status(int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
set_status(int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
||||||
@ -886,6 +900,7 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||||||
"display_height",
|
"display_height",
|
||||||
"display_pixels_x",
|
"display_pixels_x",
|
||||||
"display_pixels_y",
|
"display_pixels_y",
|
||||||
|
"display_orientation",
|
||||||
"printer_correction"
|
"printer_correction"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -926,6 +941,18 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if an object step is done on all objects and there's at least one object.
|
||||||
|
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
||||||
|
{
|
||||||
|
if (m_objects.empty())
|
||||||
|
return false;
|
||||||
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
|
for (const SLAPrintObject *object : m_objects)
|
||||||
|
if (! object->m_state.is_done_unguarded(step))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||||
Inherited(print, model_object),
|
Inherited(print, model_object),
|
||||||
m_stepmask(slaposCount, true),
|
m_stepmask(slaposCount, true),
|
||||||
@ -1098,7 +1125,9 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
|||||||
const TriangleMesh& SLAPrintObject::support_mesh() const
|
const TriangleMesh& SLAPrintObject::support_mesh() const
|
||||||
{
|
{
|
||||||
if(m_config.supports_enable.getBool() && m_supportdata &&
|
if(m_config.supports_enable.getBool() && m_supportdata &&
|
||||||
m_supportdata->support_tree_ptr) return m_supportdata->support_tree_ptr->merged_mesh();
|
m_supportdata->support_tree_ptr) {
|
||||||
|
return m_supportdata->support_tree_ptr->merged_mesh();
|
||||||
|
}
|
||||||
|
|
||||||
return EMPTY_MESH;
|
return EMPTY_MESH;
|
||||||
}
|
}
|
||||||
|
@ -177,14 +177,18 @@ private: // Prevents erroneous use by other classes.
|
|||||||
public:
|
public:
|
||||||
SLAPrint(): m_stepmask(slapsCount, true) {}
|
SLAPrint(): m_stepmask(slapsCount, true) {}
|
||||||
|
|
||||||
virtual ~SLAPrint() { this->clear(); }
|
virtual ~SLAPrint() override { this->clear(); }
|
||||||
|
|
||||||
PrinterTechnology technology() const noexcept { return ptSLA; }
|
PrinterTechnology technology() const noexcept override { return ptSLA; }
|
||||||
|
|
||||||
void clear() override;
|
void clear() override;
|
||||||
bool empty() const override { return m_objects.empty(); }
|
bool empty() const override { return m_objects.empty(); }
|
||||||
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
||||||
void process() override;
|
void process() override;
|
||||||
|
// Returns true if an object step is done on all objects and there's at least one object.
|
||||||
|
bool is_step_done(SLAPrintObjectStep step) const;
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
bool finished() const override { return this->is_step_done(slaposIndexSlices); }
|
||||||
|
|
||||||
template<class Fmt> void export_raster(const std::string& fname) {
|
template<class Fmt> void export_raster(const std::string& fname) {
|
||||||
if(m_printer) m_printer->save<Fmt>(fname);
|
if(m_printer) m_printer->save<Fmt>(fname);
|
||||||
|
@ -458,6 +458,8 @@ Polygons collect_slices_outer(const Layer &layer)
|
|||||||
class SupportGridPattern
|
class SupportGridPattern
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise
|
||||||
|
// the selection by island_samples (see the island_samples() method) will not work!
|
||||||
SupportGridPattern(
|
SupportGridPattern(
|
||||||
// Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
|
// Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
|
||||||
const Polygons &support_polygons,
|
const Polygons &support_polygons,
|
||||||
@ -485,6 +487,18 @@ public:
|
|||||||
bbox.align_to_grid(grid_resolution);
|
bbox.align_to_grid(grid_resolution);
|
||||||
m_grid.set_bbox(bbox);
|
m_grid.set_bbox(bbox);
|
||||||
m_grid.create(*m_support_polygons, grid_resolution);
|
m_grid.create(*m_support_polygons, grid_resolution);
|
||||||
|
#if 0
|
||||||
|
if (m_grid.has_intersecting_edges()) {
|
||||||
|
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
|
||||||
|
m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
|
||||||
|
m_support_polygons = &m_support_polygons_rotated;
|
||||||
|
m_grid.set_bbox(bbox);
|
||||||
|
m_grid.create(*m_support_polygons, grid_resolution);
|
||||||
|
// assert(! m_grid.has_intersecting_edges());
|
||||||
|
printf("SupportGridPattern: fixing polygons with intersection %s\n",
|
||||||
|
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
m_grid.calculate_sdf();
|
m_grid.calculate_sdf();
|
||||||
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
|
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
|
||||||
// polygons if ever these polygons get split into parts by the trimming polygons.
|
// polygons if ever these polygons get split into parts by the trimming polygons.
|
||||||
@ -499,9 +513,12 @@ public:
|
|||||||
{
|
{
|
||||||
// Generate islands, so each island may be tested for overlap with m_island_samples.
|
// Generate islands, so each island may be tested for overlap with m_island_samples.
|
||||||
assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
|
assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
|
||||||
ExPolygons islands = diff_ex(
|
#ifdef SLIC3R_DEBUG
|
||||||
m_grid.contours_simplified(offset_in_grid, fill_holes),
|
Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
|
||||||
*m_trimming_polygons, false);
|
ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
|
||||||
|
#else
|
||||||
|
ExPolygons islands = diff_ex(m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Extract polygons, which contain some of the m_island_samples.
|
// Extract polygons, which contain some of the m_island_samples.
|
||||||
Polygons out;
|
Polygons out;
|
||||||
@ -551,7 +568,10 @@ public:
|
|||||||
bbox.merge(get_extents(islands));
|
bbox.merge(get_extents(islands));
|
||||||
if (!out.empty())
|
if (!out.empty())
|
||||||
bbox.merge(get_extents(out));
|
bbox.merge(get_extents(out));
|
||||||
|
if (!support_polygons_simplified.empty())
|
||||||
|
bbox.merge(get_extents(support_polygons_simplified));
|
||||||
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox);
|
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox);
|
||||||
|
svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
|
||||||
svg.draw(islands, "red", 0.5f);
|
svg.draw(islands, "red", 0.5f);
|
||||||
svg.draw(union_ex(out), "green", 0.5f);
|
svg.draw(union_ex(out), "green", 0.5f);
|
||||||
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
|
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
|
||||||
@ -568,7 +588,121 @@ public:
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
void serialize(const std::string &path)
|
||||||
|
{
|
||||||
|
FILE *file = ::fopen(path.c_str(), "wb");
|
||||||
|
::fwrite(&m_support_spacing, 8, 1, file);
|
||||||
|
::fwrite(&m_support_angle, 8, 1, file);
|
||||||
|
uint32_t n_polygons = m_support_polygons->size();
|
||||||
|
::fwrite(&n_polygons, 4, 1, file);
|
||||||
|
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||||
|
const Polygon &poly = (*m_support_polygons)[i];
|
||||||
|
uint32_t n_points = poly.size();
|
||||||
|
::fwrite(&n_points, 4, 1, file);
|
||||||
|
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||||
|
const Point &pt = poly.points[j];
|
||||||
|
::fwrite(&pt.x, sizeof(coord_t), 1, file);
|
||||||
|
::fwrite(&pt.y, sizeof(coord_t), 1, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n_polygons = m_trimming_polygons->size();
|
||||||
|
::fwrite(&n_polygons, 4, 1, file);
|
||||||
|
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||||
|
const Polygon &poly = (*m_trimming_polygons)[i];
|
||||||
|
uint32_t n_points = poly.size();
|
||||||
|
::fwrite(&n_points, 4, 1, file);
|
||||||
|
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||||
|
const Point &pt = poly.points[j];
|
||||||
|
::fwrite(&pt.x, sizeof(coord_t), 1, file);
|
||||||
|
::fwrite(&pt.y, sizeof(coord_t), 1, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SupportGridPattern deserialize(const std::string &path, int which = -1)
|
||||||
|
{
|
||||||
|
SupportGridPattern out;
|
||||||
|
out.deserialize_(path, which);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialization constructor
|
||||||
|
bool deserialize_(const std::string &path, int which = -1)
|
||||||
|
{
|
||||||
|
FILE *file = ::fopen(path.c_str(), "rb");
|
||||||
|
if (file == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_support_polygons = &m_support_polygons_deserialized;
|
||||||
|
m_trimming_polygons = &m_trimming_polygons_deserialized;
|
||||||
|
|
||||||
|
::fread(&m_support_spacing, 8, 1, file);
|
||||||
|
::fread(&m_support_angle, 8, 1, file);
|
||||||
|
//FIXME
|
||||||
|
//m_support_spacing *= 0.01 / 2;
|
||||||
|
uint32_t n_polygons;
|
||||||
|
::fread(&n_polygons, 4, 1, file);
|
||||||
|
m_support_polygons_deserialized.reserve(n_polygons);
|
||||||
|
int32_t scale = 1;
|
||||||
|
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||||
|
Polygon poly;
|
||||||
|
uint32_t n_points;
|
||||||
|
::fread(&n_points, 4, 1, file);
|
||||||
|
poly.points.reserve(n_points);
|
||||||
|
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||||
|
coord_t x, y;
|
||||||
|
::fread(&x, sizeof(coord_t), 1, file);
|
||||||
|
::fread(&y, sizeof(coord_t), 1, file);
|
||||||
|
poly.points.emplace_back(Point(x * scale, y * scale));
|
||||||
|
}
|
||||||
|
if (which == -1 || which == i)
|
||||||
|
m_support_polygons_deserialized.emplace_back(std::move(poly));
|
||||||
|
printf("Polygon %d, area: %lf\n", i, area(poly.points));
|
||||||
|
}
|
||||||
|
::fread(&n_polygons, 4, 1, file);
|
||||||
|
m_trimming_polygons_deserialized.reserve(n_polygons);
|
||||||
|
for (uint32_t i = 0; i < n_polygons; ++ i) {
|
||||||
|
Polygon poly;
|
||||||
|
uint32_t n_points;
|
||||||
|
::fread(&n_points, 4, 1, file);
|
||||||
|
poly.points.reserve(n_points);
|
||||||
|
for (uint32_t j = 0; j < n_points; ++ j) {
|
||||||
|
coord_t x, y;
|
||||||
|
::fread(&x, sizeof(coord_t), 1, file);
|
||||||
|
::fread(&y, sizeof(coord_t), 1, file);
|
||||||
|
poly.points.emplace_back(Point(x * scale, y * scale));
|
||||||
|
}
|
||||||
|
m_trimming_polygons_deserialized.emplace_back(std::move(poly));
|
||||||
|
}
|
||||||
|
::fclose(file);
|
||||||
|
|
||||||
|
m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false);
|
||||||
|
//m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false));
|
||||||
|
|
||||||
|
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
|
||||||
|
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
|
||||||
|
BoundingBox bbox = get_extents(*m_support_polygons);
|
||||||
|
bbox.offset(20);
|
||||||
|
bbox.align_to_grid(grid_resolution);
|
||||||
|
m_grid.set_bbox(bbox);
|
||||||
|
m_grid.create(*m_support_polygons, grid_resolution);
|
||||||
|
m_grid.calculate_sdf();
|
||||||
|
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
|
||||||
|
// polygons if ever these polygons get split into parts by the trimming polygons.
|
||||||
|
m_island_samples = island_samples(*m_support_polygons);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Polygons& support_polygons() const { return *m_support_polygons; }
|
||||||
|
const Polygons& trimming_polygons() const { return *m_trimming_polygons; }
|
||||||
|
const EdgeGrid::Grid& grid() const { return m_grid; }
|
||||||
|
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SupportGridPattern() {}
|
||||||
SupportGridPattern& operator=(const SupportGridPattern &rhs);
|
SupportGridPattern& operator=(const SupportGridPattern &rhs);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@ -639,6 +773,12 @@ private:
|
|||||||
// Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
|
// Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
|
||||||
// to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
|
// to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
|
||||||
Points m_island_samples;
|
Points m_island_samples;
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
// support for deserialization of m_support_polygons, m_trimming_polygons
|
||||||
|
Polygons m_support_polygons_deserialized;
|
||||||
|
Polygons m_trimming_polygons_deserialized;
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace SupportMaterialInternal {
|
namespace SupportMaterialInternal {
|
||||||
@ -783,17 +923,40 @@ namespace SupportMaterialInternal {
|
|||||||
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
|
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
|
||||||
polygons_append(bridges, surface.expolygon);
|
polygons_append(bridges, surface.expolygon);
|
||||||
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
|
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
|
||||||
contact_polygons = diff(contact_polygons, bridges, true);
|
// Remove the unsupported ends of the bridges from the bridged areas.
|
||||||
// Add the bridge anchors into the region.
|
|
||||||
//FIXME add supports at regular intervals to support long bridges!
|
//FIXME add supports at regular intervals to support long bridges!
|
||||||
polygons_append(contact_polygons,
|
bridges = diff(bridges,
|
||||||
intersection(
|
|
||||||
// Offset unsupported edges into polygons.
|
// Offset unsupported edges into polygons.
|
||||||
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
|
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||||
bridges));
|
// Remove bridged areas from the supported areas.
|
||||||
|
contact_polygons = diff(contact_polygons, bridges, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int Test()
|
||||||
|
{
|
||||||
|
// for (int i = 0; i < 30; ++ i)
|
||||||
|
{
|
||||||
|
int i = -1;
|
||||||
|
// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i);
|
||||||
|
// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i);
|
||||||
|
auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i);
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = grid.grid().intersecting_edges();
|
||||||
|
if (! intersections.empty())
|
||||||
|
printf("Intersections between contours!\n");
|
||||||
|
Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons());
|
||||||
|
Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false));
|
||||||
|
Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false));
|
||||||
|
Polygons extracted = grid.extract_support(scale_(0.21 / 2), true);
|
||||||
|
Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false));
|
||||||
|
printf("hu!");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int run_support_test = Test();
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
// Generate top contact layers supporting overhangs.
|
// Generate top contact layers supporting overhangs.
|
||||||
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
||||||
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
||||||
@ -1096,6 +1259,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise
|
||||||
|
// the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
|
||||||
SupportGridPattern support_grid_pattern(
|
SupportGridPattern support_grid_pattern(
|
||||||
// Support islands, to be stretched into a grid.
|
// Support islands, to be stretched into a grid.
|
||||||
contact_polygons,
|
contact_polygons,
|
||||||
@ -1114,9 +1279,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||||||
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
|
||||||
Polygons dense_interface_polygons = diff(overhang_polygons,
|
Polygons dense_interface_polygons = diff(overhang_polygons,
|
||||||
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||||
// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
|
||||||
if (! dense_interface_polygons.empty()) {
|
if (! dense_interface_polygons.empty()) {
|
||||||
//FIXME do it for the bridges only?
|
dense_interface_polygons =
|
||||||
|
// Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise
|
||||||
|
// the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work!
|
||||||
|
diff(
|
||||||
|
// Regularize the contour.
|
||||||
|
offset(dense_interface_polygons, no_interface_offset * 0.1f),
|
||||||
|
slices_margin_cached);
|
||||||
SupportGridPattern support_grid_pattern(
|
SupportGridPattern support_grid_pattern(
|
||||||
// Support islands, to be stretched into a grid.
|
// Support islands, to be stretched into a grid.
|
||||||
dense_interface_polygons,
|
dense_interface_polygons,
|
||||||
@ -1126,8 +1296,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||||||
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
|
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
|
||||||
Geometry::deg2rad(m_object_config->support_material_angle.value));
|
Geometry::deg2rad(m_object_config->support_material_angle.value));
|
||||||
new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
|
new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
{
|
||||||
|
support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z));
|
||||||
|
|
||||||
|
BoundingBox bbox = get_extents(contact_polygons);
|
||||||
|
bbox.merge(get_extents(new_layer.polygons));
|
||||||
|
::Slic3r::SVG svg(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
|
||||||
|
svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
|
||||||
|
svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
|
||||||
|
svg.draw(union_ex(dense_interface_polygons, false), "green", 0.5f);
|
||||||
|
svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
|
||||||
|
svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
{
|
||||||
|
BoundingBox bbox = get_extents(contact_polygons);
|
||||||
|
bbox.merge(get_extents(new_layer.polygons));
|
||||||
|
::Slic3r::SVG svg(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z));
|
||||||
|
svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f);
|
||||||
|
svg.draw(union_ex(contact_polygons, false), "blue", 0.5f);
|
||||||
|
svg.draw(union_ex(overhang_polygons, false), "green", 0.5f);
|
||||||
|
svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f);
|
||||||
|
svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f));
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
|
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
|
||||||
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
|
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
// Shows camera target in the 3D scene
|
// Shows camera target in the 3D scene
|
||||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||||
|
// Log debug messages to console when changing selection
|
||||||
|
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||||
|
|
||||||
//=============
|
//=============
|
||||||
// 1.42.0 techs
|
// 1.42.0 techs
|
||||||
|
@ -1212,21 +1212,11 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
|
||||||
//#ifdef _DEBUG
|
|
||||||
for (const Line &l : lines)
|
|
||||||
assert(l.a != l.b);
|
|
||||||
#endif /* _DEBUG */
|
|
||||||
|
|
||||||
remove_tangent_edges(lines);
|
struct OpenPolyline {
|
||||||
|
|
||||||
struct OpenPolyline {
|
|
||||||
OpenPolyline() {};
|
OpenPolyline() {};
|
||||||
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
|
||||||
start(start), end(end), points(std::move(points)), consumed(false) {}
|
start(start), end(end), points(std::move(points)), consumed(false) { this->length = Slic3r::length(this->points); }
|
||||||
void reverse() {
|
void reverse() {
|
||||||
std::swap(start, end);
|
std::swap(start, end);
|
||||||
std::reverse(points.begin(), points.end());
|
std::reverse(points.begin(), points.end());
|
||||||
@ -1234,10 +1224,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
IntersectionReference start;
|
IntersectionReference start;
|
||||||
IntersectionReference end;
|
IntersectionReference end;
|
||||||
Points points;
|
Points points;
|
||||||
|
double length;
|
||||||
bool consumed;
|
bool consumed;
|
||||||
};
|
};
|
||||||
std::vector<OpenPolyline> open_polylines;
|
|
||||||
{
|
// called by TriangleMeshSlicer::make_loops() to connect sliced triangles into closed loops and open polylines by the triangle connectivity.
|
||||||
|
// Only connects segments crossing triangles of the same orientation.
|
||||||
|
static void chain_lines_by_triangle_connectivity(std::vector<IntersectionLine> &lines, Polygons &loops, std::vector<OpenPolyline> &open_polylines)
|
||||||
|
{
|
||||||
// Build a map of lines by edge_a_id and a_id.
|
// Build a map of lines by edge_a_id and a_id.
|
||||||
std::vector<IntersectionLine*> by_edge_a_id;
|
std::vector<IntersectionLine*> by_edge_a_id;
|
||||||
std::vector<IntersectionLine*> by_a_id;
|
std::vector<IntersectionLine*> by_a_id;
|
||||||
@ -1312,7 +1306,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
|
||||||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
|
||||||
// The current loop is complete. Add it to the output.
|
// The current loop is complete. Add it to the output.
|
||||||
loops->emplace_back(std::move(loop_pts));
|
loops.emplace_back(std::move(loop_pts));
|
||||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
|
||||||
#endif
|
#endif
|
||||||
@ -1335,10 +1329,26 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
next_line->set_skip();
|
next_line->set_skip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process the open polylines.
|
std::vector<OpenPolyline*> open_polylines_sorted(std::vector<OpenPolyline> &open_polylines, bool update_lengths)
|
||||||
if (! open_polylines.empty()) {
|
{
|
||||||
|
std::vector<OpenPolyline*> out;
|
||||||
|
out.reserve(open_polylines.size());
|
||||||
|
for (OpenPolyline &opl : open_polylines)
|
||||||
|
if (! opl.consumed) {
|
||||||
|
if (update_lengths)
|
||||||
|
opl.length = Slic3r::length(opl.points);
|
||||||
|
out.emplace_back(&opl);
|
||||||
|
}
|
||||||
|
std::sort(out.begin(), out.end(), [](const OpenPolyline *lhs, const OpenPolyline *rhs){ return lhs->length > rhs->length; });
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices.
|
||||||
|
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||||
|
static void chain_open_polylines_exact(std::vector<OpenPolyline> &open_polylines, Polygons &loops, bool try_connect_reversed)
|
||||||
|
{
|
||||||
// Store the end points of open_polylines into vectors sorted
|
// Store the end points of open_polylines into vectors sorted
|
||||||
struct OpenPolylineEnd {
|
struct OpenPolylineEnd {
|
||||||
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||||
@ -1346,106 +1356,287 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
// Is it the start or end point?
|
// Is it the start or end point?
|
||||||
bool start;
|
bool start;
|
||||||
const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
|
const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
|
||||||
int point_id() const { return ipref().point_id; }
|
// Return a unique ID for the intersection point.
|
||||||
int edge_id () const { return ipref().edge_id; }
|
// Return a positive id for a point, or a negative id for an edge.
|
||||||
|
int id() const { const IntersectionReference &r = ipref(); return (r.point_id >= 0) ? r.point_id : - r.edge_id; }
|
||||||
|
bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
|
||||||
};
|
};
|
||||||
auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); };
|
auto by_id_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.id() < ope2.id(); };
|
||||||
auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); };
|
std::vector<OpenPolylineEnd> by_id;
|
||||||
std::vector<OpenPolylineEnd> by_edge_id;
|
by_id.reserve(2 * open_polylines.size());
|
||||||
std::vector<OpenPolylineEnd> by_point_id;
|
|
||||||
by_edge_id.reserve(2 * open_polylines.size());
|
|
||||||
by_point_id.reserve(2 * open_polylines.size());
|
|
||||||
for (OpenPolyline &opl : open_polylines) {
|
for (OpenPolyline &opl : open_polylines) {
|
||||||
if (opl.start.edge_id != -1)
|
if (opl.start.point_id != -1 || opl.start.edge_id != -1)
|
||||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, true));
|
by_id.emplace_back(OpenPolylineEnd(&opl, true));
|
||||||
if (opl.end.edge_id != -1)
|
if (try_connect_reversed && (opl.end.point_id != -1 || opl.end.edge_id != -1))
|
||||||
by_edge_id .emplace_back(OpenPolylineEnd(&opl, false));
|
by_id.emplace_back(OpenPolylineEnd(&opl, false));
|
||||||
if (opl.start.point_id != -1)
|
|
||||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, true));
|
|
||||||
if (opl.end.point_id != -1)
|
|
||||||
by_point_id.emplace_back(OpenPolylineEnd(&opl, false));
|
|
||||||
}
|
}
|
||||||
std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower);
|
std::sort(by_id.begin(), by_id.end(), by_id_lower);
|
||||||
std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower);
|
// Find an iterator to by_id_lower for the particular end of OpenPolyline (by comparing the OpenPolyline pointer and the start attribute).
|
||||||
|
auto find_polyline_end = [&by_id, by_id_lower](const OpenPolylineEnd &end) -> std::vector<OpenPolylineEnd>::iterator {
|
||||||
|
for (auto it = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
||||||
|
it != by_id.end() && it->id() == end.id(); ++ it)
|
||||||
|
if (*it == end)
|
||||||
|
return it;
|
||||||
|
return by_id.end();
|
||||||
|
};
|
||||||
// Try to connect the loops.
|
// Try to connect the loops.
|
||||||
for (OpenPolyline &opl : open_polylines) {
|
std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, false);
|
||||||
if (opl.consumed)
|
for (OpenPolyline *opl : sorted_by_length) {
|
||||||
|
if (opl->consumed)
|
||||||
continue;
|
continue;
|
||||||
opl.consumed = true;
|
opl->consumed = true;
|
||||||
OpenPolylineEnd end(&opl, false);
|
OpenPolylineEnd end(opl, false);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// find a line starting where last one finishes
|
// find a line starting where last one finishes
|
||||||
OpenPolylineEnd* next_start = nullptr;
|
auto it_next_start = std::lower_bound(by_id.begin(), by_id.end(), end, by_id_lower);
|
||||||
if (end.edge_id() != -1) {
|
for (; it_next_start != by_id.end() && it_next_start->id() == end.id(); ++ it_next_start)
|
||||||
auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower);
|
if (! it_next_start->polyline->consumed)
|
||||||
if (it_begin != by_edge_id.end()) {
|
goto found;
|
||||||
auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower);
|
|
||||||
for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge)
|
|
||||||
if (! it_edge->polyline->consumed) {
|
|
||||||
next_start = &(*it_edge);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (next_start == nullptr && end.point_id() != -1) {
|
|
||||||
auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower);
|
|
||||||
if (it_begin != by_point_id.end()) {
|
|
||||||
auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower);
|
|
||||||
for (auto it_point = it_begin; it_point != it_end; ++ it_point)
|
|
||||||
if (! it_point->polyline->consumed) {
|
|
||||||
next_start = &(*it_point);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (next_start == nullptr) {
|
|
||||||
// The current loop could not be closed. Unmark the segment.
|
// The current loop could not be closed. Unmark the segment.
|
||||||
opl.consumed = false;
|
opl->consumed = false;
|
||||||
break;
|
break;
|
||||||
}
|
found:
|
||||||
// Attach this polyline to the end of the initial polyline.
|
// Attach this polyline to the end of the initial polyline.
|
||||||
if (next_start->start) {
|
if (it_next_start->start) {
|
||||||
auto it = next_start->polyline->points.begin();
|
auto it = it_next_start->polyline->points.begin();
|
||||||
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
|
std::copy(++ it, it_next_start->polyline->points.end(), back_inserter(opl->points));
|
||||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end());
|
|
||||||
} else {
|
} else {
|
||||||
auto it = next_start->polyline->points.rbegin();
|
auto it = it_next_start->polyline->points.rbegin();
|
||||||
std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points));
|
std::copy(++ it, it_next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||||
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend());
|
}
|
||||||
|
opl->length += it_next_start->polyline->length;
|
||||||
|
// Mark the next polyline as consumed.
|
||||||
|
it_next_start->polyline->points.clear();
|
||||||
|
it_next_start->polyline->length = 0.;
|
||||||
|
it_next_start->polyline->consumed = true;
|
||||||
|
if (try_connect_reversed) {
|
||||||
|
// Running in a mode, where the polylines may be connected by mixing their orientations.
|
||||||
|
// Update the end point lookup structure after the end point of the current polyline was extended.
|
||||||
|
auto it_end = find_polyline_end(end);
|
||||||
|
auto it_next_end = find_polyline_end(OpenPolylineEnd(it_next_start->polyline, !it_next_start->start));
|
||||||
|
// Swap the end points of the current and next polyline, but keep the polyline ptr and the start flag.
|
||||||
|
std::swap(opl->end, it_next_end->start ? it_next_end->polyline->start : it_next_end->polyline->end);
|
||||||
|
// Swap the positions of OpenPolylineEnd structures in the sorted array to match their respective end point positions.
|
||||||
|
std::swap(*it_end, *it_next_end);
|
||||||
}
|
}
|
||||||
end = *next_start;
|
|
||||||
end.start = !end.start;
|
|
||||||
next_start->polyline->points.clear();
|
|
||||||
next_start->polyline->consumed = true;
|
|
||||||
// Check whether we closed this loop.
|
// Check whether we closed this loop.
|
||||||
const IntersectionReference &ip1 = opl.start;
|
if ((opl->start.edge_id != -1 && opl->start.edge_id == opl->end.edge_id) ||
|
||||||
const IntersectionReference &ip2 = end.ipref();
|
(opl->start.point_id != -1 && opl->start.point_id == opl->end.point_id)) {
|
||||||
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
|
|
||||||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
|
|
||||||
// The current loop is complete. Add it to the output.
|
// The current loop is complete. Add it to the output.
|
||||||
//assert(opl.points.front().point_id == opl.points.back().point_id);
|
//assert(opl->points.front().point_id == opl->points.back().point_id);
|
||||||
//assert(opl.points.front().edge_id == opl.points.back().edge_id);
|
//assert(opl->points.front().edge_id == opl->points.back().edge_id);
|
||||||
// Remove the duplicate last point.
|
// Remove the duplicate last point.
|
||||||
opl.points.pop_back();
|
opl->points.pop_back();
|
||||||
if (opl.points.size() >= 3) {
|
if (opl->points.size() >= 3) {
|
||||||
|
if (try_connect_reversed && area(opl->points) < 0)
|
||||||
// The closed polygon is patched from pieces with messed up orientation, therefore
|
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||||
// the orientation of the patched up polygon is not known.
|
// the orientation of the patched up polygon is not known.
|
||||||
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||||
double area = 0.;
|
std::reverse(opl->points.begin(), opl->points.end());
|
||||||
for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
|
loops.emplace_back(std::move(opl->points));
|
||||||
area += double(opl.points[j](0) + opl.points[i](0)) * double(opl.points[i](1) - opl.points[j](1));
|
|
||||||
if (area < 0)
|
|
||||||
std::reverse(opl.points.begin(), opl.points.end());
|
|
||||||
loops->emplace_back(std::move(opl.points));
|
|
||||||
}
|
}
|
||||||
opl.points.clear();
|
opl->points.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Continue with the current loop.
|
// Continue with the current loop.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by TriangleMeshSlicer::make_loops() to connect remaining open polylines across shared triangle edges and vertices,
|
||||||
|
// possibly closing small gaps.
|
||||||
|
// Depending on "try_connect_reversed", it may or may not connect segments crossing triangles of opposite orientation.
|
||||||
|
static void chain_open_polylines_close_gaps(std::vector<OpenPolyline> &open_polylines, Polygons &loops, double max_gap, bool try_connect_reversed)
|
||||||
|
{
|
||||||
|
const coord_t max_gap_scaled = (coord_t)scale_(max_gap);
|
||||||
|
|
||||||
|
// Sort the open polylines by their length, so the new loops will be seeded from longer chains.
|
||||||
|
// Update the polyline lengths, return only not yet consumed polylines.
|
||||||
|
std::vector<OpenPolyline*> sorted_by_length = open_polylines_sorted(open_polylines, true);
|
||||||
|
|
||||||
|
// Store the end points of open_polylines into ClosestPointInRadiusLookup<OpenPolylineEnd>.
|
||||||
|
struct OpenPolylineEnd {
|
||||||
|
OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
|
||||||
|
OpenPolyline *polyline;
|
||||||
|
// Is it the start or end point?
|
||||||
|
bool start;
|
||||||
|
const Point& point() const { return start ? polyline->points.front() : polyline->points.back(); }
|
||||||
|
bool operator==(const OpenPolylineEnd &rhs) const { return this->polyline == rhs.polyline && this->start == rhs.start; }
|
||||||
|
};
|
||||||
|
struct OpenPolylineEndAccessor {
|
||||||
|
const Point* operator()(const OpenPolylineEnd &pt) const { return pt.polyline->consumed ? nullptr : &pt.point(); }
|
||||||
|
};
|
||||||
|
typedef ClosestPointInRadiusLookup<OpenPolylineEnd, OpenPolylineEndAccessor> ClosestPointLookupType;
|
||||||
|
ClosestPointLookupType closest_end_point_lookup(max_gap_scaled);
|
||||||
|
for (OpenPolyline *opl : sorted_by_length) {
|
||||||
|
closest_end_point_lookup.insert(OpenPolylineEnd(opl, true));
|
||||||
|
if (try_connect_reversed)
|
||||||
|
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
||||||
}
|
}
|
||||||
|
// Try to connect the loops.
|
||||||
|
for (OpenPolyline *opl : sorted_by_length) {
|
||||||
|
if (opl->consumed)
|
||||||
|
continue;
|
||||||
|
OpenPolylineEnd end(opl, false);
|
||||||
|
if (try_connect_reversed)
|
||||||
|
// The end point of this polyline will be modified, thus the following entry will become invalid. Remove it.
|
||||||
|
closest_end_point_lookup.erase(end);
|
||||||
|
opl->consumed = true;
|
||||||
|
size_t n_segments_joined = 1;
|
||||||
|
for (;;) {
|
||||||
|
// Find a line starting where last one finishes, only return non-consumed open polylines (OpenPolylineEndAccessor returns null for consumed).
|
||||||
|
std::pair<const OpenPolylineEnd*, double> next_start_and_dist = closest_end_point_lookup.find(end.point());
|
||||||
|
const OpenPolylineEnd *next_start = next_start_and_dist.first;
|
||||||
|
// Check whether we closed this loop.
|
||||||
|
double current_loop_closing_distance2 = (opl->points.back() - opl->points.front()).cast<double>().squaredNorm();
|
||||||
|
bool loop_closed = current_loop_closing_distance2 < coordf_t(max_gap_scaled) * coordf_t(max_gap_scaled);
|
||||||
|
if (next_start != nullptr && loop_closed && current_loop_closing_distance2 < next_start_and_dist.second) {
|
||||||
|
// Heuristics to decide, whether to close the loop, or connect another polyline.
|
||||||
|
// One should avoid closing loops shorter than max_gap_scaled.
|
||||||
|
loop_closed = sqrt(current_loop_closing_distance2) < 0.3 * length(opl->points);
|
||||||
|
}
|
||||||
|
if (loop_closed) {
|
||||||
|
// Remove the start point of the current polyline from the lookup.
|
||||||
|
// Mark the current segment as not consumed, otherwise the closest_end_point_lookup.erase() would fail.
|
||||||
|
opl->consumed = false;
|
||||||
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl, true));
|
||||||
|
if (current_loop_closing_distance2 == 0.) {
|
||||||
|
// Remove the duplicate last point.
|
||||||
|
opl->points.pop_back();
|
||||||
|
} else {
|
||||||
|
// The end points are different, keep both of them.
|
||||||
|
}
|
||||||
|
if (opl->points.size() >= 3) {
|
||||||
|
if (try_connect_reversed && n_segments_joined > 1 && area(opl->points) < 0)
|
||||||
|
// The closed polygon is patched from pieces with messed up orientation, therefore
|
||||||
|
// the orientation of the patched up polygon is not known.
|
||||||
|
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
|
||||||
|
std::reverse(opl->points.begin(), opl->points.end());
|
||||||
|
loops.emplace_back(std::move(opl->points));
|
||||||
|
}
|
||||||
|
opl->points.clear();
|
||||||
|
opl->consumed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next_start == nullptr) {
|
||||||
|
// The current loop could not be closed. Unmark the segment.
|
||||||
|
opl->consumed = false;
|
||||||
|
if (try_connect_reversed)
|
||||||
|
// Re-insert the end point.
|
||||||
|
closest_end_point_lookup.insert(OpenPolylineEnd(opl, false));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Attach this polyline to the end of the initial polyline.
|
||||||
|
if (next_start->start) {
|
||||||
|
auto it = next_start->polyline->points.begin();
|
||||||
|
if (*it == opl->points.back())
|
||||||
|
++ it;
|
||||||
|
std::copy(it, next_start->polyline->points.end(), back_inserter(opl->points));
|
||||||
|
} else {
|
||||||
|
auto it = next_start->polyline->points.rbegin();
|
||||||
|
if (*it == opl->points.back())
|
||||||
|
++ it;
|
||||||
|
std::copy(it, next_start->polyline->points.rend(), back_inserter(opl->points));
|
||||||
|
}
|
||||||
|
++ n_segments_joined;
|
||||||
|
// Remove the end points of the consumed polyline segment from the lookup.
|
||||||
|
OpenPolyline *opl2 = next_start->polyline;
|
||||||
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, true));
|
||||||
|
if (try_connect_reversed)
|
||||||
|
closest_end_point_lookup.erase(OpenPolylineEnd(opl2, false));
|
||||||
|
opl2->points.clear();
|
||||||
|
opl2->consumed = true;
|
||||||
|
// Continue with the current loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
|
//#ifdef _DEBUG
|
||||||
|
for (const Line &l : lines)
|
||||||
|
assert(l.a != l.b);
|
||||||
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
|
// There should be no tangent edges, as the horizontal triangles are ignored and if two triangles touch at a cutting plane,
|
||||||
|
// only the bottom triangle is considered to be cutting the plane.
|
||||||
|
// remove_tangent_edges(lines);
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
BoundingBox bbox_svg;
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
for (const Line &line : lines) {
|
||||||
|
bbox_svg.merge(line.a);
|
||||||
|
bbox_svg.merge(line.b);
|
||||||
|
}
|
||||||
|
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-raw_lines-%d.svg", iRun ++).c_str(), bbox_svg);
|
||||||
|
for (const Line &line : lines)
|
||||||
|
svg.draw(line);
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
|
std::vector<OpenPolyline> open_polylines;
|
||||||
|
chain_lines_by_triangle_connectivity(lines, *loops, open_polylines);
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
|
||||||
|
svg.draw(union_ex(*loops));
|
||||||
|
for (const OpenPolyline &pl : open_polylines)
|
||||||
|
svg.draw(Polyline(pl.points), "red");
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
|
// Now process the open polylines.
|
||||||
|
// Do it in two rounds, first try to connect in the same direction only,
|
||||||
|
// then try to connect the open polylines in reversed order as well.
|
||||||
|
chain_open_polylines_exact(open_polylines, *loops, false);
|
||||||
|
chain_open_polylines_exact(open_polylines, *loops, true);
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
|
||||||
|
svg.draw(union_ex(*loops));
|
||||||
|
for (const OpenPolyline &pl : open_polylines) {
|
||||||
|
if (pl.points.empty())
|
||||||
|
continue;
|
||||||
|
svg.draw(Polyline(pl.points), "red");
|
||||||
|
svg.draw(pl.points.front(), "blue");
|
||||||
|
svg.draw(pl.points.back(), "blue");
|
||||||
|
}
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
|
// Try to close gaps.
|
||||||
|
// Do it in two rounds, first try to connect in the same direction only,
|
||||||
|
// then try to connect the open polylines in reversed order as well.
|
||||||
|
const double max_gap = 2.; //mm
|
||||||
|
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, false);
|
||||||
|
chain_open_polylines_close_gaps(open_polylines, *loops, max_gap, true);
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
|
||||||
|
svg.draw(union_ex(*loops));
|
||||||
|
for (const OpenPolyline &pl : open_polylines) {
|
||||||
|
if (pl.points.empty())
|
||||||
|
continue;
|
||||||
|
svg.draw(Polyline(pl.points), "red");
|
||||||
|
svg.draw(pl.points.front(), "blue");
|
||||||
|
svg.draw(pl.points.back(), "blue");
|
||||||
|
}
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used to cut the mesh into two halves.
|
// Only used to cut the mesh into two halves.
|
||||||
@ -1580,10 +1771,11 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
|||||||
// p_slices = diff(p_slices, *loop);
|
// p_slices = diff(p_slices, *loop);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
// Perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||||
double safety_offset = scale_(0.0499);
|
// 0.0499 comes from https://github.com/slic3r/Slic3r/issues/959
|
||||||
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
|
// double safety_offset = scale_(0.0499);
|
||||||
// double safety_offset = scale_(0.0001);
|
// 0.0001 is set to satisfy GH #520, #1029, #1364
|
||||||
|
double safety_offset = scale_(0.0001);
|
||||||
|
|
||||||
/* The following line is commented out because it can generate wrong polygons,
|
/* The following line is commented out because it can generate wrong polygons,
|
||||||
see for example issue #661 */
|
see for example issue #661 */
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
|
#include "libslic3r/SLAPrint.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
#include "libslic3r/Format/3mf.hpp"
|
#include "libslic3r/Format/3mf.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
@ -105,7 +106,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
// load config files supplied via --load
|
// load config files supplied via --load
|
||||||
for (const std::string &file : cli_config.load.values) {
|
for (const std::string &file : cli_config.load.values) {
|
||||||
if (!boost::filesystem::exists(file)) {
|
if (! boost::filesystem::exists(file)) {
|
||||||
boost::nowide::cout << "No such file: " << file << std::endl;
|
boost::nowide::cout << "No such file: " << file << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -206,22 +207,35 @@ int main(int argc, char **argv)
|
|||||||
// lower.mesh().write_binary((outfile + "_lower.stl").c_str());
|
// lower.mesh().write_binary((outfile + "_lower.stl").c_str());
|
||||||
}
|
}
|
||||||
} else if (cli_config.slice) {
|
} else if (cli_config.slice) {
|
||||||
|
PrinterTechnology printer_technology = print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value;
|
||||||
std::string outfile = cli_config.output.value;
|
std::string outfile = cli_config.output.value;
|
||||||
Print print;
|
Print fff_print;
|
||||||
|
SLAPrint sla_print;
|
||||||
|
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
|
||||||
if (! cli_config.dont_arrange) {
|
if (! cli_config.dont_arrange) {
|
||||||
model.arrange_objects(print.config().min_object_distance());
|
//FIXME make the min_object_distance configurable.
|
||||||
|
model.arrange_objects(fff_print.config().min_object_distance());
|
||||||
model.center_instances_around_point(cli_config.print_center);
|
model.center_instances_around_point(cli_config.print_center);
|
||||||
}
|
}
|
||||||
if (outfile.empty())
|
if (outfile.empty()) {
|
||||||
outfile = model.propose_export_file_name() + ".gcode";
|
outfile = model.propose_export_file_name();
|
||||||
|
outfile += (printer_technology == ptFFF) ? ".gcode" : ".zip";
|
||||||
|
}
|
||||||
|
if (printer_technology == ptFFF) {
|
||||||
for (auto* mo : model.objects)
|
for (auto* mo : model.objects)
|
||||||
print.auto_assign_extruders(mo);
|
fff_print.auto_assign_extruders(mo);
|
||||||
|
}
|
||||||
print_config.normalize();
|
print_config.normalize();
|
||||||
print.apply(model, print_config);
|
print->apply(model, print_config);
|
||||||
std::string err = print.validate();
|
std::string err = print->validate();
|
||||||
if (err.empty())
|
if (err.empty()) {
|
||||||
print.export_gcode(outfile, nullptr);
|
if (printer_technology == ptFFF) {
|
||||||
else
|
fff_print.export_gcode(outfile, nullptr);
|
||||||
|
} else {
|
||||||
|
assert(printer_technology == ptSLA);
|
||||||
|
//FIXME add the output here
|
||||||
|
}
|
||||||
|
} else
|
||||||
std::cerr << err << "\n";
|
std::cerr << err << "\n";
|
||||||
} else {
|
} else {
|
||||||
boost::nowide::cerr << "error: command not supported" << std::endl;
|
boost::nowide::cerr << "error: command not supported" << std::endl;
|
||||||
|
@ -103,12 +103,12 @@ add_library(libslic3r_gui STATIC
|
|||||||
GUI/ProgressIndicator.hpp
|
GUI/ProgressIndicator.hpp
|
||||||
GUI/ProgressStatusBar.hpp
|
GUI/ProgressStatusBar.hpp
|
||||||
GUI/ProgressStatusBar.cpp
|
GUI/ProgressStatusBar.cpp
|
||||||
|
GUI/PrintHostDialogs.cpp
|
||||||
|
GUI/PrintHostDialogs.hpp
|
||||||
Utils/Http.cpp
|
Utils/Http.cpp
|
||||||
Utils/Http.hpp
|
Utils/Http.hpp
|
||||||
Utils/FixModelByWin10.cpp
|
Utils/FixModelByWin10.cpp
|
||||||
Utils/FixModelByWin10.hpp
|
Utils/FixModelByWin10.hpp
|
||||||
Utils/PrintHostSendDialog.cpp
|
|
||||||
Utils/PrintHostSendDialog.hpp
|
|
||||||
Utils/OctoPrint.cpp
|
Utils/OctoPrint.cpp
|
||||||
Utils/OctoPrint.hpp
|
Utils/OctoPrint.hpp
|
||||||
Utils/Duet.cpp
|
Utils/Duet.cpp
|
||||||
|
@ -870,7 +870,7 @@ void GLVolumeCollection::load_object_auxiliary(
|
|||||||
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||||
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||||
v.is_modifier = false;
|
v.is_modifier = false;
|
||||||
v.shader_outside_printer_detection_enabled = true;
|
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
|
||||||
v.set_instance_transformation(model_instance.get_transformation());
|
v.set_instance_transformation(model_instance.get_transformation());
|
||||||
// Leave the volume transformation at identity.
|
// Leave the volume transformation at identity.
|
||||||
// v.set_volume_transformation(model_volume->get_transformation());
|
// v.set_volume_transformation(model_volume->get_transformation());
|
||||||
@ -1039,8 +1039,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
|||||||
|
|
||||||
for (GLVolume* volume : this->volumes)
|
for (GLVolume* volume : this->volumes)
|
||||||
{
|
{
|
||||||
if ((volume != nullptr) && !volume->is_modifier && (!volume->is_wipe_tower || (volume->is_wipe_tower && volume->shader_outside_printer_detection_enabled)))
|
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
|
||||||
{
|
continue;
|
||||||
|
|
||||||
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
|
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
|
||||||
bool contained = print_volume.contains(bb);
|
bool contained = print_volume.contains(bb);
|
||||||
all_contained &= contained;
|
all_contained &= contained;
|
||||||
@ -1053,7 +1054,6 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
|||||||
if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
|
if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
|
||||||
state = ModelInstance::PVS_Partly_Outside;
|
state = ModelInstance::PVS_Partly_Outside;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (out_state != nullptr)
|
if (out_state != nullptr)
|
||||||
*out_state = state;
|
*out_state = state;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -125,8 +126,13 @@ void AppConfig::load()
|
|||||||
|
|
||||||
void AppConfig::save()
|
void AppConfig::save()
|
||||||
{
|
{
|
||||||
|
// The config is first written to a file with a PID suffix and then moved
|
||||||
|
// to avoid race conditions with multiple instances of Slic3r
|
||||||
|
const auto path = config_path();
|
||||||
|
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
|
||||||
|
|
||||||
boost::nowide::ofstream c;
|
boost::nowide::ofstream c;
|
||||||
c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc);
|
c.open(path_pid, std::ios::out | std::ios::trunc);
|
||||||
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
||||||
// Make sure the "no" category is written first.
|
// Make sure the "no" category is written first.
|
||||||
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
|
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
|
||||||
@ -155,6 +161,8 @@ void AppConfig::save()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.close();
|
c.close();
|
||||||
|
|
||||||
|
rename_file(path_pid, path);
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,13 @@
|
|||||||
//#undef NDEBUG
|
//#undef NDEBUG
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -61,6 +65,11 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
|||||||
return m_print->technology();
|
return m_print->technology();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isspace(int ch)
|
||||||
|
{
|
||||||
|
return std::isspace(ch) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
// This function may one day be merged into the Print, but historically the print was separated
|
// This function may one day be merged into the Print, but historically the print was separated
|
||||||
// from the G-code generator.
|
// from the G-code generator.
|
||||||
void BackgroundSlicingProcess::process_fff()
|
void BackgroundSlicingProcess::process_fff()
|
||||||
@ -72,11 +81,13 @@ void BackgroundSlicingProcess::process_fff()
|
|||||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||||
if (! m_export_path.empty()) {
|
if (! m_export_path.empty()) {
|
||||||
//FIXME localize the messages
|
//FIXME localize the messages
|
||||||
if (copy_file(m_temp_output_path, m_export_path) != 0)
|
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
||||||
|
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||||
|
if (copy_file(m_temp_output_path, export_path) != 0)
|
||||||
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
run_post_process_scripts(m_export_path, m_fff_print->config());
|
run_post_process_scripts(export_path, m_fff_print->config());
|
||||||
m_print->set_status(100, "G-code file exported to " + m_export_path);
|
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||||
} else {
|
} else {
|
||||||
m_print->set_status(100, "Slicing complete");
|
m_print->set_status(100, "Slicing complete");
|
||||||
}
|
}
|
||||||
@ -102,10 +113,17 @@ public:
|
|||||||
zipstream(zipfile),
|
zipstream(zipfile),
|
||||||
pngstream(zipstream)
|
pngstream(zipstream)
|
||||||
{
|
{
|
||||||
if(!zipfile.IsOk())
|
if(!is_ok())
|
||||||
throw std::runtime_error("Cannot create zip file.");
|
throw std::runtime_error("Cannot create zip file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~LayerWriter() {
|
||||||
|
// In case of an error (disk space full) zipstream destructor would
|
||||||
|
// crash.
|
||||||
|
pngstream.clear();
|
||||||
|
zipstream.CloseEntry();
|
||||||
|
}
|
||||||
|
|
||||||
inline void next_entry(const std::string& fname) {
|
inline void next_entry(const std::string& fname) {
|
||||||
zipstream.PutNextEntry(fname);
|
zipstream.PutNextEntry(fname);
|
||||||
}
|
}
|
||||||
@ -118,6 +136,10 @@ public:
|
|||||||
pngstream << arg; return *this;
|
pngstream << arg; return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_ok() const {
|
||||||
|
return pngstream.good() && zipstream.IsOk() && zipfile.IsOk();
|
||||||
|
}
|
||||||
|
|
||||||
inline void close() {
|
inline void close() {
|
||||||
zipstream.Close();
|
zipstream.Close();
|
||||||
zipfile.Close();
|
zipfile.Close();
|
||||||
@ -345,6 +367,22 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
|||||||
m_export_path = path;
|
m_export_path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||||
|
{
|
||||||
|
assert(m_export_path.empty());
|
||||||
|
if (! m_export_path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const boost::filesystem::path path = boost::filesystem::temp_directory_path()
|
||||||
|
/ boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||||
|
|
||||||
|
// Guard against entering the export step before changing the export path.
|
||||||
|
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||||
|
this->invalidate_step(bspsGCodeFinalize);
|
||||||
|
m_export_path = path.string();
|
||||||
|
m_upload_job = std::move(upload_job);
|
||||||
|
}
|
||||||
|
|
||||||
void BackgroundSlicingProcess::reset_export()
|
void BackgroundSlicingProcess::reset_export()
|
||||||
{
|
{
|
||||||
assert(! this->running());
|
assert(! this->running());
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
|
#include "slic3r/Utils/PrintHost.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -86,6 +87,9 @@ public:
|
|||||||
// Set the export path of the G-code.
|
// Set the export path of the G-code.
|
||||||
// Once the path is set, the G-code
|
// Once the path is set, the G-code
|
||||||
void schedule_export(const std::string &path);
|
void schedule_export(const std::string &path);
|
||||||
|
// Set print host upload job data to be enqueued to the PrintHostJobQueue
|
||||||
|
// after current print slicing is complete
|
||||||
|
void schedule_upload(Slic3r::PrintHostJob upload_job);
|
||||||
// Clear m_export_path.
|
// Clear m_export_path.
|
||||||
void reset_export();
|
void reset_export();
|
||||||
// Once the G-code export is scheduled, the apply() methods will do nothing.
|
// Once the G-code export is scheduled, the apply() methods will do nothing.
|
||||||
@ -110,6 +114,11 @@ public:
|
|||||||
State state() const { return m_state; }
|
State state() const { return m_state; }
|
||||||
bool idle() const { return m_state == STATE_IDLE; }
|
bool idle() const { return m_state == STATE_IDLE; }
|
||||||
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
|
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
|
||||||
|
// Returns true if the last step of the active print was finished with success.
|
||||||
|
// The "finished" flag is reset by the apply() method, if it changes the state of the print.
|
||||||
|
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
|
||||||
|
// and it does not account for the OctoPrint scheduling.
|
||||||
|
bool finished() const { return m_print->finished(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void thread_proc();
|
void thread_proc();
|
||||||
@ -138,6 +147,9 @@ private:
|
|||||||
// Output path provided by the user. The output path may be set even if the slicing is running,
|
// Output path provided by the user. The output path may be set even if the slicing is running,
|
||||||
// but once set, it cannot be re-set.
|
// but once set, it cannot be re-set.
|
||||||
std::string m_export_path;
|
std::string m_export_path;
|
||||||
|
// Print host upload job to schedule after slicing is complete, used by schedule_upload(),
|
||||||
|
// empty by default (ie. no upload to schedule)
|
||||||
|
PrintHostJob m_upload_job;
|
||||||
// Thread, on which the background processing is executed. The thread will always be present
|
// Thread, on which the background processing is executed. The thread will always be present
|
||||||
// and ready to execute the slicing process.
|
// and ready to execute the slicing process.
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
|
@ -74,7 +74,16 @@ void Field::on_kill_focus(wxEvent& event)
|
|||||||
event.Skip();
|
event.Skip();
|
||||||
// call the registered function if it is available
|
// call the registered function if it is available
|
||||||
if (m_on_kill_focus!=nullptr)
|
if (m_on_kill_focus!=nullptr)
|
||||||
m_on_kill_focus();
|
m_on_kill_focus(m_opt_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Field::on_set_focus(wxEvent& event)
|
||||||
|
{
|
||||||
|
// to allow the default behavior
|
||||||
|
event.Skip();
|
||||||
|
// call the registered function if it is available
|
||||||
|
if (m_on_set_focus!=nullptr)
|
||||||
|
m_on_set_focus(m_opt_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Field::on_change_field()
|
void Field::on_change_field()
|
||||||
@ -125,9 +134,9 @@ void Field::get_value_by_opt_type(wxString& str)
|
|||||||
case coPercents:
|
case coPercents:
|
||||||
case coFloats:
|
case coFloats:
|
||||||
case coFloat:{
|
case coFloat:{
|
||||||
if (m_opt.type == coPercent && str.Last() == '%')
|
if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%')
|
||||||
str.RemoveLast();
|
str.RemoveLast();
|
||||||
else if (str.Last() == '%') {
|
else if (!str.IsEmpty() && str.Last() == '%') {
|
||||||
wxString label = m_Label->GetLabel();
|
wxString label = m_Label->GetLabel();
|
||||||
if (label.Last() == '\n') label.RemoveLast();
|
if (label.Last() == '\n') label.RemoveLast();
|
||||||
while (label.Last() == ' ') label.RemoveLast();
|
while (label.Last() == ' ') label.RemoveLast();
|
||||||
@ -162,7 +171,7 @@ void Field::get_value_by_opt_type(wxString& str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextCtrl::is_defined_input_value()
|
bool TextCtrl::is_defined_input_value() const
|
||||||
{
|
{
|
||||||
if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings)
|
if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings)
|
||||||
return false;
|
return false;
|
||||||
@ -216,11 +225,13 @@ void TextCtrl::BUILD() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const long style = m_opt.multiline ? wxTE_MULTILINE : 0 | m_process_enter ? wxTE_PROCESS_ENTER : 0;
|
const long style = m_opt.multiline ? wxTE_MULTILINE : 0;
|
||||||
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
|
auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style);
|
||||||
|
|
||||||
temp->SetToolTip(get_tooltip_text(text_value));
|
temp->SetToolTip(get_tooltip_text(text_value));
|
||||||
|
|
||||||
|
temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId());
|
||||||
|
|
||||||
temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
|
temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event)
|
||||||
{
|
{
|
||||||
//! to allow the default handling
|
//! to allow the default handling
|
||||||
@ -240,17 +251,13 @@ void TextCtrl::BUILD() {
|
|||||||
e.Skip();
|
e.Skip();
|
||||||
temp->GetToolTip()->Enable(true);
|
temp->GetToolTip()->Enable(true);
|
||||||
#endif // __WXGTK__
|
#endif // __WXGTK__
|
||||||
if (!is_defined_input_value())
|
// if (!is_defined_input_value())
|
||||||
|
if (is_defined_input_value())
|
||||||
|
on_change_field();
|
||||||
|
else
|
||||||
on_kill_focus(e);
|
on_kill_focus(e);
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
|
/*
|
||||||
if (m_process_enter) {
|
|
||||||
temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent& evt) {
|
|
||||||
if(is_defined_input_value())
|
|
||||||
on_change_field();
|
|
||||||
}), temp->GetId());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
|
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
|
||||||
{
|
{
|
||||||
#ifdef __WXGTK__
|
#ifdef __WXGTK__
|
||||||
@ -267,8 +274,7 @@ void TextCtrl::BUILD() {
|
|||||||
temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
|
temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
|
||||||
temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
|
temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
|
||||||
#endif //__WXGTK__
|
#endif //__WXGTK__
|
||||||
}
|
*/
|
||||||
|
|
||||||
// select all text using Ctrl+A
|
// select all text using Ctrl+A
|
||||||
temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
|
temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event)
|
||||||
{
|
{
|
||||||
@ -371,7 +377,15 @@ void SpinCtrl::BUILD() {
|
|||||||
0, min_val, max_val, default_value);
|
0, min_val, max_val, default_value);
|
||||||
|
|
||||||
// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
|
// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
|
||||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { on_kill_focus(e); }), temp->GetId());
|
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
||||||
|
{
|
||||||
|
if (tmp_value < 0)
|
||||||
|
on_kill_focus(e);
|
||||||
|
else {
|
||||||
|
e.Skip();
|
||||||
|
on_change_field();
|
||||||
|
}
|
||||||
|
}), temp->GetId());
|
||||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||||
{
|
{
|
||||||
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||||
@ -382,7 +396,8 @@ void SpinCtrl::BUILD() {
|
|||||||
std::string value = e.GetString().utf8_str().data();
|
std::string value = e.GetString().utf8_str().data();
|
||||||
if (is_matched(value, "^\\d+$"))
|
if (is_matched(value, "^\\d+$"))
|
||||||
tmp_value = std::stoi(value);
|
tmp_value = std::stoi(value);
|
||||||
on_change_field();
|
else tmp_value = -9999;
|
||||||
|
// on_change_field();
|
||||||
// # We don't reset tmp_value here because _on_change might put callbacks
|
// # We don't reset tmp_value here because _on_change might put callbacks
|
||||||
// # in the CallAfter queue, and we want the tmp value to be available from
|
// # in the CallAfter queue, and we want the tmp value to be available from
|
||||||
// # them as well.
|
// # them as well.
|
||||||
|
@ -29,8 +29,8 @@ namespace Slic3r { namespace GUI {
|
|||||||
|
|
||||||
class Field;
|
class Field;
|
||||||
using t_field = std::unique_ptr<Field>;
|
using t_field = std::unique_ptr<Field>;
|
||||||
using t_kill_focus = std::function<void()>;
|
using t_kill_focus = std::function<void(const std::string&)>;
|
||||||
using t_change = std::function<void(t_config_option_key, const boost::any&)>;
|
using t_change = std::function<void(const t_config_option_key&, const boost::any&)>;
|
||||||
using t_back_to_init = std::function<void(const std::string&)>;
|
using t_back_to_init = std::function<void(const std::string&)>;
|
||||||
|
|
||||||
wxString double_to_string(double const value, const int max_precision = 4);
|
wxString double_to_string(double const value, const int max_precision = 4);
|
||||||
@ -76,6 +76,8 @@ protected:
|
|||||||
//! in another case we can't unfocused control at all
|
//! in another case we can't unfocused control at all
|
||||||
void on_kill_focus(wxEvent& event);
|
void on_kill_focus(wxEvent& event);
|
||||||
/// Call the attached on_change method.
|
/// Call the attached on_change method.
|
||||||
|
void on_set_focus(wxEvent& event);
|
||||||
|
/// Call the attached on_change method.
|
||||||
void on_change_field();
|
void on_change_field();
|
||||||
/// Call the attached m_back_to_initial_value method.
|
/// Call the attached m_back_to_initial_value method.
|
||||||
void on_back_to_initial_value();
|
void on_back_to_initial_value();
|
||||||
@ -89,6 +91,9 @@ public:
|
|||||||
/// Function object to store callback passed in from owning object.
|
/// Function object to store callback passed in from owning object.
|
||||||
t_kill_focus m_on_kill_focus {nullptr};
|
t_kill_focus m_on_kill_focus {nullptr};
|
||||||
|
|
||||||
|
/// Function object to store callback passed in from owning object.
|
||||||
|
t_kill_focus m_on_set_focus {nullptr};
|
||||||
|
|
||||||
/// Function object to store callback passed in from owning object.
|
/// Function object to store callback passed in from owning object.
|
||||||
t_change m_on_change {nullptr};
|
t_change m_on_change {nullptr};
|
||||||
|
|
||||||
@ -139,10 +144,9 @@ public:
|
|||||||
|
|
||||||
/// Factory method for generating new derived classes.
|
/// Factory method for generating new derived classes.
|
||||||
template<class T>
|
template<class T>
|
||||||
static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id, const bool process_enter = false)// interface for creating shared objects
|
static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id)// interface for creating shared objects
|
||||||
{
|
{
|
||||||
auto p = Slic3r::make_unique<T>(parent, opt, id);
|
auto p = Slic3r::make_unique<T>(parent, opt, id);
|
||||||
p->m_process_enter = process_enter;
|
|
||||||
p->PostInitialize();
|
p->PostInitialize();
|
||||||
return std::move(p); //!p;
|
return std::move(p); //!p;
|
||||||
}
|
}
|
||||||
@ -223,9 +227,6 @@ protected:
|
|||||||
// current value
|
// current value
|
||||||
boost::any m_value;
|
boost::any m_value;
|
||||||
|
|
||||||
//this variable shows a mode of a call of the on_change function
|
|
||||||
bool m_process_enter { false };
|
|
||||||
|
|
||||||
friend class OptionsGroup;
|
friend class OptionsGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -265,7 +266,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::any& get_value() override;
|
boost::any& get_value() override;
|
||||||
bool is_defined_input_value();
|
bool is_defined_input_value() const ;
|
||||||
|
|
||||||
virtual void enable();
|
virtual void enable();
|
||||||
virtual void disable();
|
virtual void disable();
|
||||||
|
@ -68,8 +68,10 @@ static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
|||||||
0.0f, 0.0f, 1.0f, 0.0f,
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||||||
0.0f, 0.0f, 0.0f, 1.0f };
|
0.0f, 0.0f, 0.0f, 1.0f };
|
||||||
|
|
||||||
static const float DEFAULT_BG_COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
|
static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f };
|
||||||
static const float ERROR_BG_COLOR[3] = { 144.0f / 255.0f, 49.0f / 255.0f, 10.0f / 255.0f };
|
static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f };
|
||||||
|
static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
|
||||||
|
static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f };
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
@ -579,7 +581,7 @@ void GLCanvas3D::Bed::_render_custom() const
|
|||||||
|
|
||||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
::glColor4f(0.8f, 0.6f, 0.5f, 0.4f);
|
::glColor4f(0.35f, 0.35f, 0.35f, 0.4f);
|
||||||
::glNormal3d(0.0f, 0.0f, 1.0f);
|
::glNormal3d(0.0f, 0.0f, 1.0f);
|
||||||
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
|
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
|
||||||
::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount);
|
::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount);
|
||||||
@ -1404,6 +1406,9 @@ bool GLCanvas3D::Selection::is_single_full_instance() const
|
|||||||
if (m_type == SingleFullInstance)
|
if (m_type == SingleFullInstance)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (m_type == SingleFullObject)
|
||||||
|
return get_instance_idx() != -1;
|
||||||
|
|
||||||
if (m_list.empty() || m_volumes->empty())
|
if (m_list.empty() || m_volumes->empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1531,7 +1536,8 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||||||
#if ENABLE_WORLD_ROTATIONS
|
#if ENABLE_WORLD_ROTATIONS
|
||||||
{
|
{
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix();
|
||||||
|
Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||||
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -2110,6 +2116,7 @@ void GLCanvas3D::Selection::_update_type()
|
|||||||
v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false;
|
v->disabled = requires_disable ? (v->object_idx() != object_idx) || (v->instance_idx() != instance_idx) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SELECTION_DEBUG_OUTPUT
|
||||||
std::cout << "Selection: ";
|
std::cout << "Selection: ";
|
||||||
std::cout << "mode: ";
|
std::cout << "mode: ";
|
||||||
switch (m_mode)
|
switch (m_mode)
|
||||||
@ -2191,6 +2198,7 @@ void GLCanvas3D::Selection::_update_type()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_SELECTION_DEBUG_OUTPUT
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::Selection::_set_caches()
|
void GLCanvas3D::Selection::_set_caches()
|
||||||
@ -3173,7 +3181,8 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
|
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
|
||||||
const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 };
|
const unsigned char GLCanvas3D::LegendTexture::Default_Background_Color[3] = { (unsigned char)(DEFAULT_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(DEFAULT_BG_LIGHT_COLOR[2] * 255.0f) };
|
||||||
|
const unsigned char GLCanvas3D::LegendTexture::Error_Background_Color[3] = { (unsigned char)(ERROR_BG_LIGHT_COLOR[0] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[1] * 255.0f), (unsigned char)(ERROR_BG_LIGHT_COLOR[2] * 255.0f) };
|
||||||
const unsigned char GLCanvas3D::LegendTexture::Opacity = 255;
|
const unsigned char GLCanvas3D::LegendTexture::Opacity = 255;
|
||||||
|
|
||||||
GLCanvas3D::LegendTexture::LegendTexture()
|
GLCanvas3D::LegendTexture::LegendTexture()
|
||||||
@ -3183,7 +3192,7 @@ GLCanvas3D::LegendTexture::LegendTexture()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas)
|
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool use_error_colors)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -3222,8 +3231,11 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
wxMemoryDC memDC;
|
wxMemoryDC memDC;
|
||||||
|
wxMemoryDC mask_memDC;
|
||||||
|
|
||||||
// select default font
|
// select default font
|
||||||
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||||
|
mask_memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||||
|
|
||||||
// calculates texture size
|
// calculates texture size
|
||||||
wxCoord w, h;
|
wxCoord w, h;
|
||||||
@ -3252,16 +3264,28 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||||||
|
|
||||||
// generates bitmap
|
// generates bitmap
|
||||||
wxBitmap bitmap(m_width, m_height);
|
wxBitmap bitmap(m_width, m_height);
|
||||||
|
wxBitmap mask(m_width, m_height);
|
||||||
|
|
||||||
memDC.SelectObject(bitmap);
|
memDC.SelectObject(bitmap);
|
||||||
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
|
mask_memDC.SelectObject(mask);
|
||||||
|
|
||||||
|
memDC.SetBackground(wxBrush(use_error_colors ? *wxWHITE : *wxBLACK));
|
||||||
|
mask_memDC.SetBackground(wxBrush(*wxBLACK));
|
||||||
|
|
||||||
memDC.Clear();
|
memDC.Clear();
|
||||||
|
mask_memDC.Clear();
|
||||||
|
|
||||||
// draw title
|
// draw title
|
||||||
memDC.SetTextForeground(*wxWHITE);
|
memDC.SetTextForeground(use_error_colors ? *wxWHITE : *wxBLACK);
|
||||||
|
mask_memDC.SetTextForeground(*wxWHITE);
|
||||||
|
|
||||||
int title_x = Px_Border;
|
int title_x = Px_Border;
|
||||||
int title_y = Px_Border;
|
int title_y = Px_Border;
|
||||||
memDC.DrawText(title, title_x, title_y);
|
memDC.DrawText(title, title_x, title_y);
|
||||||
|
mask_memDC.DrawText(title, title_x, title_y);
|
||||||
|
|
||||||
|
mask_memDC.SetPen(wxPen(*wxWHITE));
|
||||||
|
mask_memDC.SetBrush(wxBrush(*wxWHITE));
|
||||||
|
|
||||||
// draw icons contours as background
|
// draw icons contours as background
|
||||||
int squares_contour_x = Px_Border;
|
int squares_contour_x = Px_Border;
|
||||||
@ -3277,6 +3301,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||||||
memDC.SetPen(pen);
|
memDC.SetPen(pen);
|
||||||
memDC.SetBrush(brush);
|
memDC.SetBrush(brush);
|
||||||
memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
|
memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
|
||||||
|
mask_memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
|
||||||
|
|
||||||
// draw items (colored icon + text)
|
// draw items (colored icon + text)
|
||||||
int icon_x = squares_contour_x + Px_Square_Contour;
|
int icon_x = squares_contour_x + Px_Square_Contour;
|
||||||
@ -3313,16 +3338,18 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||||||
|
|
||||||
// draw text
|
// draw text
|
||||||
memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
|
memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
|
||||||
|
mask_memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
|
||||||
|
|
||||||
// update y
|
// update y
|
||||||
icon_y += icon_y_step;
|
icon_y += icon_y_step;
|
||||||
}
|
}
|
||||||
|
|
||||||
memDC.SelectObject(wxNullBitmap);
|
memDC.SelectObject(wxNullBitmap);
|
||||||
|
mask_memDC.SelectObject(wxNullBitmap);
|
||||||
|
|
||||||
// Convert the bitmap into a linear data ready to be loaded into the GPU.
|
// Convert the bitmap into a linear data ready to be loaded into the GPU.
|
||||||
wxImage image = bitmap.ConvertToImage();
|
wxImage image = bitmap.ConvertToImage();
|
||||||
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
|
wxImage mask_image = mask.ConvertToImage();
|
||||||
|
|
||||||
// prepare buffer
|
// prepare buffer
|
||||||
std::vector<unsigned char> data(4 * m_width * m_height, 0);
|
std::vector<unsigned char> data(4 * m_width * m_height, 0);
|
||||||
@ -3335,7 +3362,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||||||
*px_ptr++ = image.GetRed(w, h);
|
*px_ptr++ = image.GetRed(w, h);
|
||||||
*px_ptr++ = image.GetGreen(w, h);
|
*px_ptr++ = image.GetGreen(w, h);
|
||||||
*px_ptr++ = image.GetBlue(w, h);
|
*px_ptr++ = image.GetBlue(w, h);
|
||||||
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
|
*px_ptr++ = (mask_image.GetRed(w, h) + mask_image.GetGreen(w, h) + mask_image.GetBlue(w, h)) / 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4089,6 +4116,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
m_reload_delayed = ! m_canvas->IsShown() && ! refresh_immediately && ! force_full_scene_refresh;
|
m_reload_delayed = ! m_canvas->IsShown() && ! refresh_immediately && ! force_full_scene_refresh;
|
||||||
|
|
||||||
PrinterTechnology printer_technology = m_process->current_printer_technology();
|
PrinterTechnology printer_technology = m_process->current_printer_technology();
|
||||||
|
int volume_idx_wipe_tower_old = -1;
|
||||||
|
|
||||||
if (m_regenerate_volumes)
|
if (m_regenerate_volumes)
|
||||||
{
|
{
|
||||||
@ -4146,6 +4174,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
}
|
}
|
||||||
if (mvs == nullptr || force_full_scene_refresh) {
|
if (mvs == nullptr || force_full_scene_refresh) {
|
||||||
// This GLVolume will be released.
|
// This GLVolume will be released.
|
||||||
|
if (volume->is_wipe_tower) {
|
||||||
|
// There is only one wipe tower.
|
||||||
|
assert(volume_idx_wipe_tower_old == -1);
|
||||||
|
volume_idx_wipe_tower_old = (int)volume_id;
|
||||||
|
}
|
||||||
volume->release_geometry();
|
volume->release_geometry();
|
||||||
if (! m_reload_delayed)
|
if (! m_reload_delayed)
|
||||||
delete volume;
|
delete volume;
|
||||||
@ -4313,8 +4346,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
float depth = print->get_wipe_tower_depth();
|
float depth = print->get_wipe_tower_depth();
|
||||||
if (!print->is_step_done(psWipeTower))
|
if (!print->is_step_done(psWipeTower))
|
||||||
depth = (900.f/w) * (float)(extruders_count - 1) ;
|
depth = (900.f/w) * (float)(extruders_count - 1) ;
|
||||||
m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower),
|
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||||
|
1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower),
|
||||||
print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
|
print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
|
||||||
|
if (volume_idx_wipe_tower_old != -1)
|
||||||
|
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4378,10 +4414,10 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
|
|||||||
return;
|
return;
|
||||||
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
||||||
|
|
||||||
if (m_volumes.empty())
|
|
||||||
{
|
|
||||||
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
|
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
|
||||||
|
|
||||||
|
if (m_volumes.empty())
|
||||||
|
{
|
||||||
m_gcode_preview_volume_index.reset();
|
m_gcode_preview_volume_index.reset();
|
||||||
|
|
||||||
_load_gcode_extrusion_paths(preview_data, tool_colors);
|
_load_gcode_extrusion_paths(preview_data, tool_colors);
|
||||||
@ -4389,12 +4425,8 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
|
|||||||
_load_gcode_retractions(preview_data);
|
_load_gcode_retractions(preview_data);
|
||||||
_load_gcode_unretractions(preview_data);
|
_load_gcode_unretractions(preview_data);
|
||||||
|
|
||||||
if (m_volumes.empty())
|
if (!m_volumes.empty())
|
||||||
reset_legend_texture();
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_generate_legend_texture(preview_data, tool_colors);
|
|
||||||
|
|
||||||
// removes empty volumes
|
// removes empty volumes
|
||||||
m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
|
m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
|
||||||
[](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
|
[](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
|
||||||
@ -4406,6 +4438,11 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
|
|||||||
|
|
||||||
_update_gcode_volumes_visibility(preview_data);
|
_update_gcode_volumes_visibility(preview_data);
|
||||||
_show_warning_texture_if_needed();
|
_show_warning_texture_if_needed();
|
||||||
|
|
||||||
|
if (m_volumes.empty())
|
||||||
|
reset_legend_texture();
|
||||||
|
else
|
||||||
|
_generate_legend_texture(preview_data, tool_colors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4994,6 +5031,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||||
#if ENABLE_CONSTRAINED_CAMERA_TARGET
|
#if ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
m_camera.set_scene_box(scene_bounding_box(), *this);
|
m_camera.set_scene_box(scene_bounding_box(), *this);
|
||||||
|
set_camera_zoom(0.0f);
|
||||||
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5801,18 +5839,22 @@ void GLCanvas3D::_render_background() const
|
|||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
::glLoadIdentity();
|
::glLoadIdentity();
|
||||||
|
|
||||||
// Draws a bluish bottom to top gradient over the complete screen.
|
// Draws a bottom to top gradient over the complete screen.
|
||||||
::glDisable(GL_DEPTH_TEST);
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
::glBegin(GL_QUADS);
|
::glBegin(GL_QUADS);
|
||||||
::glColor3f(0.0f, 0.0f, 0.0f);
|
if (m_dynamic_background_enabled && _is_any_volume_outside())
|
||||||
|
::glColor3fv(ERROR_BG_DARK_COLOR);
|
||||||
|
else
|
||||||
|
::glColor3fv(DEFAULT_BG_DARK_COLOR);
|
||||||
|
|
||||||
::glVertex2f(-1.0f, -1.0f);
|
::glVertex2f(-1.0f, -1.0f);
|
||||||
::glVertex2f(1.0f, -1.0f);
|
::glVertex2f(1.0f, -1.0f);
|
||||||
|
|
||||||
if (m_dynamic_background_enabled && _is_any_volume_outside())
|
if (m_dynamic_background_enabled && _is_any_volume_outside())
|
||||||
::glColor3fv(ERROR_BG_COLOR);
|
::glColor3fv(ERROR_BG_LIGHT_COLOR);
|
||||||
else
|
else
|
||||||
::glColor3fv(DEFAULT_BG_COLOR);
|
::glColor3fv(DEFAULT_BG_LIGHT_COLOR);
|
||||||
|
|
||||||
::glVertex2f(1.0f, 1.0f);
|
::glVertex2f(1.0f, 1.0f);
|
||||||
::glVertex2f(-1.0f, 1.0f);
|
::glVertex2f(-1.0f, 1.0f);
|
||||||
@ -6083,21 +6125,47 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const SLAPrint* print = this->sla_print();
|
const SLAPrint* print = this->sla_print();
|
||||||
if (print->objects().empty())
|
const PrintObjects& print_objects = print->objects();
|
||||||
|
if (print_objects.empty())
|
||||||
// nothing to render, return
|
// nothing to render, return
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double clip_min_z = -m_clipping_planes[0].get_data()[3];
|
double clip_min_z = -m_clipping_planes[0].get_data()[3];
|
||||||
double clip_max_z = m_clipping_planes[1].get_data()[3];
|
double clip_max_z = m_clipping_planes[1].get_data()[3];
|
||||||
for (const SLAPrintObject* obj : print->objects())
|
for (unsigned int i = 0; i < (unsigned int)print_objects.size(); ++i)
|
||||||
{
|
{
|
||||||
if (obj->is_step_done(slaposIndexSlices))
|
const SLAPrintObject* obj = print_objects[i];
|
||||||
{
|
|
||||||
const std::vector<ExPolygons>& model_slices = obj->get_model_slices();
|
|
||||||
const std::vector<ExPolygons>& support_slices = obj->get_support_slices();
|
|
||||||
const std::vector<SLAPrintObject::Instance>& instances = obj->instances();
|
|
||||||
double shift_z = obj->get_current_elevation();
|
|
||||||
|
|
||||||
|
Pointf3s bottom_obj_triangles;
|
||||||
|
Pointf3s bottom_sup_triangles;
|
||||||
|
Pointf3s top_obj_triangles;
|
||||||
|
Pointf3s top_sup_triangles;
|
||||||
|
|
||||||
|
double shift_z = obj->get_current_elevation();
|
||||||
|
double min_z = clip_min_z - shift_z;
|
||||||
|
double max_z = clip_max_z - shift_z;
|
||||||
|
|
||||||
|
if (m_sla_caps[0].matches(min_z))
|
||||||
|
{
|
||||||
|
SlaCap::ObjectIdToTrianglesMap::const_iterator it = m_sla_caps[0].triangles.find(i);
|
||||||
|
if (it != m_sla_caps[0].triangles.end())
|
||||||
|
{
|
||||||
|
bottom_obj_triangles = it->second.object;
|
||||||
|
bottom_sup_triangles = it->second.suppports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_sla_caps[1].matches(max_z))
|
||||||
|
{
|
||||||
|
SlaCap::ObjectIdToTrianglesMap::const_iterator it = m_sla_caps[1].triangles.find(i);
|
||||||
|
if (it != m_sla_caps[1].triangles.end())
|
||||||
|
{
|
||||||
|
top_obj_triangles = it->second.object;
|
||||||
|
top_sup_triangles = it->second.suppports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SLAPrintObject::Instance>& instances = obj->instances();
|
||||||
struct InstanceTransform
|
struct InstanceTransform
|
||||||
{
|
{
|
||||||
Vec3d offset;
|
Vec3d offset;
|
||||||
@ -6110,111 +6178,102 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
instance_transforms.push_back({ to_3d(unscale(inst.shift), shift_z), Geometry::rad2deg(inst.rotation) });
|
instance_transforms.push_back({ to_3d(unscale(inst.shift), shift_z), Geometry::rad2deg(inst.rotation) });
|
||||||
}
|
}
|
||||||
|
|
||||||
double min_z = clip_min_z - shift_z;
|
if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && obj->is_step_done(slaposIndexSlices))
|
||||||
double max_z = clip_max_z - shift_z;
|
|
||||||
|
|
||||||
Pointf3s bottom_triangles;
|
|
||||||
Pointf3s top_triangles;
|
|
||||||
|
|
||||||
if (m_sla_caps[0].matches(min_z))
|
|
||||||
bottom_triangles = m_sla_caps[0].triangles;
|
|
||||||
|
|
||||||
if (m_sla_caps[1].matches(max_z))
|
|
||||||
top_triangles = m_sla_caps[1].triangles;
|
|
||||||
|
|
||||||
if (bottom_triangles.empty() || top_triangles.empty())
|
|
||||||
{
|
{
|
||||||
|
const std::vector<ExPolygons>& model_slices = obj->get_model_slices();
|
||||||
|
const std::vector<ExPolygons>& support_slices = obj->get_support_slices();
|
||||||
|
|
||||||
const SLAPrintObject::SliceIndex& index = obj->get_slice_index();
|
const SLAPrintObject::SliceIndex& index = obj->get_slice_index();
|
||||||
SLAPrintObject::SliceIndex::const_iterator it_min_z = std::find_if(index.begin(), index.end(), [min_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(min_z - id.first) < EPSILON; });
|
SLAPrintObject::SliceIndex::const_iterator it_min_z = std::find_if(index.begin(), index.end(), [min_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(min_z - id.first) < EPSILON; });
|
||||||
SLAPrintObject::SliceIndex::const_iterator it_max_z = std::find_if(index.begin(), index.end(), [max_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(max_z - id.first) < EPSILON; });
|
SLAPrintObject::SliceIndex::const_iterator it_max_z = std::find_if(index.begin(), index.end(), [max_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(max_z - id.first) < EPSILON; });
|
||||||
|
|
||||||
if (bottom_triangles.empty() && (it_min_z != index.end()))
|
if (it_min_z != index.end())
|
||||||
|
{
|
||||||
|
if (bottom_obj_triangles.empty() && (it_min_z->second.model_slices_idx < model_slices.size()))
|
||||||
{
|
{
|
||||||
// calculate model bottom cap
|
// calculate model bottom cap
|
||||||
if (it_min_z->second.model_slices_idx < model_slices.size())
|
|
||||||
{
|
|
||||||
const ExPolygons& polys = model_slices[it_min_z->second.model_slices_idx];
|
const ExPolygons& polys = model_slices[it_min_z->second.model_slices_idx];
|
||||||
for (const ExPolygon& poly : polys)
|
for (const ExPolygon& poly : polys)
|
||||||
{
|
{
|
||||||
Polygons triangles;
|
Polygons poly_triangles;
|
||||||
poly.triangulate(&triangles);
|
poly.triangulate(&poly_triangles);
|
||||||
for (const Polygon& t : triangles)
|
for (const Polygon& t : poly_triangles)
|
||||||
{
|
{
|
||||||
for (int v = 2; v >= 0; --v)
|
for (int v = 2; v >= 0; --v)
|
||||||
{
|
{
|
||||||
bottom_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z));
|
bottom_obj_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate support bottom cap
|
if (bottom_sup_triangles.empty() && (it_min_z->second.support_slices_idx < support_slices.size()))
|
||||||
if (it_min_z->second.support_slices_idx < support_slices.size())
|
|
||||||
{
|
{
|
||||||
|
// calculate support bottom cap
|
||||||
const ExPolygons& polys = support_slices[it_min_z->second.support_slices_idx];
|
const ExPolygons& polys = support_slices[it_min_z->second.support_slices_idx];
|
||||||
for (const ExPolygon& poly : polys)
|
for (const ExPolygon& poly : polys)
|
||||||
{
|
{
|
||||||
Polygons triangles;
|
Polygons poly_triangles;
|
||||||
poly.triangulate(&triangles);
|
poly.triangulate(&poly_triangles);
|
||||||
for (const Polygon& t : triangles)
|
for (const Polygon& t : poly_triangles)
|
||||||
{
|
{
|
||||||
for (int v = 2; v >= 0; --v)
|
for (int v = 2; v >= 0; --v)
|
||||||
{
|
{
|
||||||
bottom_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z));
|
bottom_sup_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m_sla_caps[0].z = min_z;
|
|
||||||
m_sla_caps[0].triangles = bottom_triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top_triangles.empty() && (it_max_z != index.end()))
|
m_sla_caps[0].triangles.insert(SlaCap::ObjectIdToTrianglesMap::value_type(i, { bottom_obj_triangles, bottom_sup_triangles }));
|
||||||
|
m_sla_caps[0].z = min_z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it_max_z != index.end())
|
||||||
|
{
|
||||||
|
if (top_obj_triangles.empty() && (it_max_z->second.model_slices_idx < model_slices.size()))
|
||||||
{
|
{
|
||||||
// calculate model top cap
|
// calculate model top cap
|
||||||
if (it_max_z->second.model_slices_idx < model_slices.size())
|
|
||||||
{
|
|
||||||
const ExPolygons& polys = model_slices[it_max_z->second.model_slices_idx];
|
const ExPolygons& polys = model_slices[it_max_z->second.model_slices_idx];
|
||||||
for (const ExPolygon& poly : polys)
|
for (const ExPolygon& poly : polys)
|
||||||
{
|
{
|
||||||
Polygons triangles;
|
Polygons poly_triangles;
|
||||||
poly.triangulate(&triangles);
|
poly.triangulate(&poly_triangles);
|
||||||
for (const Polygon& t : triangles)
|
for (const Polygon& t : poly_triangles)
|
||||||
{
|
{
|
||||||
for (int v = 0; v < 3; ++v)
|
for (int v = 0; v < 3; ++v)
|
||||||
{
|
{
|
||||||
top_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z));
|
top_obj_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate support top cap
|
if (top_sup_triangles.empty() && (it_max_z->second.support_slices_idx < support_slices.size()))
|
||||||
if (it_max_z->second.support_slices_idx < support_slices.size())
|
|
||||||
{
|
{
|
||||||
|
// calculate support top cap
|
||||||
const ExPolygons& polys = support_slices[it_max_z->second.support_slices_idx];
|
const ExPolygons& polys = support_slices[it_max_z->second.support_slices_idx];
|
||||||
for (const ExPolygon& poly : polys)
|
for (const ExPolygon& poly : polys)
|
||||||
{
|
{
|
||||||
Polygons triangles;
|
Polygons poly_triangles;
|
||||||
poly.triangulate(&triangles);
|
poly.triangulate(&poly_triangles);
|
||||||
for (const Polygon& t : triangles)
|
for (const Polygon& t : poly_triangles)
|
||||||
{
|
{
|
||||||
for (int v = 0; v < 3; ++v)
|
for (int v = 0; v < 3; ++v)
|
||||||
{
|
{
|
||||||
top_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z));
|
top_sup_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_sla_caps[1].triangles.insert(SlaCap::ObjectIdToTrianglesMap::value_type(i, { top_obj_triangles, top_sup_triangles }));
|
||||||
m_sla_caps[1].z = max_z;
|
m_sla_caps[1].z = max_z;
|
||||||
m_sla_caps[1].triangles = top_triangles;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bottom_triangles.empty() || !top_triangles.empty())
|
if (!bottom_obj_triangles.empty() || !top_obj_triangles.empty() || !bottom_sup_triangles.empty() || !top_sup_triangles.empty())
|
||||||
{
|
{
|
||||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
|
||||||
|
|
||||||
for (const InstanceTransform& inst : instance_transforms)
|
for (const InstanceTransform& inst : instance_transforms)
|
||||||
{
|
{
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
@ -6223,20 +6282,28 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
|
|
||||||
::glBegin(GL_TRIANGLES);
|
::glBegin(GL_TRIANGLES);
|
||||||
|
|
||||||
if (!bottom_triangles.empty())
|
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||||
{
|
|
||||||
for (const Vec3d& v : bottom_triangles)
|
for (const Vec3d& v : bottom_obj_triangles)
|
||||||
{
|
{
|
||||||
::glVertex3dv((GLdouble*)v.data());
|
::glVertex3dv((GLdouble*)v.data());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!top_triangles.empty())
|
for (const Vec3d& v : top_obj_triangles)
|
||||||
{
|
|
||||||
for (const Vec3d& v : top_triangles)
|
|
||||||
{
|
{
|
||||||
::glVertex3dv((GLdouble*)v.data());
|
::glVertex3dv((GLdouble*)v.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::glColor3f(1.0f, 0.0f, 0.37f);
|
||||||
|
|
||||||
|
for (const Vec3d& v : bottom_sup_triangles)
|
||||||
|
{
|
||||||
|
::glVertex3dv((GLdouble*)v.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Vec3d& v : top_sup_triangles)
|
||||||
|
{
|
||||||
|
::glVertex3dv((GLdouble*)v.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
::glEnd();
|
::glEnd();
|
||||||
@ -6245,7 +6312,6 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_update_volumes_hover_state() const
|
void GLCanvas3D::_update_volumes_hover_state() const
|
||||||
@ -7401,6 +7467,9 @@ void GLCanvas3D::_load_shells_sla()
|
|||||||
int obj_idx = 0;
|
int obj_idx = 0;
|
||||||
for (const SLAPrintObject* obj : print->objects())
|
for (const SLAPrintObject* obj : print->objects())
|
||||||
{
|
{
|
||||||
|
if (!obj->is_step_done(slaposIndexSlices))
|
||||||
|
continue;
|
||||||
|
|
||||||
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
|
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
|
||||||
|
|
||||||
const ModelObject* model_obj = obj->model_object();
|
const ModelObject* model_obj = obj->model_object();
|
||||||
@ -7451,7 +7520,7 @@ void GLCanvas3D::_load_shells_sla()
|
|||||||
else
|
else
|
||||||
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
||||||
|
|
||||||
v.shader_outside_printer_detection_enabled = true;
|
v.shader_outside_printer_detection_enabled = false;
|
||||||
v.composite_id.volume_id = -1;
|
v.composite_id.volume_id = -1;
|
||||||
v.set_instance_offset(offset);
|
v.set_instance_offset(offset);
|
||||||
v.set_instance_rotation(rotation);
|
v.set_instance_rotation(rotation);
|
||||||
@ -7608,7 +7677,7 @@ void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data,
|
|||||||
return;
|
return;
|
||||||
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
||||||
|
|
||||||
m_legend_texture.generate(preview_data, tool_colors, *this);
|
m_legend_texture.generate(preview_data, tool_colors, *this, m_dynamic_background_enabled && _is_any_volume_outside());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
|
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
|
||||||
|
@ -701,8 +701,14 @@ private:
|
|||||||
|
|
||||||
struct SlaCap
|
struct SlaCap
|
||||||
{
|
{
|
||||||
|
struct Triangles
|
||||||
|
{
|
||||||
|
Pointf3s object;
|
||||||
|
Pointf3s suppports;
|
||||||
|
};
|
||||||
|
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
||||||
double z;
|
double z;
|
||||||
Pointf3s triangles;
|
ObjectIdToTrianglesMap triangles;
|
||||||
|
|
||||||
SlaCap() { reset(); }
|
SlaCap() { reset(); }
|
||||||
void reset() { z = DBL_MAX; triangles.clear(); }
|
void reset() { z = DBL_MAX; triangles.clear(); }
|
||||||
@ -733,7 +739,8 @@ private:
|
|||||||
static const int Px_Square_Contour = 1;
|
static const int Px_Square_Contour = 1;
|
||||||
static const int Px_Border = Px_Square / 2;
|
static const int Px_Border = Px_Square / 2;
|
||||||
static const unsigned char Squares_Border_Color[3];
|
static const unsigned char Squares_Border_Color[3];
|
||||||
static const unsigned char Background_Color[3];
|
static const unsigned char Default_Background_Color[3];
|
||||||
|
static const unsigned char Error_Background_Color[3];
|
||||||
static const unsigned char Opacity;
|
static const unsigned char Opacity;
|
||||||
|
|
||||||
int m_original_width;
|
int m_original_width;
|
||||||
@ -742,7 +749,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
LegendTexture();
|
LegendTexture();
|
||||||
|
|
||||||
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas);
|
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool use_error_colors);
|
||||||
|
|
||||||
void render(const GLCanvas3D& canvas) const;
|
void render(const GLCanvas3D& canvas) const;
|
||||||
};
|
};
|
||||||
@ -965,6 +972,8 @@ public:
|
|||||||
void viewport_changed();
|
void viewport_changed();
|
||||||
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
|
|
||||||
|
void handle_sidebar_focus_event(const std::string& opt_key) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
void _force_zoom_to_bed();
|
void _force_zoom_to_bed();
|
||||||
|
@ -1292,9 +1292,11 @@ void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const
|
|||||||
|
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
render_grabbers(box);
|
render_grabbers(box);
|
||||||
render_grabber_extension(X, box, false);
|
for (unsigned int i = 0; i < 3; ++i)
|
||||||
render_grabber_extension(Y, box, false);
|
{
|
||||||
render_grabber_extension(Z, box, false);
|
if (m_grabbers[i].enabled)
|
||||||
|
render_grabber_extension((Axis)i, box, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -147,7 +147,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
|||||||
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
||||||
break;
|
break;
|
||||||
case coStrings:{
|
case coStrings:{
|
||||||
if (opt_key == "compatible_prints" || opt_key == "compatible_printers") {
|
if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "post_process") {
|
||||||
config.option<ConfigOptionStrings>(opt_key)->values =
|
config.option<ConfigOptionStrings>(opt_key)->values =
|
||||||
boost::any_cast<std::vector<std::string>>(value);
|
boost::any_cast<std::vector<std::string>>(value);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
|
|
||||||
#include "../Utils/PresetUpdater.hpp"
|
#include "../Utils/PresetUpdater.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
#include "ConfigWizard_private.hpp"
|
#include "ConfigWizard_private.hpp"
|
||||||
#include "slic3r/Config/Snapshot.hpp"
|
#include "slic3r/Config/Snapshot.hpp"
|
||||||
#include "ConfigSnapshotDialog.hpp"
|
#include "ConfigSnapshotDialog.hpp"
|
||||||
@ -72,6 +73,7 @@ GUI_App::GUI_App()
|
|||||||
: wxApp()
|
: wxApp()
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
, m_imgui(new ImGuiWrapper())
|
, m_imgui(new ImGuiWrapper())
|
||||||
|
, m_printhost_queue(new PrintHostJobQueue())
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ class AppConfig;
|
|||||||
class PresetBundle;
|
class PresetBundle;
|
||||||
class PresetUpdater;
|
class PresetUpdater;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
|
class PrintHostJobQueue;
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
@ -91,6 +92,8 @@ class GUI_App : public wxApp
|
|||||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
|
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool OnInit() override;
|
bool OnInit() override;
|
||||||
|
|
||||||
@ -161,6 +164,8 @@ public:
|
|||||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
|
PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
DECLARE_APP(GUI_App)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
||||||
|
@ -60,32 +60,30 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||||||
#endif //__WXMSW__
|
#endif //__WXMSW__
|
||||||
});
|
});
|
||||||
|
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [this](wxDataViewEvent& event) {
|
// Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||||
context_menu();
|
|
||||||
});
|
|
||||||
|
|
||||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
// Extruder value changed
|
|
||||||
Bind(wxEVT_CHOICE, [this](wxCommandEvent& event) { update_extruder_in_config(event.GetString()); });
|
|
||||||
|
|
||||||
GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||||
set_tooltip_for_item(/*event.GetPosition()*/get_mouse_position_in_control());
|
set_tooltip_for_item(/*event.GetPosition()*/get_mouse_position_in_control());
|
||||||
event.Skip();
|
event.Skip();
|
||||||
});
|
});
|
||||||
#else
|
|
||||||
// equivalent to wxEVT_CHOICE on __WXMSW__
|
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [this](wxDataViewEvent& e) { item_value_change(e); });
|
|
||||||
#endif //__WXMSW__
|
#endif //__WXMSW__
|
||||||
|
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, [this](wxDataViewEvent& e) {on_begin_drag(e); });
|
Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &ObjectList::OnContextMenu, this);
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, [this](wxDataViewEvent& e) {on_drop_possible(e); });
|
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_DROP, [this](wxDataViewEvent& e) {on_drop(e); });
|
|
||||||
|
|
||||||
Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, [this](wxCommandEvent& e) {last_volume_is_deleted(e.GetInt()); });
|
Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, &ObjectList::OnBeginDrag, this);
|
||||||
|
Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this);
|
||||||
|
Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this);
|
||||||
|
|
||||||
Bind(wxEVT_DATAVIEW_ITEM_START_EDITING, &ObjectList::OnStartEditing, this);
|
Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this);
|
||||||
|
|
||||||
|
Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this);
|
||||||
|
|
||||||
|
Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, [this](wxCommandEvent& e) { last_volume_is_deleted(e.GetInt()); });
|
||||||
|
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
||||||
|
#endif //__WXOSX__
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectList::~ObjectList()
|
ObjectList::~ObjectList()
|
||||||
@ -199,12 +197,54 @@ wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_
|
|||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::update_extruder_values_for_items(const int max_extruder)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_objects->size(); ++i)
|
||||||
|
{
|
||||||
|
wxDataViewItem item = m_objects_model->GetItemById(i);
|
||||||
|
if (!item) continue;
|
||||||
|
|
||||||
|
auto object = (*m_objects)[i];
|
||||||
|
wxString extruder;
|
||||||
|
if (!object->config.has("extruder") ||
|
||||||
|
object->config.option<ConfigOptionInt>("extruder")->value > max_extruder)
|
||||||
|
extruder = "default";
|
||||||
|
else
|
||||||
|
extruder = wxString::Format("%d", object->config.option<ConfigOptionInt>("extruder")->value);
|
||||||
|
|
||||||
|
m_objects_model->SetValue(extruder, item, 1);
|
||||||
|
|
||||||
|
if (object->volumes.size() > 1) {
|
||||||
|
for (auto id = 0; id < object->volumes.size(); id++) {
|
||||||
|
item = m_objects_model->GetItemByVolumeId(i, id);
|
||||||
|
if (!item) continue;
|
||||||
|
if (!object->volumes[id]->config.has("extruder") ||
|
||||||
|
object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value > max_extruder)
|
||||||
|
extruder = "default";
|
||||||
|
else
|
||||||
|
extruder = wxString::Format("%d", object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value);
|
||||||
|
|
||||||
|
m_objects_model->SetValue(extruder, item, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::update_objects_list_extruder_column(int extruders_count)
|
void ObjectList::update_objects_list_extruder_column(int extruders_count)
|
||||||
{
|
{
|
||||||
if (!this) return; // #ys_FIXME
|
if (!this) return; // #ys_FIXME
|
||||||
if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA)
|
if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA)
|
||||||
extruders_count = 1;
|
extruders_count = 1;
|
||||||
|
|
||||||
|
wxDataViewChoiceRenderer* ch_render = dynamic_cast<wxDataViewChoiceRenderer*>(GetColumn(1)->GetRenderer());
|
||||||
|
if (ch_render->GetChoices().GetCount() - 1 == extruders_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_prevent_update_extruder_in_config = true;
|
||||||
|
|
||||||
|
if (m_objects && extruders_count > 1)
|
||||||
|
update_extruder_values_for_items(extruders_count);
|
||||||
|
|
||||||
// delete old 2nd column
|
// delete old 2nd column
|
||||||
DeleteColumn(GetColumn(1));
|
DeleteColumn(GetColumn(1));
|
||||||
// insert new created 3rd column
|
// insert new created 3rd column
|
||||||
@ -213,25 +253,60 @@ void ObjectList::update_objects_list_extruder_column(int extruders_count)
|
|||||||
set_extruder_column_hidden(extruders_count <= 1);
|
set_extruder_column_hidden(extruders_count <= 1);
|
||||||
//a workaround for a wrong last column width updating under OSX
|
//a workaround for a wrong last column width updating under OSX
|
||||||
GetColumn(2)->SetWidth(25);
|
GetColumn(2)->SetWidth(25);
|
||||||
|
|
||||||
|
m_prevent_update_extruder_in_config = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::set_extruder_column_hidden(bool hide)
|
void ObjectList::set_extruder_column_hidden(const bool hide) const
|
||||||
{
|
{
|
||||||
GetColumn(1)->SetHidden(hide);
|
GetColumn(1)->SetHidden(hide);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::update_extruder_in_config(const wxString& selection)
|
void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
|
||||||
{
|
{
|
||||||
|
if (m_prevent_update_extruder_in_config)
|
||||||
|
return;
|
||||||
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
||||||
|
const int obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
|
||||||
|
const int volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
if (obj_idx < 0 || volume_id < 0)
|
||||||
|
return;
|
||||||
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxVariant variant;
|
||||||
|
m_objects_model->GetValue(variant, item, 1);
|
||||||
|
const wxString selection = variant.GetString();
|
||||||
|
|
||||||
if (!m_config || selection.empty())
|
if (!m_config || selection.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int extruder = selection.size() > 1 ? 0 : atoi(selection.c_str());
|
const int extruder = selection.size() > 1 ? 0 : atoi(selection.c_str());
|
||||||
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
||||||
|
|
||||||
// update scene
|
// update scene
|
||||||
wxGetApp().plater()->update();
|
wxGetApp().plater()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::update_name_in_model(const wxDataViewItem& item)
|
||||||
|
{
|
||||||
|
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||||
|
if (obj_idx < 0) return;
|
||||||
|
|
||||||
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
||||||
|
(*m_objects)[obj_idx]->name = m_objects_model->GetName(item).ToStdString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
if (volume_id < 0) return;
|
||||||
|
(*m_objects)[obj_idx]->volumes[volume_id]->name = m_objects_model->GetName(item).ToStdString();
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::init_icons()
|
void ObjectList::init_icons()
|
||||||
{
|
{
|
||||||
m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
||||||
@ -275,13 +350,20 @@ void ObjectList::selection_changed()
|
|||||||
}
|
}
|
||||||
|
|
||||||
part_selection_changed();
|
part_selection_changed();
|
||||||
|
|
||||||
#ifdef __WXOSX__
|
|
||||||
update_extruder_in_config(m_selected_extruder);
|
|
||||||
#endif //__WXOSX__
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::context_menu()
|
void ObjectList::OnChar(wxKeyEvent& event)
|
||||||
|
{
|
||||||
|
if (event.GetKeyCode() == WXK_BACK){
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT))
|
||||||
|
select_item_all_children();
|
||||||
|
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::OnContextMenu(wxDataViewEvent&)
|
||||||
{
|
{
|
||||||
wxDataViewItem item;
|
wxDataViewItem item;
|
||||||
wxDataViewColumn* col;
|
wxDataViewColumn* col;
|
||||||
@ -303,7 +385,6 @@ void ObjectList::context_menu()
|
|||||||
|
|
||||||
if (title == " ")
|
if (title == " ")
|
||||||
show_context_menu();
|
show_context_menu();
|
||||||
|
|
||||||
else if (title == _("Name") && pt.x >15 &&
|
else if (title == _("Name") && pt.x >15 &&
|
||||||
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData())
|
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData())
|
||||||
{
|
{
|
||||||
@ -358,88 +439,74 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::item_value_change(wxDataViewEvent& event)
|
void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
if (event.GetColumn() == 1)
|
const wxDataViewItem item(event.GetItem());
|
||||||
{
|
|
||||||
wxVariant variant;
|
|
||||||
m_objects_model->GetValue(variant, event.GetItem(), 1);
|
|
||||||
#ifdef __WXOSX__
|
|
||||||
m_selected_extruder = variant.GetString();
|
|
||||||
#else // --> for Linux
|
|
||||||
update_extruder_in_config(variant.GetString());
|
|
||||||
#endif //__WXOSX__
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct draging_item_data
|
|
||||||
{
|
|
||||||
int obj_idx;
|
|
||||||
int vol_idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ObjectList::on_begin_drag(wxDataViewEvent &event)
|
|
||||||
{
|
|
||||||
wxDataViewItem item(event.GetItem());
|
|
||||||
|
|
||||||
// only allow drags for item, not containers
|
// only allow drags for item, not containers
|
||||||
if (multiple_selection() ||
|
if (multiple_selection() || GetSelection()!=item ||
|
||||||
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||||
m_objects_model->GetItemType(item) != itVolume ) {
|
m_objects_model->GetItemType(item) != itVolume ) {
|
||||||
event.Veto();
|
event.Veto();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_objects_model->GetVolumeIdByItem(item));
|
||||||
|
|
||||||
/* Under MSW or OSX, DnD moves an item to the place of another selected item
|
/* Under MSW or OSX, DnD moves an item to the place of another selected item
|
||||||
* But under GTK, DnD moves an item between another two items.
|
* But under GTK, DnD moves an item between another two items.
|
||||||
* And as a result - call EVT_CHANGE_SELECTION to unselect all items.
|
* And as a result - call EVT_CHANGE_SELECTION to unselect all items.
|
||||||
* To prevent such behavior use g_prevent_list_events
|
* To prevent such behavior use m_prevent_list_events
|
||||||
**/
|
**/
|
||||||
m_prevent_list_events = true;//it's needed for GTK
|
m_prevent_list_events = true;//it's needed for GTK
|
||||||
|
|
||||||
wxTextDataObject *obj = new wxTextDataObject;
|
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value,
|
||||||
obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item)));
|
* so set some nonempty string
|
||||||
|
*/
|
||||||
|
wxTextDataObject* obj = new wxTextDataObject;
|
||||||
|
obj->SetText("Some text");//it's needed for GTK
|
||||||
|
|
||||||
event.SetDataObject(obj);
|
event.SetDataObject(obj);
|
||||||
event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move;
|
event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::on_drop_possible(wxDataViewEvent &event)
|
void ObjectList::OnDropPossible(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
wxDataViewItem item(event.GetItem());
|
wxDataViewItem item(event.GetItem());
|
||||||
|
|
||||||
// only allow drags for item or background, not containers
|
// only allow drags for item or background, not containers
|
||||||
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
if (!item.IsOk() ||
|
||||||
event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume)
|
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||||
|
m_objects_model->GetItemType(item) != itVolume ||
|
||||||
|
m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item))
|
||||||
event.Veto();
|
event.Veto();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::on_drop(wxDataViewEvent &event)
|
void ObjectList::OnDrop(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
wxDataViewItem item(event.GetItem());
|
wxDataViewItem item(event.GetItem());
|
||||||
|
|
||||||
// only allow drops for item, not containers
|
if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||||
if (m_selected_object_id < 0 ||
|
m_objects_model->GetItemType(item) != itVolume ||
|
||||||
item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) {
|
||||||
event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) {
|
|
||||||
event.Veto();
|
event.Veto();
|
||||||
|
m_dragged_data.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTextDataObject obj;
|
const int from_volume_id = m_dragged_data.vol_idx();
|
||||||
obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer());
|
|
||||||
|
|
||||||
int from_volume_id = std::stoi(obj.GetText().ToStdString());
|
|
||||||
int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
|
int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
|
||||||
#ifdef __WXGTK__
|
// It looks like a fixed in current version of the wxWidgets
|
||||||
/* Under GTK, DnD moves an item between another two items.
|
// #ifdef __WXGTK__
|
||||||
* And event.GetItem() return item, which is under "insertion line"
|
// /* Under GTK, DnD moves an item between another two items.
|
||||||
* So, if we move item down we should to decrease the to_volume_id value
|
// * And event.GetItem() return item, which is under "insertion line"
|
||||||
**/
|
// * So, if we move item down we should to decrease the to_volume_id value
|
||||||
if (to_volume_id > from_volume_id) to_volume_id--;
|
// **/
|
||||||
#endif // __WXGTK__
|
// if (to_volume_id > from_volume_id) to_volume_id--;
|
||||||
|
// #endif // __WXGTK__
|
||||||
|
|
||||||
auto& volumes = (*m_objects)[m_selected_object_id]->volumes;
|
auto& volumes = (*m_objects)[/*m_selected_object_id*/m_dragged_data.obj_idx()]->volumes;
|
||||||
auto delta = to_volume_id < from_volume_id ? -1 : 1;
|
auto delta = to_volume_id < from_volume_id ? -1 : 1;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
|
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
|
||||||
@ -449,9 +516,9 @@ void ObjectList::on_drop(wxDataViewEvent &event)
|
|||||||
m_objects_model->GetParent(item)));
|
m_objects_model->GetParent(item)));
|
||||||
|
|
||||||
m_parts_changed = true;
|
m_parts_changed = true;
|
||||||
parts_changed(m_selected_object_id);
|
parts_changed(/*m_selected_object_id*/m_dragged_data.obj_idx());
|
||||||
|
|
||||||
// m_prevent_list_events = false;
|
m_dragged_data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -571,9 +638,6 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||||||
const auto settings_item = m_objects_model->GetSettingsItem(item);
|
const auto settings_item = m_objects_model->GetSettingsItem(item);
|
||||||
select_item(settings_item ? settings_item :
|
select_item(settings_item ? settings_item :
|
||||||
m_objects_model->AddSettingsChild(item));
|
m_objects_model->AddSettingsChild(item));
|
||||||
#ifndef __WXOSX__
|
|
||||||
// part_selection_changed();
|
|
||||||
#endif //no __WXOSX__
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto panel = wxGetApp().sidebar().scrolled_panel();
|
auto panel = wxGetApp().sidebar().scrolled_panel();
|
||||||
@ -747,11 +811,6 @@ void ObjectList::load_subobject(int type)
|
|||||||
if (i == part_names.size() - 1)
|
if (i == part_names.size() - 1)
|
||||||
select_item(sel_item);
|
select_item(sel_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
|
||||||
// selection_changed();
|
|
||||||
#endif //no __WXOSX__//__WXMSW__
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::load_part( ModelObject* model_object,
|
void ObjectList::load_part( ModelObject* model_object,
|
||||||
@ -1138,7 +1197,9 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||||||
{
|
{
|
||||||
auto model_object = (*m_objects)[obj_idx];
|
auto model_object = (*m_objects)[obj_idx];
|
||||||
wxString item_name = model_object->name;
|
wxString item_name = model_object->name;
|
||||||
auto item = m_objects_model->Add(item_name);
|
const auto item = m_objects_model->Add(item_name,
|
||||||
|
!model_object->config.has("extruder") ? 0 :
|
||||||
|
model_object->config.option<ConfigOptionInt>("extruder")->value);
|
||||||
|
|
||||||
// Add error icon if detected auto-repaire
|
// Add error icon if detected auto-repaire
|
||||||
auto stats = model_object->volumes[0]->mesh.stl.stats;
|
auto stats = model_object->volumes[0]->mesh.stl.stats;
|
||||||
@ -1152,13 +1213,19 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||||||
|
|
||||||
// add volumes to the object
|
// add volumes to the object
|
||||||
if (model_object->volumes.size() > 1) {
|
if (model_object->volumes.size() > 1) {
|
||||||
for (auto id = 0; id < model_object->volumes.size(); id++)
|
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
||||||
m_objects_model->AddVolumeChild(item,
|
auto vol_item = m_objects_model->AddVolumeChild(item,
|
||||||
model_object->volumes[id]->name,
|
model_object->volumes[id]->name,
|
||||||
ModelVolume::MODEL_PART,
|
model_object->volumes[id]->type()/*ModelVolume::MODEL_PART*/,
|
||||||
!model_object->volumes[id]->config.has("extruder") ? 0 :
|
!model_object->volumes[id]->config.has("extruder") ? 0 :
|
||||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
||||||
false);
|
false);
|
||||||
|
auto opt_keys = model_object->volumes[id]->config.keys();
|
||||||
|
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
||||||
|
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||||
|
Collapse(vol_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
Expand(item);
|
Expand(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1391,6 +1458,14 @@ void ObjectList::update_selections()
|
|||||||
}
|
}
|
||||||
|
|
||||||
select_items(sels);
|
select_items(sels);
|
||||||
|
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
if (GetSelection()) {
|
||||||
|
const int sel_item_row = GetRowByItem(GetSelection());
|
||||||
|
ScrollLines(sel_item_row - m_selected_row);
|
||||||
|
m_selected_row = sel_item_row;
|
||||||
|
}
|
||||||
|
#endif //__WXMSW__
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::update_selections_on_canvas()
|
void ObjectList::update_selections_on_canvas()
|
||||||
@ -1532,7 +1607,24 @@ void ObjectList::change_part_type()
|
|||||||
ModelVolume* volume = get_selected_model_volume();
|
ModelVolume* volume = get_selected_model_volume();
|
||||||
if (!volume)
|
if (!volume)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto type = volume->type();
|
const auto type = volume->type();
|
||||||
|
if (type == ModelVolume::MODEL_PART)
|
||||||
|
{
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (obj_idx < 0) return;
|
||||||
|
|
||||||
|
int model_part_cnt = 0;
|
||||||
|
for (auto vol : (*m_objects)[obj_idx]->volumes) {
|
||||||
|
if (vol->type() == ModelVolume::MODEL_PART)
|
||||||
|
++model_part_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model_part_cnt == 1) {
|
||||||
|
Slic3r::GUI::show_error(nullptr, _(L("You can't change a type of the last solid part of the object.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" };
|
const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" };
|
||||||
|
|
||||||
@ -1552,11 +1644,11 @@ void ObjectList::change_part_type()
|
|||||||
//(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
|
//(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
|
||||||
const auto settings_item = m_objects_model->GetSettingsItem(item);
|
const auto settings_item = m_objects_model->GetSettingsItem(item);
|
||||||
if (settings_item &&
|
if (settings_item &&
|
||||||
new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) {
|
(new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER)) {
|
||||||
m_objects_model->Delete(settings_item);
|
m_objects_model->Delete(settings_item);
|
||||||
}
|
}
|
||||||
else if (!settings_item &&
|
else if (!settings_item &&
|
||||||
new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) {
|
(new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER)) {
|
||||||
select_item(m_objects_model->AddSettingsChild(item));
|
select_item(m_objects_model->AddSettingsChild(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1600,11 +1692,24 @@ void ObjectList::update_settings_items()
|
|||||||
UnselectAll();
|
UnselectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::OnStartEditing(wxDataViewEvent &event)
|
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
const auto item_type = m_objects_model->GetItemType(event.GetItem());
|
if (event.GetColumn() == 0)
|
||||||
if ( !(item_type&(itObject|itVolume)) )
|
update_name_in_model(event.GetItem());
|
||||||
event.Veto();
|
else if (event.GetColumn() == 1)
|
||||||
|
update_extruder_in_config(event.GetItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
||||||
|
{
|
||||||
|
if (event.GetColumn() != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto renderer = dynamic_cast<PrusaBitmapTextRenderer*>(GetColumn(0)->GetRenderer());
|
||||||
|
|
||||||
|
if (renderer->WasCanceled())
|
||||||
|
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
|
||||||
|
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace GUI
|
} //namespace GUI
|
||||||
|
@ -53,6 +53,27 @@ struct ItemForDelete
|
|||||||
|
|
||||||
class ObjectList : public wxDataViewCtrl
|
class ObjectList : public wxDataViewCtrl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct dragged_item_data
|
||||||
|
{
|
||||||
|
void init(const int obj_idx, const int vol_idx) {
|
||||||
|
m_obj_idx = obj_idx;
|
||||||
|
m_vol_idx = vol_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
m_obj_idx = -1;
|
||||||
|
m_vol_idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int obj_idx() const { return m_obj_idx; }
|
||||||
|
int vol_idx() const { return m_vol_idx; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_obj_idx = -1;
|
||||||
|
int m_vol_idx = -1;
|
||||||
|
} m_dragged_data;
|
||||||
|
|
||||||
wxBoxSizer *m_sizer {nullptr};
|
wxBoxSizer *m_sizer {nullptr};
|
||||||
|
|
||||||
DynamicPrintConfig *m_default_config {nullptr};
|
DynamicPrintConfig *m_default_config {nullptr};
|
||||||
@ -80,12 +101,15 @@ class ObjectList : public wxDataViewCtrl
|
|||||||
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
|
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
|
||||||
// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler
|
// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler
|
||||||
// calls this method again and again and again
|
// calls this method again and again and again
|
||||||
#ifdef __WXOSX__
|
|
||||||
wxString m_selected_extruder = "";
|
bool m_prevent_update_extruder_in_config = false; // We use this flag to avoid updating of the extruder value in config
|
||||||
#endif //__WXOSX__
|
// during updating of the extruder count.
|
||||||
|
|
||||||
bool m_parts_changed = false;
|
bool m_parts_changed = false;
|
||||||
bool m_part_settings_changed = false;
|
bool m_part_settings_changed = false;
|
||||||
|
|
||||||
|
int m_selected_row = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectList(wxWindow* parent);
|
ObjectList(wxWindow* parent);
|
||||||
~ObjectList();
|
~ObjectList();
|
||||||
@ -103,23 +127,20 @@ public:
|
|||||||
wxDataViewColumn* create_objects_list_extruder_column(int extruders_count);
|
wxDataViewColumn* create_objects_list_extruder_column(int extruders_count);
|
||||||
void update_objects_list_extruder_column(int extruders_count);
|
void update_objects_list_extruder_column(int extruders_count);
|
||||||
// show/hide "Extruder" column for Objects List
|
// show/hide "Extruder" column for Objects List
|
||||||
void set_extruder_column_hidden(bool hide);
|
void set_extruder_column_hidden(const bool hide) const;
|
||||||
// update extruder in current config
|
// update extruder in current config
|
||||||
void update_extruder_in_config(const wxString& selection);
|
void update_extruder_in_config(const wxDataViewItem& item);
|
||||||
|
// update changed name in the object model
|
||||||
|
void update_name_in_model(const wxDataViewItem& item);
|
||||||
|
void update_extruder_values_for_items(const int max_extruder);
|
||||||
|
|
||||||
void init_icons();
|
void init_icons();
|
||||||
|
|
||||||
void set_tooltip_for_item(const wxPoint& pt);
|
void set_tooltip_for_item(const wxPoint& pt);
|
||||||
|
|
||||||
void selection_changed();
|
void selection_changed();
|
||||||
void context_menu();
|
|
||||||
void show_context_menu();
|
void show_context_menu();
|
||||||
void key_event(wxKeyEvent& event);
|
void key_event(wxKeyEvent& event);
|
||||||
void item_value_change(wxDataViewEvent& event);
|
|
||||||
|
|
||||||
void on_begin_drag(wxDataViewEvent &event);
|
|
||||||
void on_drop_possible(wxDataViewEvent &event);
|
|
||||||
void on_drop(wxDataViewEvent &event);
|
|
||||||
|
|
||||||
void get_settings_choice(const wxString& category_name);
|
void get_settings_choice(const wxString& category_name);
|
||||||
void append_menu_item_add_generic(wxMenuItem* menu, const int type);
|
void append_menu_item_add_generic(wxMenuItem* menu, const int type);
|
||||||
@ -202,8 +223,15 @@ public:
|
|||||||
void update_settings_items();
|
void update_settings_items();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnStartEditing(wxDataViewEvent &event);
|
void OnChar(wxKeyEvent& event);
|
||||||
|
void OnContextMenu(wxDataViewEvent &event);
|
||||||
|
|
||||||
|
void OnBeginDrag(wxDataViewEvent &event);
|
||||||
|
void OnDropPossible(wxDataViewEvent &event);
|
||||||
|
void OnDrop(wxDataViewEvent &event);
|
||||||
|
|
||||||
|
void ItemValueChanged(wxDataViewEvent &event);
|
||||||
|
void OnEditingDone(wxDataViewEvent &event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,9 +21,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||||||
m_og->set_name(_(L("Object Manipulation")));
|
m_og->set_name(_(L("Object Manipulation")));
|
||||||
m_og->label_width = 100;
|
m_og->label_width = 100;
|
||||||
m_og->set_grid_vgap(5);
|
m_og->set_grid_vgap(5);
|
||||||
m_og->set_process_enter(); // We need to update new values only after press ENTER
|
|
||||||
|
|
||||||
m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
|
||||||
std::vector<std::string> axes{ "_x", "_y", "_z" };
|
std::vector<std::string> axes{ "_x", "_y", "_z" };
|
||||||
|
|
||||||
if (opt_key == "scale_unit") {
|
if (opt_key == "scale_unit") {
|
||||||
@ -54,6 +53,29 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||||||
change_scale_value(new_value);
|
change_scale_value(new_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
m_og->m_fill_empty_value = [this](const std::string& opt_key)
|
||||||
|
{
|
||||||
|
if (opt_key == "scale_unit")
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string param;
|
||||||
|
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||||
|
if (param == "position") {
|
||||||
|
int axis = opt_key.back() == 'x' ? 0 :
|
||||||
|
opt_key.back() == 'y' ? 1 : 2;
|
||||||
|
|
||||||
|
m_og->set_value(opt_key, double_to_string(cache_position(axis)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_og->set_value(opt_key, double_to_string(0.0));
|
||||||
|
};
|
||||||
|
|
||||||
|
m_og->m_set_focus = [this](const std::string& opt_key)
|
||||||
|
{
|
||||||
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key);
|
||||||
|
};
|
||||||
|
|
||||||
ConfigOptionDef def;
|
ConfigOptionDef def;
|
||||||
|
|
||||||
// Objects(sub-objects) name
|
// Objects(sub-objects) name
|
||||||
|
@ -17,7 +17,7 @@ protected:
|
|||||||
wxWindow* m_parent;
|
wxWindow* m_parent;
|
||||||
public:
|
public:
|
||||||
OG_Settings(wxWindow* parent, const bool staticbox);
|
OG_Settings(wxWindow* parent, const bool staticbox);
|
||||||
~OG_Settings() {}
|
virtual ~OG_Settings() {}
|
||||||
|
|
||||||
virtual bool IsShown();
|
virtual bool IsShown();
|
||||||
virtual void Show(const bool show);
|
virtual void Show(const bool show);
|
||||||
|
@ -794,7 +794,8 @@ void MainFrame::update_ui_from_settings()
|
|||||||
{
|
{
|
||||||
bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
|
bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
|
||||||
m_menu_item_reslice_now->Enable(bp_on);
|
m_menu_item_reslice_now->Enable(bp_on);
|
||||||
m_plater->sidebar().show_button(baReslice, !bp_on);
|
m_plater->sidebar().show_reslice(!bp_on);
|
||||||
|
m_plater->sidebar().Layout();
|
||||||
if (m_plater)
|
if (m_plater)
|
||||||
m_plater->update_ui_from_settings();
|
m_plater->update_ui_from_settings();
|
||||||
for (auto tab: wxGetApp().tabs_list)
|
for (auto tab: wxGetApp().tabs_list)
|
||||||
|
@ -44,7 +44,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
|
|||||||
case coPercents:
|
case coPercents:
|
||||||
case coString:
|
case coString:
|
||||||
case coStrings:
|
case coStrings:
|
||||||
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id, process_enter)));
|
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
|
||||||
break;
|
break;
|
||||||
case coBool:
|
case coBool:
|
||||||
case coBools:
|
case coBools:
|
||||||
@ -67,16 +67,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
|
|||||||
}
|
}
|
||||||
// Grab a reference to fields for convenience
|
// Grab a reference to fields for convenience
|
||||||
const t_field& field = m_fields[id];
|
const t_field& field = m_fields[id];
|
||||||
field->m_on_change = [this](std::string opt_id, boost::any value) {
|
field->m_on_change = [this](const std::string& opt_id, const boost::any& value) {
|
||||||
//! This function will be called from Field.
|
//! This function will be called from Field.
|
||||||
//! Call OptionGroup._on_change(...)
|
//! Call OptionGroup._on_change(...)
|
||||||
if (!m_disabled)
|
if (!m_disabled)
|
||||||
this->on_change_OG(opt_id, value);
|
this->on_change_OG(opt_id, value);
|
||||||
};
|
};
|
||||||
field->m_on_kill_focus = [this]() {
|
field->m_on_kill_focus = [this](const std::string& opt_id) {
|
||||||
//! This function will be called from Field.
|
//! This function will be called from Field.
|
||||||
if (!m_disabled)
|
if (!m_disabled)
|
||||||
this->on_kill_focus();
|
this->on_kill_focus(opt_id);
|
||||||
|
};
|
||||||
|
field->m_on_set_focus = [this](const std::string& opt_id) {
|
||||||
|
//! This function will be called from Field.
|
||||||
|
if (!m_disabled)
|
||||||
|
this->on_set_focus(opt_id);
|
||||||
};
|
};
|
||||||
field->m_parent = parent();
|
field->m_parent = parent();
|
||||||
|
|
||||||
@ -277,6 +282,12 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OptionsGroup::on_set_focus(const std::string& opt_key)
|
||||||
|
{
|
||||||
|
if (m_set_focus != nullptr)
|
||||||
|
m_set_focus(opt_key);
|
||||||
|
}
|
||||||
|
|
||||||
void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) {
|
void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) {
|
||||||
if (m_on_change != nullptr)
|
if (m_on_change != nullptr)
|
||||||
m_on_change(opt_id, value);
|
m_on_change(opt_id, value);
|
||||||
@ -378,6 +389,15 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
|
|||||||
on_change_OG(opt_key, get_value(opt_key));
|
on_change_OG(opt_key, get_value(opt_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigOptionsGroup::on_kill_focus(const std::string& opt_key)
|
||||||
|
{
|
||||||
|
if (m_fill_empty_value) {
|
||||||
|
m_fill_empty_value(opt_key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reload_config();
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigOptionsGroup::reload_config() {
|
void ConfigOptionsGroup::reload_config() {
|
||||||
for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
|
for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) {
|
||||||
auto opt_id = it->first;
|
auto opt_id = it->first;
|
||||||
@ -534,6 +554,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
|||||||
else if (opt_key.compare("host_type") == 0) {
|
else if (opt_key.compare("host_type") == 0) {
|
||||||
ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value);
|
ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value);
|
||||||
}
|
}
|
||||||
|
else if (opt_key.compare("display_orientation") == 0) {
|
||||||
|
ret = static_cast<int>(config.option<ConfigOptionEnum<SLADisplayOrientation>>(opt_key)->value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case coPoints:
|
case coPoints:
|
||||||
|
@ -85,7 +85,9 @@ public:
|
|||||||
size_t label_width {200};
|
size_t label_width {200};
|
||||||
wxSizer* sizer {nullptr};
|
wxSizer* sizer {nullptr};
|
||||||
column_t extra_column {nullptr};
|
column_t extra_column {nullptr};
|
||||||
t_change m_on_change {nullptr};
|
t_change m_on_change { nullptr };
|
||||||
|
t_kill_focus m_fill_empty_value { nullptr };
|
||||||
|
t_kill_focus m_set_focus { nullptr };
|
||||||
std::function<DynamicPrintConfig()> m_get_initial_config{ nullptr };
|
std::function<DynamicPrintConfig()> m_get_initial_config{ nullptr };
|
||||||
std::function<DynamicPrintConfig()> m_get_sys_config{ nullptr };
|
std::function<DynamicPrintConfig()> m_get_sys_config{ nullptr };
|
||||||
std::function<bool()> have_sys_config{ nullptr };
|
std::function<bool()> have_sys_config{ nullptr };
|
||||||
@ -94,8 +96,6 @@ public:
|
|||||||
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
||||||
int sidetext_width{ -1 };
|
int sidetext_width{ -1 };
|
||||||
|
|
||||||
bool process_enter { false };
|
|
||||||
|
|
||||||
/// Returns a copy of the pointer of the parent wxWindow.
|
/// Returns a copy of the pointer of the parent wxWindow.
|
||||||
/// Accessor function is because users are not allowed to change the parent
|
/// Accessor function is because users are not allowed to change the parent
|
||||||
/// but defining it as const means a lot of const_casts to deal with wx functions.
|
/// but defining it as const means a lot of const_casts to deal with wx functions.
|
||||||
@ -154,11 +154,6 @@ public:
|
|||||||
m_show_modified_btns = show;
|
m_show_modified_btns = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The controls inside this option group will generate the event wxEVT_TEXT_ENTER
|
|
||||||
void set_process_enter() {
|
|
||||||
process_enter = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
|
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
|
||||||
column_t extra_clmn = nullptr) :
|
column_t extra_clmn = nullptr) :
|
||||||
m_parent(_parent), title(title),
|
m_parent(_parent), title(title),
|
||||||
@ -215,7 +210,8 @@ protected:
|
|||||||
const t_field& build_field(const Option& opt, wxStaticText* label = nullptr);
|
const t_field& build_field(const Option& opt, wxStaticText* label = nullptr);
|
||||||
void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
|
void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field);
|
||||||
|
|
||||||
virtual void on_kill_focus () {};
|
virtual void on_kill_focus(const std::string& opt_key) {};
|
||||||
|
virtual void on_set_focus(const std::string& opt_key);
|
||||||
virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
|
virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
|
||||||
virtual void back_to_initial_value(const std::string& opt_key) {}
|
virtual void back_to_initial_value(const std::string& opt_key) {}
|
||||||
virtual void back_to_sys_value(const std::string& opt_key) {}
|
virtual void back_to_sys_value(const std::string& opt_key) {}
|
||||||
@ -251,7 +247,7 @@ public:
|
|||||||
void back_to_initial_value(const std::string& opt_key) override;
|
void back_to_initial_value(const std::string& opt_key) override;
|
||||||
void back_to_sys_value(const std::string& opt_key) override;
|
void back_to_sys_value(const std::string& opt_key) override;
|
||||||
void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key);
|
void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key);
|
||||||
void on_kill_focus() override{ reload_config();}
|
void on_kill_focus(const std::string& opt_key) override;// { reload_config(); }
|
||||||
void reload_config();
|
void reload_config();
|
||||||
// return value shows visibility : false => all options are hidden
|
// return value shows visibility : false => all options are hidden
|
||||||
void Hide();
|
void Hide();
|
||||||
|
@ -54,7 +54,9 @@
|
|||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
#include "BackgroundSlicingProcess.hpp"
|
#include "BackgroundSlicingProcess.hpp"
|
||||||
#include "ProgressStatusBar.hpp"
|
#include "ProgressStatusBar.hpp"
|
||||||
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "../Utils/ASCIIFolding.hpp"
|
#include "../Utils/ASCIIFolding.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
#include "../Utils/FixModelByWin10.hpp"
|
#include "../Utils/FixModelByWin10.hpp"
|
||||||
|
|
||||||
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
|
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
|
||||||
@ -64,6 +66,7 @@ using boost::optional;
|
|||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
using Slic3r::_3DScene;
|
using Slic3r::_3DScene;
|
||||||
using Slic3r::Preset;
|
using Slic3r::Preset;
|
||||||
|
using Slic3r::PrintHostJob;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -323,17 +326,17 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||||||
double brim_width = config->opt_float("brim_width");
|
double brim_width = config->opt_float("brim_width");
|
||||||
if (boost::any_cast<bool>(value) == true)
|
if (boost::any_cast<bool>(value) == true)
|
||||||
{
|
{
|
||||||
new_val = m_brim_width == 0.0 ? 10 :
|
new_val = m_brim_width == 0.0 ? 5 :
|
||||||
m_brim_width < 0.0 ? m_brim_width * (-1) :
|
m_brim_width < 0.0 ? m_brim_width * (-1) :
|
||||||
m_brim_width;
|
m_brim_width;
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
m_brim_width = brim_width * (-1);
|
m_brim_width = brim_width * (-1);
|
||||||
new_val = 0;
|
new_val = 0;
|
||||||
}
|
}
|
||||||
new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val));
|
new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val));
|
||||||
}
|
}
|
||||||
else{ //(opt_key == "support")
|
else { //(opt_key == "support")
|
||||||
const wxString& selection = boost::any_cast<wxString>(value);
|
const wxString& selection = boost::any_cast<wxString>(value);
|
||||||
|
|
||||||
auto support_material = selection == _("None") ? false : true;
|
auto support_material = selection == _("None") ? false : true;
|
||||||
@ -447,7 +450,6 @@ struct Sidebar::priv
|
|||||||
|
|
||||||
wxButton *btn_export_gcode;
|
wxButton *btn_export_gcode;
|
||||||
wxButton *btn_reslice;
|
wxButton *btn_reslice;
|
||||||
// wxButton *btn_print; // XXX: remove
|
|
||||||
wxButton *btn_send_gcode;
|
wxButton *btn_send_gcode;
|
||||||
|
|
||||||
priv(Plater *plater) : plater(plater) {}
|
priv(Plater *plater) : plater(plater) {}
|
||||||
@ -543,13 +545,11 @@ Sidebar::Sidebar(Plater *parent)
|
|||||||
p->object_settings->Hide();
|
p->object_settings->Hide();
|
||||||
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||||
|
|
||||||
// Buttons in the scrolled area
|
|
||||||
wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG);
|
wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG);
|
||||||
p->btn_send_gcode = new wxButton(p->scrolled, wxID_ANY, _(L("Send to printer")));
|
p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer")));
|
||||||
p->btn_send_gcode->SetBitmap(arrow_up);
|
p->btn_send_gcode->SetBitmap(arrow_up);
|
||||||
|
p->btn_send_gcode->SetFont(wxGetApp().bold_font());
|
||||||
p->btn_send_gcode->Hide();
|
p->btn_send_gcode->Hide();
|
||||||
auto *btns_sizer_scrolled = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
btns_sizer_scrolled->Add(p->btn_send_gcode);
|
|
||||||
|
|
||||||
// Info boxes
|
// Info boxes
|
||||||
p->object_info = new ObjectInfo(p->scrolled);
|
p->object_info = new ObjectInfo(p->scrolled);
|
||||||
@ -559,7 +559,6 @@ Sidebar::Sidebar(Plater *parent)
|
|||||||
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2);
|
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2);
|
||||||
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND);
|
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND);
|
||||||
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
||||||
scrolled_sizer->Add(btns_sizer_scrolled, 0, wxEXPAND, 0);
|
|
||||||
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
||||||
|
|
||||||
// Buttons underneath the scrolled area
|
// Buttons underneath the scrolled area
|
||||||
@ -571,6 +570,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||||||
|
|
||||||
auto *btns_sizer = new wxBoxSizer(wxVERTICAL);
|
auto *btns_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, 5);
|
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, 5);
|
||||||
|
btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, 5);
|
||||||
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, 5);
|
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, 5);
|
||||||
|
|
||||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
@ -820,14 +820,6 @@ void Sidebar::show_sliced_info_sizer(const bool show)
|
|||||||
p->scrolled->Refresh();
|
p->scrolled->Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sidebar::show_buttons(const bool show)
|
|
||||||
{
|
|
||||||
p->btn_reslice->Show(show);
|
|
||||||
TabPrinter *tab = dynamic_cast<TabPrinter*>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
|
||||||
if (tab && p->plater->printer_technology() == ptFFF)
|
|
||||||
p->btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sidebar::enable_buttons(bool enable)
|
void Sidebar::enable_buttons(bool enable)
|
||||||
{
|
{
|
||||||
p->btn_reslice->Enable(enable);
|
p->btn_reslice->Enable(enable);
|
||||||
@ -835,23 +827,8 @@ void Sidebar::enable_buttons(bool enable)
|
|||||||
p->btn_send_gcode->Enable(enable);
|
p->btn_send_gcode->Enable(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sidebar::show_button(ButtonAction but_action, bool show)
|
void Sidebar::show_reslice(bool show) { p->btn_reslice->Show(show); }
|
||||||
{
|
void Sidebar::show_send(bool show) { p->btn_send_gcode->Show(show); }
|
||||||
switch (but_action)
|
|
||||||
{
|
|
||||||
case baReslice:
|
|
||||||
p->btn_reslice->Show(show);
|
|
||||||
break;
|
|
||||||
case baExportGcode:
|
|
||||||
p->btn_export_gcode->Show(show);
|
|
||||||
break;
|
|
||||||
case baSendGcode:
|
|
||||||
p->btn_send_gcode->Show(show);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Sidebar::is_multifilament()
|
bool Sidebar::is_multifilament()
|
||||||
{
|
{
|
||||||
@ -1008,6 +985,7 @@ struct Plater::priv
|
|||||||
};
|
};
|
||||||
// returns bit mask of UpdateBackgroundProcessReturnState
|
// returns bit mask of UpdateBackgroundProcessReturnState
|
||||||
unsigned int update_background_process();
|
unsigned int update_background_process();
|
||||||
|
void export_gcode(fs::path output_path, PrintHostJob upload_job);
|
||||||
void async_apply_config();
|
void async_apply_config();
|
||||||
void reload_from_disk();
|
void reload_from_disk();
|
||||||
void fix_through_netfabb(const int obj_idx);
|
void fix_through_netfabb(const int obj_idx);
|
||||||
@ -1453,6 +1431,18 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check multi-part object adding for the SLA-printing
|
||||||
|
if (printer_technology == ptSLA)
|
||||||
|
{
|
||||||
|
for (auto obj : model.objects)
|
||||||
|
if ( obj->volumes.size()>1 ) {
|
||||||
|
Slic3r::GUI::show_error(nullptr,
|
||||||
|
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),
|
||||||
|
filename.string()));
|
||||||
|
return std::vector<size_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (one_by_one) {
|
if (one_by_one) {
|
||||||
auto loaded_idxs = load_model_objects(model.objects);
|
auto loaded_idxs = load_model_objects(model.objects);
|
||||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||||
@ -2047,6 +2037,45 @@ unsigned int Plater::priv::update_background_process()
|
|||||||
return return_state;
|
return return_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty");
|
||||||
|
|
||||||
|
if (model.objects.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (background_process.is_export_scheduled()) {
|
||||||
|
GUI::show_error(q, _(L("Another export job is currently running.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitmask of UpdateBackgroundProcessReturnState
|
||||||
|
unsigned int state = update_background_process();
|
||||||
|
if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE)
|
||||||
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
view3D->reload_scene(false);
|
||||||
|
#else
|
||||||
|
canvas3D->reload_scene(false);
|
||||||
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (! output_path.empty()) {
|
||||||
|
background_process.schedule_export(output_path.string());
|
||||||
|
} else {
|
||||||
|
background_process.schedule_upload(std::move(upload_job));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! background_process.running()) {
|
||||||
|
// The print is valid and it should be started.
|
||||||
|
if (background_process.start())
|
||||||
|
statusbar()->set_cancel_callback([this]() {
|
||||||
|
statusbar()->set_status_text(L("Cancelling"));
|
||||||
|
background_process.stop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::priv::async_apply_config()
|
void Plater::priv::async_apply_config()
|
||||||
{
|
{
|
||||||
// bitmask of UpdateBackgroundProcessReturnState
|
// bitmask of UpdateBackgroundProcessReturnState
|
||||||
@ -2200,6 +2229,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||||||
}
|
}
|
||||||
else if (current_panel == preview)
|
else if (current_panel == preview)
|
||||||
{
|
{
|
||||||
|
this->q->reslice();
|
||||||
preview->reload_print();
|
preview->reload_print();
|
||||||
preview->set_canvas_as_dirty();
|
preview->set_canvas_as_dirty();
|
||||||
}
|
}
|
||||||
@ -2443,7 +2473,8 @@ void Plater::priv::on_right_click(Vec2dEvent& evt)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
|
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
|
||||||
get_selection().is_single_full_object() ? &object_menu : &part_menu;
|
get_selection().is_single_full_instance/*object*/() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||||
|
&object_menu : &part_menu;
|
||||||
|
|
||||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||||
|
|
||||||
@ -2899,22 +2930,6 @@ void Plater::export_gcode(fs::path output_path)
|
|||||||
if (p->model.objects.empty())
|
if (p->model.objects.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->p->background_process.is_export_scheduled()) {
|
|
||||||
GUI::show_error(this, _(L("Another export job is currently running.")));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bitmask of UpdateBackgroundProcessReturnState
|
|
||||||
unsigned int state = this->p->update_background_process();
|
|
||||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE)
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
|
||||||
this->p->view3D->reload_scene(false);
|
|
||||||
#else
|
|
||||||
this->p->canvas3D->reload_scene(false);
|
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
|
||||||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// select output file
|
// select output file
|
||||||
if (output_path.empty()) {
|
if (output_path.empty()) {
|
||||||
// XXX: take output path from CLI opts? Ancient Slic3r versions used to do that...
|
// XXX: take output path from CLI opts? Ancient Slic3r versions used to do that...
|
||||||
@ -2952,16 +2967,8 @@ void Plater::export_gcode(fs::path output_path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! output_path.empty())
|
if (! output_path.empty()) {
|
||||||
this->p->background_process.schedule_export(output_path.string());
|
p->export_gcode(std::move(output_path), PrintHostJob());
|
||||||
|
|
||||||
if ((! output_path.empty() || this->p->background_processing_enabled()) && ! this->p->background_process.running()) {
|
|
||||||
// The print is valid and it should be started.
|
|
||||||
if (this->p->background_process.start())
|
|
||||||
this->p->statusbar()->set_cancel_callback([this]() {
|
|
||||||
this->p->statusbar()->set_status_text(L("Cancelling"));
|
|
||||||
this->p->background_process.stop();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3052,7 +3059,7 @@ void Plater::reslice()
|
|||||||
#else
|
#else
|
||||||
this->p->canvas3D->reload_scene(false);
|
this->p->canvas3D->reload_scene(false);
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running()) {
|
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running() && !this->p->background_process.finished()) {
|
||||||
// The print is valid and it can be started.
|
// The print is valid and it can be started.
|
||||||
if (this->p->background_process.start())
|
if (this->p->background_process.start())
|
||||||
this->p->statusbar()->set_cancel_callback([this]() {
|
this->p->statusbar()->set_cancel_callback([this]() {
|
||||||
@ -3064,7 +3071,28 @@ void Plater::reslice()
|
|||||||
|
|
||||||
void Plater::send_gcode()
|
void Plater::send_gcode()
|
||||||
{
|
{
|
||||||
// p->send_gcode_file = export_gcode();
|
if (p->model.objects.empty()) { return; }
|
||||||
|
|
||||||
|
PrintHostJob upload_job(p->config);
|
||||||
|
if (upload_job.empty()) { return; }
|
||||||
|
|
||||||
|
// Obtain default output path
|
||||||
|
fs::path default_output_file;
|
||||||
|
try {
|
||||||
|
default_output_file = this->p->background_process.current_print()->output_filepath("");
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
show_error(this, ex.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||||
|
|
||||||
|
Slic3r::PrintHostSendDialog dlg(default_output_file);
|
||||||
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
|
upload_job.upload_data.start_print = dlg.start_print();
|
||||||
|
|
||||||
|
p->export_gcode(fs::path(), std::move(upload_job));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::on_extruders_change(int num_extruders)
|
void Plater::on_extruders_change(int num_extruders)
|
||||||
@ -3114,14 +3142,6 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||||||
opt_key == "single_extruder_multi_material") {
|
opt_key == "single_extruder_multi_material") {
|
||||||
update_scheduled = true;
|
update_scheduled = true;
|
||||||
}
|
}
|
||||||
// else if(opt_key == "serial_port") {
|
|
||||||
// sidebar()->p->btn_print->Show(config.get("serial_port")); // ???: btn_print is removed
|
|
||||||
// Layout();
|
|
||||||
// }
|
|
||||||
else if (opt_key == "print_host") {
|
|
||||||
sidebar().show_button(baReslice, !p->config->option<ConfigOptionString>(opt_key)->value.empty());
|
|
||||||
Layout();
|
|
||||||
}
|
|
||||||
else if(opt_key == "variable_layer_height") {
|
else if(opt_key == "variable_layer_height") {
|
||||||
if (p->config->opt_bool("variable_layer_height") != true) {
|
if (p->config->opt_bool("variable_layer_height") != true) {
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
@ -3161,6 +3181,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto prin_host_opt = p->config->option<ConfigOptionString>("print_host");
|
||||||
|
p->sidebar->show_send(prin_host_opt != nullptr && !prin_host_opt->value.empty());
|
||||||
|
}
|
||||||
|
|
||||||
if (update_scheduled)
|
if (update_scheduled)
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -55,14 +55,6 @@ private:
|
|||||||
int extruder_idx = -1;
|
int extruder_idx = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ButtonAction
|
|
||||||
{
|
|
||||||
baUndef,
|
|
||||||
baReslice,
|
|
||||||
baExportGcode,
|
|
||||||
baSendGcode
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sidebar : public wxPanel
|
class Sidebar : public wxPanel
|
||||||
{
|
{
|
||||||
/*ConfigMenuIDs*/int m_mode;
|
/*ConfigMenuIDs*/int m_mode;
|
||||||
@ -88,9 +80,9 @@ public:
|
|||||||
void update_objects_list_extruder_column(int extruders_count);
|
void update_objects_list_extruder_column(int extruders_count);
|
||||||
void show_info_sizer();
|
void show_info_sizer();
|
||||||
void show_sliced_info_sizer(const bool show);
|
void show_sliced_info_sizer(const bool show);
|
||||||
void show_buttons(const bool show);
|
|
||||||
void show_button(ButtonAction but_action, bool show);
|
|
||||||
void enable_buttons(bool enable);
|
void enable_buttons(bool enable);
|
||||||
|
void show_reslice(bool show);
|
||||||
|
void show_send(bool show);
|
||||||
bool is_multifilament();
|
bool is_multifilament();
|
||||||
void set_mode_value(const /*ConfigMenuIDs*/int mode) { m_mode = mode; }
|
void set_mode_value(const /*ConfigMenuIDs*/int mode) { m_mode = mode; }
|
||||||
|
|
||||||
@ -103,6 +95,8 @@ private:
|
|||||||
class Plater: public wxPanel
|
class Plater: public wxPanel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using fs_path = boost::filesystem::path;
|
||||||
|
|
||||||
Plater(wxWindow *parent, MainFrame *main_frame);
|
Plater(wxWindow *parent, MainFrame *main_frame);
|
||||||
Plater(Plater &&) = delete;
|
Plater(Plater &&) = delete;
|
||||||
Plater(const Plater &) = delete;
|
Plater(const Plater &) = delete;
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif /* _MSC_VER */
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
@ -13,6 +19,7 @@
|
|||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#include <boost/nowide/cenv.hpp>
|
#include <boost/nowide/cenv.hpp>
|
||||||
|
#include <boost/nowide/convert.hpp>
|
||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <boost/nowide/fstream.hpp>
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
@ -446,6 +453,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
|||||||
"printer_technology",
|
"printer_technology",
|
||||||
"bed_shape", "max_print_height",
|
"bed_shape", "max_print_height",
|
||||||
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
||||||
|
"display_orientation",
|
||||||
"printer_correction",
|
"printer_correction",
|
||||||
"printer_notes",
|
"printer_notes",
|
||||||
"inherits"
|
"inherits"
|
||||||
@ -498,6 +506,16 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
|
|||||||
++ m_num_default_presets;
|
++ m_num_default_presets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_file_plain(const std::string &path)
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
DWORD attributes = GetFileAttributesW(boost::nowide::widen(path).c_str());
|
||||||
|
return (attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Load all presets found in dir_path.
|
// Load all presets found in dir_path.
|
||||||
// Throws an exception on error.
|
// Throws an exception on error.
|
||||||
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
|
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
|
||||||
@ -506,7 +524,10 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
|
|||||||
m_dir_path = dir.string();
|
m_dir_path = dir.string();
|
||||||
std::string errors_cummulative;
|
std::string errors_cummulative;
|
||||||
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
||||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) {
|
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini") &&
|
||||||
|
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.
|
||||||
|
// https://github.com/prusa3d/Slic3r/issues/1298
|
||||||
|
is_file_plain(dir_entry.path().string())) {
|
||||||
std::string name = dir_entry.path().filename().string();
|
std::string name = dir_entry.path().filename().string();
|
||||||
// Remove the .ini suffix.
|
// Remove the .ini suffix.
|
||||||
name.erase(name.size() - 4);
|
name.erase(name.size() - 4);
|
||||||
|
@ -1313,7 +1313,7 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings
|
void PresetBundle::export_configbundle(const std::string &path, bool export_system_settings)
|
||||||
{
|
{
|
||||||
boost::nowide::ofstream c;
|
boost::nowide::ofstream c;
|
||||||
c.open(path, std::ios::out | std::ios::trunc);
|
c.open(path, std::ios::out | std::ios::trunc);
|
||||||
@ -1323,14 +1323,15 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami
|
|||||||
|
|
||||||
// Export the print, filament and printer profiles.
|
// Export the print, filament and printer profiles.
|
||||||
|
|
||||||
// #ys_FIXME_SLA_PRINT
|
for (const PresetCollection *presets : {
|
||||||
for (size_t i_group = 0; i_group < 3; ++ i_group) {
|
(const PresetCollection*)&this->prints, (const PresetCollection*)&this->filaments,
|
||||||
const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers;
|
(const PresetCollection*)&this->sla_prints, (const PresetCollection*)&this->sla_materials,
|
||||||
for (const Preset &preset : presets()) {
|
(const PresetCollection*)&this->printers }) {
|
||||||
if (preset.is_default || preset.is_external)
|
for (const Preset &preset : (*presets)()) {
|
||||||
|
if (preset.is_default || preset.is_external || (preset.is_system && ! export_system_settings))
|
||||||
// Only export the common presets, not external files or the default preset.
|
// Only export the common presets, not external files or the default preset.
|
||||||
continue;
|
continue;
|
||||||
c << std::endl << "[" << presets.name() << ":" << preset.name << "]" << std::endl;
|
c << std::endl << "[" << presets->name() << ":" << preset.name << "]" << std::endl;
|
||||||
for (const std::string &opt_key : preset.config.keys())
|
for (const std::string &opt_key : preset.config.keys())
|
||||||
c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl;
|
c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ public:
|
|||||||
size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE);
|
size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE);
|
||||||
|
|
||||||
// Export a config bundle file containing all the presets and the names of the active presets.
|
// Export a config bundle file containing all the presets and the names of the active presets.
|
||||||
void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings);
|
void export_configbundle(const std::string &path, bool export_system_settings = false);
|
||||||
|
|
||||||
// Update a filament selection combo box on the platter for an idx_extruder.
|
// Update a filament selection combo box on the platter for an idx_extruder.
|
||||||
void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui);
|
void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui);
|
||||||
|
49
src/slic3r/GUI/PrintHostDialogs.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
#include <wx/frame.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
#include <wx/progdlg.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/checkbox.h>
|
||||||
|
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||||
|
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||||
|
, txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring()))
|
||||||
|
, box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
|
||||||
|
{
|
||||||
|
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
|
||||||
|
label_dir_hint->Wrap(CONTENT_WIDTH);
|
||||||
|
|
||||||
|
content_sizer->Add(txt_filename, 0, wxEXPAND);
|
||||||
|
content_sizer->Add(label_dir_hint);
|
||||||
|
content_sizer->AddSpacer(VERT_SPACING);
|
||||||
|
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
|
||||||
|
|
||||||
|
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
|
||||||
|
|
||||||
|
txt_filename->SetFocus();
|
||||||
|
wxString stem(path.stem().wstring());
|
||||||
|
txt_filename->SetSelection(0, stem.Length());
|
||||||
|
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path PrintHostSendDialog::filename() const
|
||||||
|
{
|
||||||
|
return fs::path(txt_filename->GetValue().wx_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrintHostSendDialog::start_print() const
|
||||||
|
{
|
||||||
|
return box_print->GetValue(); }
|
||||||
|
}
|
@ -20,19 +20,30 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
PrintHostSendDialog(const boost::filesystem::path &path);
|
||||||
|
boost::filesystem::path filename() const;
|
||||||
|
bool start_print() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxTextCtrl *txt_filename;
|
wxTextCtrl *txt_filename;
|
||||||
wxCheckBox *box_print;
|
wxCheckBox *box_print;
|
||||||
bool can_start_print;
|
bool can_start_print;
|
||||||
|
|
||||||
public:
|
|
||||||
PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
|
|
||||||
boost::filesystem::path filename() const;
|
|
||||||
bool print() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PrintHostQueueDialog : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrintHostQueueDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
|
|||||||
auto panel = this;
|
auto panel = this;
|
||||||
#endif
|
#endif
|
||||||
PageShp page(new Page(panel, title, icon_idx));
|
PageShp page(new Page(panel, title, icon_idx));
|
||||||
page->SetScrollbars(1, 1, 1, 1);
|
page->SetScrollbars(1, 1, 1, 2);
|
||||||
page->Hide();
|
page->Hide();
|
||||||
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
|
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
|
||||||
|
|
||||||
@ -1883,6 +1883,7 @@ void TabPrinter::build_sla()
|
|||||||
line.append_option(option);
|
line.append_option(option);
|
||||||
line.append_option(optgroup->get_option("display_pixels_y"));
|
line.append_option(optgroup->get_option("display_pixels_y"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
optgroup->append_single_option_line("display_orientation");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("Corrections")));
|
optgroup = page->new_optgroup(_(L("Corrections")));
|
||||||
line = Line{ m_config->def()->get("printer_correction")->full_label, "" };
|
line = Line{ m_config->def()->get("printer_correction")->full_label, "" };
|
||||||
|
@ -454,9 +454,10 @@ PrusaObjectDataViewModel::~PrusaObjectDataViewModel()
|
|||||||
m_bitmap_cache = nullptr;
|
m_bitmap_cache = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name)
|
wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int extruder)
|
||||||
{
|
{
|
||||||
auto root = new PrusaObjectDataViewModelNode(name);
|
const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
|
||||||
|
auto root = new PrusaObjectDataViewModelNode(name, extruder_str);
|
||||||
m_objects.push_back(root);
|
m_objects.push_back(root);
|
||||||
// notify control
|
// notify control
|
||||||
wxDataViewItem child((void*)root);
|
wxDataViewItem child((void*)root);
|
||||||
@ -890,7 +891,7 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int in
|
|||||||
return wxDataViewItem(0);
|
return wxDataViewItem(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item)
|
int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const
|
||||||
{
|
{
|
||||||
wxASSERT(item.IsOk());
|
wxASSERT(item.IsOk());
|
||||||
|
|
||||||
@ -912,6 +913,11 @@ int PrusaObjectDataViewModel::GetIdByItemAndType(const wxDataViewItem& item, con
|
|||||||
return node->GetIdx();
|
return node->GetIdx();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PrusaObjectDataViewModel::GetObjectIdByItem(const wxDataViewItem& item) const
|
||||||
|
{
|
||||||
|
return GetIdByItem(GetTopParent(item));
|
||||||
|
}
|
||||||
|
|
||||||
int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const
|
int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) const
|
||||||
{
|
{
|
||||||
return GetIdByItemAndType(item, itVolume);
|
return GetIdByItemAndType(item, itVolume);
|
||||||
@ -1275,6 +1281,52 @@ wxSize PrusaBitmapTextRenderer::GetSize() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value)
|
||||||
|
{
|
||||||
|
wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner();
|
||||||
|
PrusaObjectDataViewModel* const model = dynamic_cast<PrusaObjectDataViewModel*>(dv_ctrl->GetModel());
|
||||||
|
|
||||||
|
if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
PrusaDataViewBitmapText data;
|
||||||
|
data << value;
|
||||||
|
m_bmp_from_editing_item = data.GetBitmap();
|
||||||
|
m_was_unusable_symbol = false;
|
||||||
|
|
||||||
|
wxPoint position = labelRect.GetPosition();
|
||||||
|
if (m_bmp_from_editing_item.IsOk()) {
|
||||||
|
const int bmp_width = m_bmp_from_editing_item.GetWidth();
|
||||||
|
position.x += bmp_width;
|
||||||
|
labelRect.SetWidth(labelRect.GetWidth() - bmp_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(),
|
||||||
|
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
||||||
|
text_editor->SetInsertionPointEnd();
|
||||||
|
|
||||||
|
return text_editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrusaBitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
||||||
|
{
|
||||||
|
wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl);
|
||||||
|
if (!text_editor || text_editor->GetValue().IsEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8());
|
||||||
|
const char* unusable_symbols = "<>:/\\|?*\"";
|
||||||
|
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
|
||||||
|
if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
|
||||||
|
m_was_unusable_symbol = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value << PrusaDataViewBitmapText(text_editor->GetValue(), m_bmp_from_editing_item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// PrusaDoubleSlider
|
// PrusaDoubleSlider
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -222,7 +222,8 @@ class PrusaObjectDataViewModelNode
|
|||||||
size_t m_volumes_cnt = 0;
|
size_t m_volumes_cnt = 0;
|
||||||
std::vector< std::string > m_opt_categories;
|
std::vector< std::string > m_opt_categories;
|
||||||
public:
|
public:
|
||||||
PrusaObjectDataViewModelNode(const wxString &name) {
|
PrusaObjectDataViewModelNode(const wxString &name,
|
||||||
|
const wxString& extruder) {
|
||||||
m_parent = NULL;
|
m_parent = NULL;
|
||||||
m_name = name;
|
m_name = name;
|
||||||
m_type = itObject;
|
m_type = itObject;
|
||||||
@ -232,6 +233,7 @@ public:
|
|||||||
// it will be produce "segmentation fault"
|
// it will be produce "segmentation fault"
|
||||||
m_container = true;
|
m_container = true;
|
||||||
#endif //__WXGTK__
|
#endif //__WXGTK__
|
||||||
|
m_extruder = extruder;
|
||||||
set_object_action_icon();
|
set_object_action_icon();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +440,7 @@ public:
|
|||||||
PrusaObjectDataViewModel();
|
PrusaObjectDataViewModel();
|
||||||
~PrusaObjectDataViewModel();
|
~PrusaObjectDataViewModel();
|
||||||
|
|
||||||
wxDataViewItem Add(const wxString &name);
|
wxDataViewItem Add(const wxString &name, const int extruder);
|
||||||
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
|
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
|
||||||
const wxString &name,
|
const wxString &name,
|
||||||
const int volume_type,
|
const int volume_type,
|
||||||
@ -455,8 +457,9 @@ public:
|
|||||||
wxDataViewItem GetItemById(int obj_idx);
|
wxDataViewItem GetItemById(int obj_idx);
|
||||||
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
|
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
|
||||||
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
|
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
|
||||||
int GetIdByItem(const wxDataViewItem& item);
|
int GetIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
|
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
|
||||||
|
int GetObjectIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
||||||
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
||||||
@ -519,7 +522,7 @@ public:
|
|||||||
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT,
|
PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE,
|
||||||
int align = wxDVR_DEFAULT_ALIGNMENT):
|
int align = wxDVR_DEFAULT_ALIGNMENT):
|
||||||
wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {}
|
wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {}
|
||||||
|
|
||||||
@ -529,10 +532,18 @@ public:
|
|||||||
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
||||||
virtual wxSize GetSize() const;
|
virtual wxSize GetSize() const;
|
||||||
|
|
||||||
virtual bool HasEditorCtrl() const { return false; }
|
bool HasEditorCtrl() const override { return true; }
|
||||||
|
wxWindow* CreateEditorCtrl(wxWindow* parent,
|
||||||
|
wxRect labelRect,
|
||||||
|
const wxVariant& value) override;
|
||||||
|
bool GetValueFromEditorCtrl( wxWindow* ctrl,
|
||||||
|
wxVariant& value) override;
|
||||||
|
bool WasCanceled() const { return m_was_unusable_symbol; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PrusaDataViewBitmapText m_value;
|
PrusaDataViewBitmapText m_value;
|
||||||
|
wxBitmap m_bmp_from_editing_item;
|
||||||
|
bool m_was_unusable_symbol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "Duet.hpp"
|
#include "Duet.hpp"
|
||||||
#include "PrintHostSendDialog.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
@ -21,6 +20,7 @@
|
|||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
|
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
@ -62,10 +62,10 @@ bool Duet::send_gcode(const std::string &filename) const
|
|||||||
const auto errortitle = _(L("Error while uploading to the Duet"));
|
const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||||
fs::path filepath(filename);
|
fs::path filepath(filename);
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename(), true);
|
PrintHostSendDialog send_dialog(filepath.filename());
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||||
|
|
||||||
const bool print = send_dialog.print();
|
const bool print = send_dialog.start_print();
|
||||||
const auto upload_filepath = send_dialog.filename();
|
const auto upload_filepath = send_dialog.filename();
|
||||||
const auto upload_filename = upload_filepath.filename();
|
const auto upload_filename = upload_filepath.filename();
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
const auto upload_parent_path = upload_filepath.parent_path();
|
||||||
@ -136,6 +136,11 @@ bool Duet::send_gcode(const std::string &filename) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Duet::upload(PrintHostUpload upload_data) const
|
||||||
|
{
|
||||||
|
throw "unimplemented";
|
||||||
|
}
|
||||||
|
|
||||||
bool Duet::has_auto_discovery() const
|
bool Duet::has_auto_discovery() const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -24,6 +24,7 @@ public:
|
|||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to duet, filename is expected to be in UTF-8
|
// Send gcode file to duet, filename is expected to be in UTF-8
|
||||||
bool send_gcode(const std::string &filename) const;
|
bool send_gcode(const std::string &filename) const;
|
||||||
|
bool upload(PrintHostUpload upload_data) const;
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
private:
|
private:
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include "OctoPrint.hpp"
|
#include "OctoPrint.hpp"
|
||||||
#include "PrintHostSendDialog.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
|
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
@ -66,10 +66,10 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
|||||||
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
|
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
|
||||||
fs::path filepath(filename);
|
fs::path filepath(filename);
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename(), true);
|
PrintHostSendDialog send_dialog(filepath.filename());
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||||
|
|
||||||
const bool print = send_dialog.print();
|
const bool print = send_dialog.start_print();
|
||||||
const auto upload_filepath = send_dialog.filename();
|
const auto upload_filepath = send_dialog.filename();
|
||||||
const auto upload_filename = upload_filepath.filename();
|
const auto upload_filename = upload_filepath.filename();
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
const auto upload_parent_path = upload_filepath.parent_path();
|
||||||
@ -101,7 +101,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
|||||||
auto http = Http::post(std::move(url));
|
auto http = Http::post(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", print ? "true" : "false")
|
http.form_add("print", print ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string())
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
.form_add_file("file", filename, upload_filename.string())
|
.form_add_file("file", filename, upload_filename.string())
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
.on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
@ -129,6 +129,11 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OctoPrint::upload(PrintHostUpload upload_data) const
|
||||||
|
{
|
||||||
|
throw "unimplemented";
|
||||||
|
}
|
||||||
|
|
||||||
bool OctoPrint::has_auto_discovery() const
|
bool OctoPrint::has_auto_discovery() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -24,6 +24,7 @@ public:
|
|||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
||||||
bool send_gcode(const std::string &filename) const;
|
bool send_gcode(const std::string &filename) const;
|
||||||
|
bool upload(PrintHostUpload upload_data) const;
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
private:
|
private:
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
#include "OctoPrint.hpp"
|
#include "OctoPrint.hpp"
|
||||||
#include "Duet.hpp"
|
#include "Duet.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
#include "libslic3r/Channel.hpp"
|
||||||
|
|
||||||
|
using boost::optional;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -20,4 +28,33 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct PrintHostJobQueue::priv
|
||||||
|
{
|
||||||
|
std::vector<PrintHostJob> jobs;
|
||||||
|
Channel<unsigned> channel;
|
||||||
|
|
||||||
|
std::thread bg_thread;
|
||||||
|
optional<PrintHostJob> bg_job;
|
||||||
|
};
|
||||||
|
|
||||||
|
PrintHostJobQueue::PrintHostJobQueue()
|
||||||
|
: p(new priv())
|
||||||
|
{
|
||||||
|
std::shared_ptr<priv> p2 = p;
|
||||||
|
p->bg_thread = std::thread([p2]() {
|
||||||
|
// Wait for commands on the channel:
|
||||||
|
auto cmd = p2->channel.pop();
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintHostJobQueue::~PrintHostJobQueue()
|
||||||
|
{
|
||||||
|
// TODO: stop the thread
|
||||||
|
// if (p && p->bg_thread.joinable()) {
|
||||||
|
// p->bg_thread.detach();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,24 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
|
|
||||||
|
|
||||||
|
struct PrintHostUpload
|
||||||
|
{
|
||||||
|
boost::filesystem::path source_path;
|
||||||
|
boost::filesystem::path upload_path;
|
||||||
|
bool start_print = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class PrintHost
|
class PrintHost
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -20,7 +30,8 @@ public:
|
|||||||
virtual wxString get_test_ok_msg () const = 0;
|
virtual wxString get_test_ok_msg () const = 0;
|
||||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||||
// Send gcode file to print host, filename is expected to be in UTF-8
|
// Send gcode file to print host, filename is expected to be in UTF-8
|
||||||
virtual bool send_gcode(const std::string &filename) const = 0;
|
virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload()
|
||||||
|
virtual bool upload(PrintHostUpload upload_data) const = 0;
|
||||||
virtual bool has_auto_discovery() const = 0;
|
virtual bool has_auto_discovery() const = 0;
|
||||||
virtual bool can_test() const = 0;
|
virtual bool can_test() const = 0;
|
||||||
|
|
||||||
@ -28,6 +39,51 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct PrintHostJob
|
||||||
|
{
|
||||||
|
PrintHostUpload upload_data;
|
||||||
|
std::unique_ptr<PrintHost> printhost;
|
||||||
|
|
||||||
|
PrintHostJob() {}
|
||||||
|
PrintHostJob(const PrintHostJob&) = delete;
|
||||||
|
PrintHostJob(PrintHostJob &&other)
|
||||||
|
: upload_data(std::move(other.upload_data))
|
||||||
|
, printhost(std::move(other.printhost))
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostJob(DynamicPrintConfig *config)
|
||||||
|
: printhost(PrintHost::get_print_host(config))
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostJob& operator=(const PrintHostJob&) = delete;
|
||||||
|
PrintHostJob& operator=(PrintHostJob &&other)
|
||||||
|
{
|
||||||
|
upload_data = std::move(other.upload_data);
|
||||||
|
printhost = std::move(other.printhost);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const { return !printhost; }
|
||||||
|
operator bool() const { return !!printhost; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PrintHostJobQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrintHostJobQueue();
|
||||||
|
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
||||||
|
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
||||||
|
~PrintHostJobQueue();
|
||||||
|
|
||||||
|
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
||||||
|
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct priv;
|
||||||
|
std::shared_ptr<priv> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
#include "PrintHostSendDialog.hpp"
|
|
||||||
|
|
||||||
#include <wx/frame.h>
|
|
||||||
#include <wx/event.h>
|
|
||||||
#include <wx/progdlg.h>
|
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/checkbox.h>
|
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print) :
|
|
||||||
MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE),
|
|
||||||
txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
|
|
||||||
box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))),
|
|
||||||
can_start_print(can_start_print)
|
|
||||||
{
|
|
||||||
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
|
|
||||||
label_dir_hint->Wrap(CONTENT_WIDTH);
|
|
||||||
|
|
||||||
content_sizer->Add(txt_filename, 0, wxEXPAND);
|
|
||||||
content_sizer->Add(label_dir_hint);
|
|
||||||
content_sizer->AddSpacer(VERT_SPACING);
|
|
||||||
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
|
|
||||||
|
|
||||||
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
|
|
||||||
|
|
||||||
txt_filename->SetFocus();
|
|
||||||
wxString stem(path.stem().wstring());
|
|
||||||
txt_filename->SetSelection(0, stem.Length());
|
|
||||||
|
|
||||||
box_print->Enable(can_start_print);
|
|
||||||
|
|
||||||
Fit();
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path PrintHostSendDialog::filename() const
|
|
||||||
{
|
|
||||||
return fs::path(txt_filename->GetValue().wx_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PrintHostSendDialog::print() const
|
|
||||||
{
|
|
||||||
return box_print->GetValue(); }
|
|
||||||
}
|
|
@ -79,7 +79,9 @@ my $cube = {
|
|||||||
my $m = Slic3r::TriangleMesh->new;
|
my $m = Slic3r::TriangleMesh->new;
|
||||||
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
||||||
$m->repair;
|
$m->repair;
|
||||||
my @z = (0,2,4,8,6,8,10,12,14,16,18,20);
|
# The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
|
||||||
|
# open intervals at the bottom end, closed at the top end.
|
||||||
|
my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20);
|
||||||
my $result = $m->slice(\@z);
|
my $result = $m->slice(\@z);
|
||||||
my $SCALING_FACTOR = 0.000001;
|
my $SCALING_FACTOR = 0.000001;
|
||||||
for my $i (0..$#z) {
|
for my $i (0..$#z) {
|
||||||
@ -105,7 +107,9 @@ my $cube = {
|
|||||||
# this second test also checks that performing a second slice on a mesh after
|
# this second test also checks that performing a second slice on a mesh after
|
||||||
# a transformation works properly (shared_vertices is correctly invalidated);
|
# a transformation works properly (shared_vertices is correctly invalidated);
|
||||||
# at Z = -10 we have a bottom horizontal surface
|
# at Z = -10 we have a bottom horizontal surface
|
||||||
my $slices = $m->slice([ -5, -10 ]);
|
# (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be
|
||||||
|
# open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice).
|
||||||
|
my $slices = $m->slice([ -5, -10+0.00001 ]);
|
||||||
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
|
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,11 +211,6 @@ ModelMaterial::attributes()
|
|||||||
void set_layer_height_ranges(t_layer_height_ranges ranges)
|
void set_layer_height_ranges(t_layer_height_ranges ranges)
|
||||||
%code%{ THIS->layer_height_ranges = ranges; %};
|
%code%{ THIS->layer_height_ranges = ranges; %};
|
||||||
|
|
||||||
std::vector<double> layer_height_profile()
|
|
||||||
%code%{ RETVAL = THIS->layer_height_profile_valid ? THIS->layer_height_profile : std::vector<double>(); %};
|
|
||||||
void set_layer_height_profile(std::vector<double> profile)
|
|
||||||
%code%{ THIS->layer_height_profile = profile; THIS->layer_height_profile_valid = true; %};
|
|
||||||
|
|
||||||
Ref<Vec3d> origin_translation()
|
Ref<Vec3d> origin_translation()
|
||||||
%code%{ RETVAL = &THIS->origin_translation; %};
|
%code%{ RETVAL = &THIS->origin_translation; %};
|
||||||
void set_origin_translation(Vec3d* point)
|
void set_origin_translation(Vec3d* point)
|
||||||
|
@ -49,8 +49,6 @@ _constant()
|
|||||||
Ref<StaticPrintConfig> config()
|
Ref<StaticPrintConfig> config()
|
||||||
%code%{ RETVAL = &THIS->config(); %};
|
%code%{ RETVAL = &THIS->config(); %};
|
||||||
Points copies();
|
Points copies();
|
||||||
t_layer_height_ranges layer_height_ranges()
|
|
||||||
%code%{ RETVAL = THIS->layer_height_ranges; %};
|
|
||||||
std::vector<double> layer_height_profile()
|
std::vector<double> layer_height_profile()
|
||||||
%code%{ RETVAL = THIS->layer_height_profile; %};
|
%code%{ RETVAL = THIS->layer_height_profile; %};
|
||||||
Clone<BoundingBox> bounding_box();
|
Clone<BoundingBox> bounding_box();
|
||||||
@ -58,9 +56,6 @@ _constant()
|
|||||||
Points _shifted_copies()
|
Points _shifted_copies()
|
||||||
%code%{ RETVAL = THIS->copies(); %};
|
%code%{ RETVAL = THIS->copies(); %};
|
||||||
|
|
||||||
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
|
|
||||||
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
|
|
||||||
|
|
||||||
size_t layer_count();
|
size_t layer_count();
|
||||||
Ref<Layer> get_layer(int idx);
|
Ref<Layer> get_layer(int idx);
|
||||||
|
|
||||||
|