Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
8d3d4b3ae7
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,16 +5,30 @@ const vec3 back_color_light = vec3(0.365, 0.365, 0.365);
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform bool transparent_background;
|
||||
uniform bool svg_source;
|
||||
|
||||
varying vec2 tex_coords;
|
||||
|
||||
void main()
|
||||
vec4 svg_color()
|
||||
{
|
||||
// takes foreground from texture
|
||||
vec4 fore_color = texture2D(texture, tex_coords);
|
||||
|
||||
// calculates radial gradient
|
||||
vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5)))));
|
||||
|
||||
vec4 fore_color = texture2D(texture, tex_coords);
|
||||
|
||||
// blends foreground with background
|
||||
gl_FragColor = vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0);
|
||||
return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0);
|
||||
}
|
||||
|
||||
vec4 non_svg_color()
|
||||
{
|
||||
// takes foreground from texture
|
||||
vec4 color = texture2D(texture, tex_coords);
|
||||
return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = svg_source ? svg_color() : non_svg_color();
|
||||
}
|
@ -165,6 +165,7 @@ add_library(libslic3r STATIC
|
||||
MinAreaBoundingBox.cpp
|
||||
miniz_extension.hpp
|
||||
miniz_extension.cpp
|
||||
SLA/SLACommon.hpp
|
||||
SLA/SLABoilerPlate.hpp
|
||||
SLA/SLABasePool.hpp
|
||||
SLA/SLABasePool.cpp
|
||||
|
@ -51,6 +51,16 @@ void PrintConfigDef::init_common_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) });
|
||||
|
||||
def = this->add("bed_custom_texture", coString);
|
||||
def->label = L("Bed custom texture");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("bed_custom_model", coString);
|
||||
def->label = L("Bed custom model");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("layer_height", coFloat);
|
||||
def->label = L("Layer height");
|
||||
def->category = L("Layers and Perimeters");
|
||||
|
@ -24,37 +24,51 @@ enum class PointsStatus {
|
||||
UserModified // User has done some edits.
|
||||
};
|
||||
|
||||
struct SupportPoint {
|
||||
struct SupportPoint
|
||||
{
|
||||
Vec3f pos;
|
||||
float head_front_radius;
|
||||
bool is_new_island;
|
||||
bool is_new_island;
|
||||
|
||||
SupportPoint() :
|
||||
pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {}
|
||||
SupportPoint()
|
||||
: pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false)
|
||||
{}
|
||||
|
||||
SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) :
|
||||
pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {}
|
||||
SupportPoint(float pos_x,
|
||||
float pos_y,
|
||||
float pos_z,
|
||||
float head_radius,
|
||||
bool new_island)
|
||||
: pos(pos_x, pos_y, pos_z)
|
||||
, head_front_radius(head_radius)
|
||||
, is_new_island(new_island)
|
||||
{}
|
||||
|
||||
SupportPoint(Vec3f position, float head_radius, bool new_island) :
|
||||
pos(position), head_front_radius(head_radius), is_new_island(new_island) {}
|
||||
SupportPoint(Vec3f position, float head_radius, bool new_island)
|
||||
: pos(position)
|
||||
, head_front_radius(head_radius)
|
||||
, is_new_island(new_island)
|
||||
{}
|
||||
|
||||
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) :
|
||||
pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4) != 0.f) {}
|
||||
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data)
|
||||
: pos(data(0), data(1), data(2))
|
||||
, head_front_radius(data(3))
|
||||
, is_new_island(data(4) != 0.f)
|
||||
{}
|
||||
|
||||
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
|
||||
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
|
||||
bool operator==(const SupportPoint &sp) const
|
||||
{
|
||||
return (pos == sp.pos) && head_front_radius == sp.head_front_radius &&
|
||||
is_new_island == sp.is_new_island;
|
||||
}
|
||||
bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); }
|
||||
|
||||
template<class Archive> void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); }
|
||||
template<class Archive> void serialize(Archive &ar)
|
||||
{
|
||||
ar(pos, head_front_radius, is_new_island);
|
||||
}
|
||||
};
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
/*struct EigenMesh3D {
|
||||
Eigen::MatrixXd V;
|
||||
Eigen::MatrixXi F;
|
||||
double ground_level = 0;
|
||||
};*/
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
class EigenMesh3D {
|
||||
@ -161,9 +175,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -83,6 +83,42 @@ const unsigned SupportConfig::max_bridges_on_pillar = 3;
|
||||
using Coordf = double;
|
||||
using Portion = std::tuple<double, double>;
|
||||
|
||||
// Set this to true to enable full parallelism in this module.
|
||||
// Only the well tested parts will be concurrent if this is set to false.
|
||||
const constexpr bool USE_FULL_CONCURRENCY = false;
|
||||
|
||||
template<bool> struct _ccr {};
|
||||
|
||||
template<> struct _ccr<true>
|
||||
{
|
||||
using Mutex = SpinMutex;
|
||||
|
||||
template<class It, class Fn>
|
||||
static inline void enumerate(It from, It to, Fn fn)
|
||||
{
|
||||
using TN = size_t;
|
||||
auto iN = to - from;
|
||||
TN N = iN < 0 ? 0 : TN(iN);
|
||||
|
||||
tbb::parallel_for(TN(0), N, [from, fn](TN n) { fn(*(from + n), n); });
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct _ccr<false>
|
||||
{
|
||||
struct Mutex { inline void lock() {} inline void unlock() {} };
|
||||
|
||||
template<class It, class Fn>
|
||||
static inline void enumerate(It from, It to, Fn fn)
|
||||
{
|
||||
for (auto it = from; it != to; ++it) fn(*it, it - from);
|
||||
}
|
||||
};
|
||||
|
||||
using ccr = _ccr<USE_FULL_CONCURRENCY>;
|
||||
using ccr_seq = _ccr<false>;
|
||||
using ccr_par = _ccr<true>;
|
||||
|
||||
inline Portion make_portion(double a, double b) {
|
||||
return std::make_tuple(a, b);
|
||||
}
|
||||
@ -677,6 +713,7 @@ struct Pad {
|
||||
}
|
||||
|
||||
tmesh.translate(0, 0, float(zlevel));
|
||||
tmesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
bool empty() const { return tmesh.facets_count() == 0; }
|
||||
@ -735,7 +772,10 @@ ClusteredPoints cluster(
|
||||
// The support pad is considered an auxiliary geometry and is not part of the
|
||||
// merged mesh. It can be retrieved using a dedicated method (pad())
|
||||
class SLASupportTree::Impl {
|
||||
std::map<unsigned, Head> m_heads;
|
||||
// For heads it is beneficial to use the same IDs as for the support points.
|
||||
std::vector<Head> m_heads;
|
||||
std::vector<size_t> m_head_indices;
|
||||
|
||||
std::vector<Pillar> m_pillars;
|
||||
std::vector<Junction> m_junctions;
|
||||
std::vector<Bridge> m_bridges;
|
||||
@ -743,8 +783,13 @@ class SLASupportTree::Impl {
|
||||
Controller m_ctl;
|
||||
|
||||
Pad m_pad;
|
||||
|
||||
using Mutex = ccr::Mutex;
|
||||
|
||||
mutable Mutex m_mutex;
|
||||
mutable TriangleMesh meshcache; mutable bool meshcache_valid = false;
|
||||
mutable double model_height = 0; // the full height of the model
|
||||
|
||||
public:
|
||||
double ground_level = 0;
|
||||
|
||||
@ -753,37 +798,49 @@ public:
|
||||
|
||||
const Controller& ctl() const { return m_ctl; }
|
||||
|
||||
template<class...Args> Head& add_head(unsigned id, Args&&... args) {
|
||||
auto el = m_heads.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(id),
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
el.first->second.id = id;
|
||||
template<class...Args> Head& add_head(unsigned id, Args&&... args)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
m_heads.emplace_back(std::forward<Args>(args)...);
|
||||
m_heads.back().id = id;
|
||||
|
||||
if (id >= m_head_indices.size()) m_head_indices.resize(id + 1);
|
||||
m_head_indices[id] = m_heads.size() - 1;
|
||||
|
||||
meshcache_valid = false;
|
||||
return el.first->second;
|
||||
return m_heads.back();
|
||||
}
|
||||
|
||||
template<class...Args> Pillar& add_pillar(unsigned headid, Args&&... args) {
|
||||
auto it = m_heads.find(headid);
|
||||
assert(it != m_heads.end());
|
||||
Head& head = it->second;
|
||||
template<class...Args> Pillar& add_pillar(unsigned headid, Args&&... args)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
|
||||
assert(headid < m_head_indices.size());
|
||||
Head &head = m_heads[m_head_indices[headid]];
|
||||
|
||||
m_pillars.emplace_back(head, std::forward<Args>(args)...);
|
||||
Pillar& pillar = m_pillars.back();
|
||||
pillar.id = long(m_pillars.size() - 1);
|
||||
head.pillar_id = pillar.id;
|
||||
pillar.start_junction_id = head.id;
|
||||
pillar.starts_from_head = true;
|
||||
|
||||
meshcache_valid = false;
|
||||
return m_pillars.back();
|
||||
}
|
||||
|
||||
void increment_bridges(const Pillar& pillar) {
|
||||
void increment_bridges(const Pillar& pillar)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
|
||||
|
||||
if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
|
||||
m_pillars[size_t(pillar.id)].bridges++;
|
||||
}
|
||||
|
||||
void increment_links(const Pillar& pillar) {
|
||||
void increment_links(const Pillar& pillar)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
|
||||
|
||||
if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
|
||||
@ -792,6 +849,7 @@ public:
|
||||
|
||||
template<class...Args> Pillar& add_pillar(Args&&...args)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
m_pillars.emplace_back(std::forward<Args>(args)...);
|
||||
Pillar& pillar = m_pillars.back();
|
||||
pillar.id = long(m_pillars.size() - 1);
|
||||
@ -800,113 +858,125 @@ public:
|
||||
return m_pillars.back();
|
||||
}
|
||||
|
||||
const Head& pillar_head(long pillar_id) const {
|
||||
const Head& pillar_head(long pillar_id) const
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(pillar_id >= 0 && pillar_id < long(m_pillars.size()));
|
||||
|
||||
const Pillar& p = m_pillars[size_t(pillar_id)];
|
||||
assert(p.starts_from_head && p.start_junction_id >= 0);
|
||||
auto it = m_heads.find(unsigned(p.start_junction_id));
|
||||
assert(it != m_heads.end());
|
||||
return it->second;
|
||||
assert(size_t(p.start_junction_id) < m_head_indices.size());
|
||||
|
||||
return m_heads[m_head_indices[p.start_junction_id]];
|
||||
}
|
||||
|
||||
const Pillar& head_pillar(unsigned headid) const {
|
||||
auto it = m_heads.find(headid);
|
||||
assert(it != m_heads.end());
|
||||
const Head& h = it->second;
|
||||
const Pillar& head_pillar(unsigned headid) const
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(headid < m_head_indices.size());
|
||||
|
||||
const Head& h = m_heads[m_head_indices[headid]];
|
||||
assert(h.pillar_id >= 0 && h.pillar_id < long(m_pillars.size()));
|
||||
return pillar(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)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
m_junctions.emplace_back(std::forward<Args>(args)...);
|
||||
m_junctions.back().id = long(m_junctions.size() - 1);
|
||||
meshcache_valid = false;
|
||||
return m_junctions.back();
|
||||
}
|
||||
|
||||
template<class...Args> const Bridge& add_bridge(Args&&... args) {
|
||||
template<class...Args> const Bridge& add_bridge(Args&&... args)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
m_bridges.emplace_back(std::forward<Args>(args)...);
|
||||
m_bridges.back().id = long(m_bridges.size() - 1);
|
||||
meshcache_valid = false;
|
||||
return m_bridges.back();
|
||||
}
|
||||
|
||||
template<class...Args>
|
||||
const CompactBridge& add_compact_bridge(Args&&...args) {
|
||||
template<class...Args> const CompactBridge& add_compact_bridge(Args&&...args)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
m_compact_bridges.emplace_back(std::forward<Args>(args)...);
|
||||
m_compact_bridges.back().id = long(m_compact_bridges.size() - 1);
|
||||
meshcache_valid = false;
|
||||
return m_compact_bridges.back();
|
||||
}
|
||||
|
||||
const std::map<unsigned, Head>& heads() const { return m_heads; }
|
||||
Head& head(unsigned idx) {
|
||||
Head &head(unsigned id)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(id < m_head_indices.size());
|
||||
|
||||
meshcache_valid = false;
|
||||
auto it = m_heads.find(idx);
|
||||
assert(it != m_heads.end());
|
||||
return it->second;
|
||||
}
|
||||
const std::vector<Pillar>& pillars() const { return m_pillars; }
|
||||
const std::vector<Bridge>& bridges() const { return m_bridges; }
|
||||
const std::vector<Junction>& junctions() const { return m_junctions; }
|
||||
const std::vector<CompactBridge>& compact_bridges() const {
|
||||
return m_compact_bridges;
|
||||
return m_heads[m_head_indices[id]];
|
||||
}
|
||||
|
||||
template<class T> inline const Pillar& pillar(T id) const {
|
||||
static_assert(std::is_integral<T>::value, "Invalid index type");
|
||||
inline size_t pillarcount() const {
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
return m_pillars.size();
|
||||
}
|
||||
|
||||
template<class T> inline IntegerOnly<T, const Pillar&> pillar(T id) const
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
assert(id >= 0 && size_t(id) < m_pillars.size() &&
|
||||
size_t(id) < std::numeric_limits<size_t>::max());
|
||||
|
||||
return m_pillars[size_t(id)];
|
||||
}
|
||||
|
||||
const Pad& create_pad(const TriangleMesh& object_supports,
|
||||
const ExPolygons& modelbase,
|
||||
const PoolConfig& cfg) {
|
||||
const Pad &create_pad(const TriangleMesh &object_supports,
|
||||
const ExPolygons & modelbase,
|
||||
const PoolConfig & cfg)
|
||||
{
|
||||
m_pad = Pad(object_supports, modelbase, ground_level, cfg);
|
||||
return m_pad;
|
||||
}
|
||||
|
||||
void remove_pad() {
|
||||
m_pad = Pad();
|
||||
}
|
||||
void remove_pad() { m_pad = Pad(); }
|
||||
|
||||
const Pad& pad() const { return m_pad; }
|
||||
|
||||
// WITHOUT THE PAD!!!
|
||||
const TriangleMesh& merged_mesh() const {
|
||||
if(meshcache_valid) return meshcache;
|
||||
const TriangleMesh &merged_mesh() const
|
||||
{
|
||||
if (meshcache_valid) return meshcache;
|
||||
|
||||
Contour3D merged;
|
||||
|
||||
for(auto& headel : heads()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
if(headel.second.is_valid())
|
||||
merged.merge(headel.second.mesh);
|
||||
for (auto &head : m_heads) {
|
||||
if (m_ctl.stopcondition()) break;
|
||||
if (head.is_valid()) merged.merge(head.mesh);
|
||||
}
|
||||
|
||||
for(auto& stick : pillars()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
for (auto &stick : m_pillars) {
|
||||
if (m_ctl.stopcondition()) break;
|
||||
merged.merge(stick.mesh);
|
||||
merged.merge(stick.base);
|
||||
}
|
||||
|
||||
for(auto& j : junctions()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
for (auto &j : m_junctions) {
|
||||
if (m_ctl.stopcondition()) break;
|
||||
merged.merge(j.mesh);
|
||||
}
|
||||
|
||||
for(auto& cb : compact_bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
for (auto &cb : m_compact_bridges) {
|
||||
if (m_ctl.stopcondition()) break;
|
||||
merged.merge(cb.mesh);
|
||||
}
|
||||
|
||||
for(auto& bs : bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
for (auto &bs : m_bridges) {
|
||||
if (m_ctl.stopcondition()) break;
|
||||
merged.merge(bs.mesh);
|
||||
}
|
||||
|
||||
if(m_ctl.stopcondition()) {
|
||||
if (m_ctl.stopcondition()) {
|
||||
// In case of failure we have to return an empty mesh
|
||||
meshcache = TriangleMesh();
|
||||
return meshcache;
|
||||
@ -918,42 +988,44 @@ public:
|
||||
// which will need this.
|
||||
if (!meshcache.empty()) meshcache.require_shared_vertices();
|
||||
|
||||
// TODO: Is this necessary?
|
||||
//meshcache.repair();
|
||||
|
||||
BoundingBoxf3&& bb = meshcache.bounding_box();
|
||||
model_height = bb.max(Z) - bb.min(Z);
|
||||
BoundingBoxf3 &&bb = meshcache.bounding_box();
|
||||
model_height = bb.max(Z) - bb.min(Z);
|
||||
|
||||
meshcache_valid = true;
|
||||
return meshcache;
|
||||
}
|
||||
|
||||
// WITH THE PAD
|
||||
double full_height() const {
|
||||
if(merged_mesh().empty() && !pad().empty())
|
||||
double full_height() const
|
||||
{
|
||||
if (merged_mesh().empty() && !pad().empty())
|
||||
return get_pad_fullheight(pad().cfg);
|
||||
|
||||
double h = mesh_height();
|
||||
if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
|
||||
if (!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
|
||||
return h;
|
||||
}
|
||||
|
||||
// WITHOUT THE PAD!!!
|
||||
double mesh_height() const {
|
||||
if(!meshcache_valid) merged_mesh();
|
||||
double mesh_height() const
|
||||
{
|
||||
if (!meshcache_valid) merged_mesh();
|
||||
return model_height;
|
||||
}
|
||||
|
||||
// Intended to be called after the generation is fully complete
|
||||
void clear_support_data() {
|
||||
void merge_and_cleanup()
|
||||
{
|
||||
merged_mesh(); // in case the mesh is not generated, it should be...
|
||||
m_heads.clear();
|
||||
m_pillars.clear();
|
||||
m_junctions.clear();
|
||||
m_bridges.clear();
|
||||
m_compact_bridges.clear();
|
||||
}
|
||||
|
||||
// Doing clear() does not garantee to release the memory.
|
||||
m_heads = {};
|
||||
m_head_indices = {};
|
||||
m_pillars = {};
|
||||
m_junctions = {};
|
||||
m_bridges = {};
|
||||
m_compact_bridges = {};
|
||||
}
|
||||
};
|
||||
|
||||
// This function returns the position of the centroid in the input 'clust'
|
||||
@ -1122,11 +1194,10 @@ class SLASupportTree::Algorithm {
|
||||
// Now a and b vectors are perpendicular to v and to each other.
|
||||
// Together they define the plane where we have to iterate with the
|
||||
// given angles in the 'phis' vector
|
||||
tbb::parallel_for(size_t(0), phis.size(),
|
||||
[&phis, &hits, &m, sd, r_pin, r_back, s, a, b, c]
|
||||
(size_t i)
|
||||
ccr_par::enumerate(phis.begin(), phis.end(),
|
||||
[&hits, &m, sd, r_pin, r_back, s, a, b, c]
|
||||
(double phi, size_t i)
|
||||
{
|
||||
double& phi = phis[i];
|
||||
double sinphi = std::sin(phi);
|
||||
double cosphi = std::cos(phi);
|
||||
|
||||
@ -1226,11 +1297,10 @@ class SLASupportTree::Algorithm {
|
||||
// Hit results
|
||||
std::array<HitResult, SAMPLES> hits;
|
||||
|
||||
tbb::parallel_for(size_t(0), phis.size(),
|
||||
[&m, &phis, a, b, sd, dir, r, s, ins_check, &hits]
|
||||
(size_t i)
|
||||
ccr_par::enumerate(phis.begin(), phis.end(),
|
||||
[&m, a, b, sd, dir, r, s, ins_check, &hits]
|
||||
(double phi, size_t i)
|
||||
{
|
||||
double& phi = phis[i];
|
||||
double sinphi = std::sin(phi);
|
||||
double cosphi = std::cos(phi);
|
||||
|
||||
@ -1458,7 +1528,7 @@ class SLASupportTree::Algorithm {
|
||||
|
||||
if(nearest_id >= 0) {
|
||||
auto nearpillarID = unsigned(nearest_id);
|
||||
if(nearpillarID < m_result.pillars().size()) {
|
||||
if(nearpillarID < m_result.pillarcount()) {
|
||||
if(!connect_to_nearpillar(head, nearpillarID)) {
|
||||
nearest_id = -1; // continue searching
|
||||
spindex.remove(ne); // without the current pillar
|
||||
@ -1647,11 +1717,17 @@ public:
|
||||
using libnest2d::opt::GeneticOptimizer;
|
||||
using libnest2d::opt::StopCriteria;
|
||||
|
||||
for(unsigned i = 0, fidx = 0; i < filtered_indices.size(); ++i)
|
||||
ccr::Mutex mutex;
|
||||
auto addfn = [&mutex](PtIndices &container, unsigned val) {
|
||||
std::lock_guard<ccr::Mutex> lk(mutex);
|
||||
container.emplace_back(val);
|
||||
};
|
||||
|
||||
ccr::enumerate(filtered_indices.begin(), filtered_indices.end(),
|
||||
[this, &nmls, addfn](unsigned fidx, size_t i)
|
||||
{
|
||||
m_thr();
|
||||
|
||||
fidx = filtered_indices[i];
|
||||
auto n = nmls.row(i);
|
||||
|
||||
// for all normals we generate the spherical coordinates and
|
||||
@ -1709,20 +1785,20 @@ public:
|
||||
|
||||
auto oresult = solver.optimize_max(
|
||||
[this, pin_r, w, hp](double plr, double azm)
|
||||
{
|
||||
auto n = Vec3d(std::cos(azm) * std::sin(plr),
|
||||
std::sin(azm) * std::sin(plr),
|
||||
std::cos(plr)).normalized();
|
||||
{
|
||||
auto n = Vec3d(std::cos(azm) * std::sin(plr),
|
||||
std::sin(azm) * std::sin(plr),
|
||||
std::cos(plr)).normalized();
|
||||
|
||||
double score = pinhead_mesh_intersect( hp, n, pin_r,
|
||||
m_cfg.head_back_radius_mm, w);
|
||||
double score = pinhead_mesh_intersect(
|
||||
hp, n, pin_r, m_cfg.head_back_radius_mm, w);
|
||||
|
||||
return score;
|
||||
},
|
||||
initvals(polar, azimuth), // start with what we have
|
||||
bound(3*PI/4, PI), // Must not exceed the tilt limit
|
||||
bound(-PI, PI) // azimuth can be a full search
|
||||
);
|
||||
return score;
|
||||
},
|
||||
initvals(polar, azimuth), // start with what we have
|
||||
bound(3*PI/4, PI), // Must not exceed the tilt limit
|
||||
bound(-PI, PI) // azimuth can be a full search
|
||||
);
|
||||
|
||||
if(oresult.score > w) {
|
||||
polar = std::get<0>(oresult.optimum);
|
||||
@ -1740,18 +1816,18 @@ public:
|
||||
if (t.distance() > w) {
|
||||
// Check distance from ground, we might have zero elevation.
|
||||
if (hp(Z) + w * nn(Z) < m_result.ground_level) {
|
||||
m_iheadless.emplace_back(fidx);
|
||||
addfn(m_iheadless, fidx);
|
||||
} else {
|
||||
// mark the point for needing a head.
|
||||
m_iheads.emplace_back(fidx);
|
||||
addfn(m_iheads, fidx);
|
||||
}
|
||||
} else if (polar >= 3 * PI / 4) {
|
||||
// Headless supports do not tilt like the headed ones
|
||||
// so the normal should point almost to the ground.
|
||||
m_iheadless.emplace_back(fidx);
|
||||
addfn(m_iheadless, fidx);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_thr();
|
||||
}
|
||||
@ -1939,11 +2015,17 @@ public:
|
||||
};
|
||||
|
||||
std::vector<unsigned> modelpillars;
|
||||
ccr::Mutex mutex;
|
||||
|
||||
// TODO: connect these to the ground pillars if possible
|
||||
for(auto item : m_iheads_onmodel) { m_thr();
|
||||
unsigned idx = item.first;
|
||||
EigenMesh3D::hit_result hit = item.second;
|
||||
ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(),
|
||||
[this, routedown, &modelpillars, &mutex]
|
||||
(const std::pair<unsigned, EigenMesh3D::hit_result> &el,
|
||||
size_t)
|
||||
{
|
||||
m_thr();
|
||||
unsigned idx = el.first;
|
||||
EigenMesh3D::hit_result hit = el.second;
|
||||
|
||||
auto& head = m_result.head(idx);
|
||||
Vec3d hjp = head.junction_point();
|
||||
@ -1952,7 +2034,7 @@ public:
|
||||
// Search nearby pillar
|
||||
// /////////////////////////////////////////////////////////////////
|
||||
|
||||
if(search_pillar_and_connect(head)) { head.transform(); continue; }
|
||||
if(search_pillar_and_connect(head)) { head.transform(); return; }
|
||||
|
||||
// /////////////////////////////////////////////////////////////////
|
||||
// Try straight path
|
||||
@ -1974,7 +2056,7 @@ public:
|
||||
}
|
||||
|
||||
if(std::isinf(tdown)) { // we heave found a route to the ground
|
||||
routedown(head, head.dir, d); continue;
|
||||
routedown(head, head.dir, d); return;
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////
|
||||
@ -2036,7 +2118,7 @@ public:
|
||||
}
|
||||
|
||||
if(std::isinf(tdown)) { // we heave found a route to the ground
|
||||
routedown(head, bridgedir, d); continue;
|
||||
routedown(head, bridgedir, d); return;
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////
|
||||
@ -2079,8 +2161,9 @@ public:
|
||||
pill.base = tailhead.mesh;
|
||||
|
||||
// Experimental: add the pillar to the index for cascading
|
||||
std::lock_guard<ccr::Mutex> lk(mutex);
|
||||
modelpillars.emplace_back(unsigned(pill.id));
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// We have failed to route this head.
|
||||
@ -2088,7 +2171,7 @@ public:
|
||||
<< "Failed to route model facing support point."
|
||||
<< " ID: " << idx;
|
||||
head.invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
for(auto pillid : modelpillars) {
|
||||
auto& pillar = m_result.pillar(pillid);
|
||||
@ -2176,7 +2259,7 @@ public:
|
||||
// Search for the pair amongst the remembered pairs
|
||||
if(pairs.find(hashval) != pairs.end()) continue;
|
||||
|
||||
const Pillar& neighborpillar = m_result.pillars()[re.second];
|
||||
const Pillar& neighborpillar = m_result.pillar(re.second);
|
||||
|
||||
// this neighbor is occupied, skip
|
||||
if(neighborpillar.links >= neighbors) continue;
|
||||
@ -2212,7 +2295,7 @@ public:
|
||||
// lonely pillars. One or even two additional pillar might get inserted
|
||||
// depending on the length of the lonely pillar.
|
||||
|
||||
size_t pillarcount = m_result.pillars().size();
|
||||
size_t pillarcount = m_result.pillarcount();
|
||||
|
||||
// Again, go through all pillars, this time in the whole support tree
|
||||
// not just the index.
|
||||
@ -2364,6 +2447,8 @@ public:
|
||||
m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
|
||||
}
|
||||
}
|
||||
|
||||
void merge_result() { m_result.merge_and_cleanup(); }
|
||||
};
|
||||
|
||||
bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
@ -2386,6 +2471,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
ROUTING_NONGROUND,
|
||||
CASCADE_PILLARS,
|
||||
HEADLESS,
|
||||
MERGE_RESULT,
|
||||
DONE,
|
||||
ABORT,
|
||||
NUM_STEPS
|
||||
@ -2413,6 +2499,8 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
|
||||
std::bind(&Algorithm::routing_headless, &alg),
|
||||
|
||||
std::bind(&Algorithm::merge_result, &alg),
|
||||
|
||||
[] () {
|
||||
// Done
|
||||
},
|
||||
@ -2427,7 +2515,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
if(cfg.ground_facing_only) {
|
||||
program[ROUTING_NONGROUND] = []() {
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Skipping model-facing supports as requested.";
|
||||
<< "Skipping model-facing supports as requested.";
|
||||
};
|
||||
program[HEADLESS] = []() {
|
||||
BOOST_LOG_TRIVIAL(info) << "Skipping headless stick generation as"
|
||||
@ -2446,6 +2534,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
"Routing supports to model surface",
|
||||
"Interconnecting pillars",
|
||||
"Processing small holes",
|
||||
"Merging support mesh",
|
||||
"Done",
|
||||
"Abort"
|
||||
};
|
||||
@ -2458,7 +2547,8 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
60,
|
||||
70,
|
||||
80,
|
||||
90,
|
||||
85,
|
||||
99,
|
||||
100,
|
||||
0
|
||||
};
|
||||
@ -2473,11 +2563,13 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
||||
case ROUTING_GROUND: pc = ROUTING_NONGROUND; break;
|
||||
case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break;
|
||||
case CASCADE_PILLARS: pc = HEADLESS; break;
|
||||
case HEADLESS: pc = DONE; break;
|
||||
case HEADLESS: pc = MERGE_RESULT; break;
|
||||
case MERGE_RESULT: pc = DONE; break;
|
||||
case DONE:
|
||||
case ABORT: break;
|
||||
default: ;
|
||||
}
|
||||
|
||||
ctl.statuscb(stepstate[pc], stepstr[pc]);
|
||||
};
|
||||
|
||||
@ -2504,44 +2596,40 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
|
||||
outmesh.merge(get_pad());
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
|
||||
std::vector<ExPolygons> SLASupportTree::slice(
|
||||
const std::vector<float> &heights, float cr) const
|
||||
{
|
||||
if(init_layerh < 0) init_layerh = layerh;
|
||||
auto& stree = get();
|
||||
const TriangleMesh &sup_mesh = m_impl->merged_mesh();
|
||||
const TriangleMesh &pad_mesh = get_pad();
|
||||
|
||||
const auto modelh = float(stree.full_height());
|
||||
auto gndlvl = float(this->m_impl->ground_level);
|
||||
const Pad& pad = m_impl->pad();
|
||||
if(!pad.empty()) gndlvl -= float(get_pad_elevation(pad.cfg));
|
||||
|
||||
std::vector<float> heights;
|
||||
heights.reserve(size_t(modelh/layerh) + 1);
|
||||
|
||||
for(float h = gndlvl + init_layerh; h < gndlvl + modelh; h += layerh) {
|
||||
heights.emplace_back(h);
|
||||
std::vector<ExPolygons> sup_slices;
|
||||
if (!sup_mesh.empty()) {
|
||||
TriangleMeshSlicer sup_slicer(&sup_mesh);
|
||||
sup_slicer.slice(heights, cr, &sup_slices, m_impl->ctl().cancelfn);
|
||||
}
|
||||
|
||||
TriangleMesh fullmesh = m_impl->merged_mesh();
|
||||
fullmesh.merge(get_pad());
|
||||
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
|
||||
TriangleMeshSlicer slicer(&fullmesh);
|
||||
std::vector<ExPolygons> ret;
|
||||
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
|
||||
auto bb = pad_mesh.bounding_box();
|
||||
auto maxzit = std::upper_bound(heights.begin(), heights.end(), bb.max.z());
|
||||
|
||||
return ret;
|
||||
}
|
||||
auto padgrid = reserve_vector<float>(heights.end() - maxzit);
|
||||
std::copy(heights.begin(), maxzit, std::back_inserter(padgrid));
|
||||
|
||||
std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
|
||||
float cr) const
|
||||
{
|
||||
TriangleMesh fullmesh = m_impl->merged_mesh();
|
||||
fullmesh.merge(get_pad());
|
||||
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
|
||||
TriangleMeshSlicer slicer(&fullmesh);
|
||||
std::vector<ExPolygons> ret;
|
||||
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
|
||||
std::vector<ExPolygons> pad_slices;
|
||||
if (!pad_mesh.empty()) {
|
||||
TriangleMeshSlicer pad_slicer(&pad_mesh);
|
||||
pad_slicer.slice(padgrid, cr, &pad_slices, m_impl->ctl().cancelfn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
size_t len = std::min(heights.size(), pad_slices.size());
|
||||
len = std::min(len, sup_slices.size());
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
std::copy(pad_slices[i].begin(), pad_slices[i].end(),
|
||||
std::back_inserter(sup_slices[i]));
|
||||
pad_slices[i] = {};
|
||||
}
|
||||
|
||||
return sup_slices;
|
||||
}
|
||||
|
||||
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
|
||||
@ -2568,16 +2656,6 @@ SLASupportTree::SLASupportTree(const std::vector<SupportPoint> &points,
|
||||
{
|
||||
m_impl->ground_level = emesh.ground_level() - cfg.object_elevation_mm;
|
||||
generate(points, emesh, cfg, ctl);
|
||||
m_impl->clear_support_data();
|
||||
}
|
||||
|
||||
SLASupportTree::SLASupportTree(const SLASupportTree &c):
|
||||
m_impl(new Impl(*c.m_impl)) {}
|
||||
|
||||
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
||||
{
|
||||
m_impl = make_unique<Impl>(*c.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SLASupportTree::~SLASupportTree() {}
|
||||
|
@ -172,8 +172,8 @@ public:
|
||||
const SupportConfig& cfg = {},
|
||||
const Controller& ctl = {});
|
||||
|
||||
SLASupportTree(const SLASupportTree&);
|
||||
SLASupportTree& operator=(const SLASupportTree&);
|
||||
SLASupportTree(const SLASupportTree&) = delete;
|
||||
SLASupportTree& operator=(const SLASupportTree&) = delete;
|
||||
|
||||
~SLASupportTree();
|
||||
|
||||
@ -183,9 +183,6 @@ public:
|
||||
|
||||
void merged_mesh_with_pad(TriangleMesh&) const;
|
||||
|
||||
/// Get the sliced 2d layers of the support geometry.
|
||||
std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
|
||||
|
||||
std::vector<ExPolygons> slice(const std::vector<float> &,
|
||||
float closing_radius) const;
|
||||
|
||||
|
@ -928,12 +928,14 @@ void SLAPrint::process()
|
||||
double d = ostepd * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
|
||||
double init = m_report_status.status();
|
||||
|
||||
ctl.statuscb = [this, d, init](unsigned st, const std::string&)
|
||||
ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg)
|
||||
{
|
||||
double current = init + st * d;
|
||||
if(std::round(m_report_status.status()) < std::round(current))
|
||||
m_report_status(*this, current,
|
||||
OBJ_STEP_LABELS(slaposSupportTree));
|
||||
OBJ_STEP_LABELS(slaposSupportTree),
|
||||
SlicingStatus::DEFAULT,
|
||||
logmsg);
|
||||
|
||||
};
|
||||
|
||||
@ -1914,11 +1916,17 @@ std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in)
|
||||
return final_path;
|
||||
}
|
||||
|
||||
void SLAPrint::StatusReporter::operator()(
|
||||
SLAPrint &p, double st, const std::string &msg, unsigned flags)
|
||||
void SLAPrint::StatusReporter::operator()(SLAPrint & p,
|
||||
double st,
|
||||
const std::string &msg,
|
||||
unsigned flags,
|
||||
const std::string &logmsg)
|
||||
{
|
||||
m_st = st;
|
||||
BOOST_LOG_TRIVIAL(info) << st << "% " << msg << log_memory_info();
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< st << "% " << msg << (logmsg.empty() ? "" : ": ") << logmsg
|
||||
<< log_memory_info();
|
||||
|
||||
p.set_status(int(std::round(st)), msg, flags);
|
||||
}
|
||||
|
||||
|
@ -448,15 +448,20 @@ private:
|
||||
// Estimated print time, material consumed.
|
||||
SLAPrintStatistics m_print_statistics;
|
||||
|
||||
class StatusReporter {
|
||||
class StatusReporter
|
||||
{
|
||||
double m_st = 0;
|
||||
|
||||
public:
|
||||
void operator() (SLAPrint& p, double st, const std::string& msg,
|
||||
unsigned flags = SlicingStatus::DEFAULT);
|
||||
void operator()(SLAPrint & p,
|
||||
double st,
|
||||
const std::string &msg,
|
||||
unsigned flags = SlicingStatus::DEFAULT,
|
||||
const std::string &logmsg = "");
|
||||
|
||||
double status() const { return m_st; }
|
||||
} m_report_status;
|
||||
|
||||
|
||||
friend SLAPrintObject;
|
||||
};
|
||||
|
||||
|
@ -32,13 +32,4 @@
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha7 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA7 1
|
||||
|
||||
// Printbed textures generated from svg files
|
||||
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
|
||||
|
||||
|
||||
#endif // _technologies_h_
|
||||
|
@ -18,10 +18,9 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
|
||||
#ifdef __APPLE__
|
||||
m_user_drawn_background = false;
|
||||
#endif /*__APPLE__*/
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); }));
|
||||
}
|
||||
void Bed_2D::repaint()
|
||||
|
||||
void Bed_2D::repaint(const std::vector<Vec2d>& shape)
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
auto cw = GetSize().GetWidth();
|
||||
@ -41,7 +40,7 @@ void Bed_2D::repaint()
|
||||
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
if (m_bed_shape.empty())
|
||||
if (shape.empty())
|
||||
return;
|
||||
|
||||
// reduce size to have some space around the drawn shape
|
||||
@ -52,10 +51,9 @@ void Bed_2D::repaint()
|
||||
auto ccenter = cbb.center();
|
||||
|
||||
// get bounding box of bed shape in G - code coordinates
|
||||
auto bed_shape = m_bed_shape;
|
||||
auto bed_polygon = Polygon::new_scale(m_bed_shape);
|
||||
auto bb = BoundingBoxf(m_bed_shape);
|
||||
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
|
||||
auto bed_polygon = Polygon::new_scale(shape);
|
||||
auto bb = BoundingBoxf(shape);
|
||||
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
|
||||
auto bw = bb.size()(0);
|
||||
auto bh = bb.size()(1);
|
||||
auto bcenter = bb.center();
|
||||
@ -73,8 +71,8 @@ void Bed_2D::repaint()
|
||||
// draw bed fill
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt: m_bed_shape)
|
||||
{
|
||||
for (auto pt : shape)
|
||||
{
|
||||
Point pt_pix = to_pixels(pt, ch);
|
||||
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
|
||||
}
|
||||
@ -155,13 +153,13 @@ void Bed_2D::repaint()
|
||||
|
||||
|
||||
// convert G - code coordinates into pixels
|
||||
Point Bed_2D::to_pixels(Vec2d point, int height)
|
||||
Point Bed_2D::to_pixels(const Vec2d& point, int height)
|
||||
{
|
||||
auto p = point * m_scale_factor + m_shift;
|
||||
return Point(p(0) + Border, height - p(1) + Border);
|
||||
}
|
||||
|
||||
void Bed_2D::set_pos(Vec2d pos)
|
||||
void Bed_2D::set_pos(const Vec2d& pos)
|
||||
{
|
||||
m_pos = pos;
|
||||
Refresh();
|
||||
|
@ -17,16 +17,13 @@ class Bed_2D : public wxPanel
|
||||
Vec2d m_shift = Vec2d::Zero();
|
||||
Vec2d m_pos = Vec2d::Zero();
|
||||
|
||||
Point to_pixels(Vec2d point, int height);
|
||||
void repaint();
|
||||
void set_pos(Vec2d pos);
|
||||
Point to_pixels(const Vec2d& point, int height);
|
||||
void set_pos(const Vec2d& pos);
|
||||
|
||||
public:
|
||||
Bed_2D(wxWindow* parent);
|
||||
~Bed_2D() {}
|
||||
|
||||
std::vector<Vec2d> m_bed_shape;
|
||||
explicit Bed_2D(wxWindow* parent);
|
||||
|
||||
void repaint(const std::vector<Vec2d>& shape);
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
static const float GROUND_Z = -0.02f;
|
||||
|
||||
@ -22,7 +23,6 @@ namespace GUI {
|
||||
|
||||
bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_vertices.clear();
|
||||
unsigned int v_size = 3 * (unsigned int)triangles.size();
|
||||
|
||||
@ -82,75 +82,12 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_vertices.clear();
|
||||
m_tex_coords.clear();
|
||||
|
||||
unsigned int v_size = 9 * (unsigned int)triangles.size();
|
||||
unsigned int t_size = 6 * (unsigned int)triangles.size();
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<float>(v_size, 0.0f);
|
||||
if (generate_tex_coords)
|
||||
m_tex_coords = std::vector<float>(t_size, 0.0f);
|
||||
|
||||
float min_x = unscale<float>(triangles[0].points[0](0));
|
||||
float min_y = unscale<float>(triangles[0].points[0](1));
|
||||
float max_x = min_x;
|
||||
float max_y = min_y;
|
||||
|
||||
unsigned int v_coord = 0;
|
||||
unsigned int t_coord = 0;
|
||||
for (const Polygon& t : triangles)
|
||||
{
|
||||
for (unsigned int v = 0; v < 3; ++v)
|
||||
{
|
||||
const Point& p = t.points[v];
|
||||
float x = unscale<float>(p(0));
|
||||
float y = unscale<float>(p(1));
|
||||
|
||||
m_vertices[v_coord++] = x;
|
||||
m_vertices[v_coord++] = y;
|
||||
m_vertices[v_coord++] = z;
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
m_tex_coords[t_coord++] = x;
|
||||
m_tex_coords[t_coord++] = y;
|
||||
|
||||
min_x = std::min(min_x, x);
|
||||
max_x = std::max(max_x, x);
|
||||
min_y = std::min(min_y, y);
|
||||
max_y = std::max(max_y, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
float size_x = max_x - min_x;
|
||||
float size_y = max_y - min_y;
|
||||
|
||||
if ((size_x != 0.0f) && (size_y != 0.0f))
|
||||
{
|
||||
float inv_size_x = 1.0f / size_x;
|
||||
float inv_size_y = -1.0f / size_y;
|
||||
for (unsigned int i = 0; i < m_tex_coords.size(); i += 2)
|
||||
{
|
||||
m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x;
|
||||
m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_vertices.clear();
|
||||
|
||||
unsigned int v_size = 2 * (unsigned int)lines.size();
|
||||
@ -174,37 +111,14 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
|
||||
v2.position[2] = z;
|
||||
++v_count;
|
||||
}
|
||||
#else
|
||||
m_vertices.clear();
|
||||
m_tex_coords.clear();
|
||||
|
||||
unsigned int size = 6 * (unsigned int)lines.size();
|
||||
if (size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<float>(size, 0.0f);
|
||||
|
||||
unsigned int coord = 0;
|
||||
for (const Line& l : lines)
|
||||
{
|
||||
m_vertices[coord++] = unscale<float>(l.a(0));
|
||||
m_vertices[coord++] = unscale<float>(l.a(1));
|
||||
m_vertices[coord++] = z;
|
||||
m_vertices[coord++] = unscale<float>(l.b(0));
|
||||
m_vertices[coord++] = unscale<float>(l.b(1));
|
||||
m_vertices[coord++] = z;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
const float* GeometryBuffer::get_vertices_data() const
|
||||
{
|
||||
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
const double Bed3D::Axes::Radius = 0.5;
|
||||
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
|
||||
@ -274,22 +188,41 @@ void Bed3D::Axes::render_axis(double length) const
|
||||
|
||||
Bed3D::Bed3D()
|
||||
: m_type(Custom)
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
, m_custom_texture("")
|
||||
, m_custom_model("")
|
||||
, m_requires_canvas_update(false)
|
||||
, m_vbo_id(0)
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
, m_scale_factor(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
bool Bed3D::set_shape(const Pointfs& shape)
|
||||
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
|
||||
{
|
||||
EType new_type = detect_type(shape);
|
||||
if (m_shape == shape && m_type == new_type)
|
||||
|
||||
// check that the passed custom texture filename is valid
|
||||
std::string cst_texture(custom_texture);
|
||||
if (!cst_texture.empty())
|
||||
{
|
||||
if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture))
|
||||
cst_texture = "";
|
||||
}
|
||||
|
||||
// check that the passed custom texture filename is valid
|
||||
std::string cst_model(custom_model);
|
||||
if (!cst_model.empty())
|
||||
{
|
||||
if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model))
|
||||
cst_model = "";
|
||||
}
|
||||
|
||||
if ((m_shape == shape) && (m_type == new_type) && (m_custom_texture == cst_texture) && (m_custom_model == cst_model))
|
||||
// No change, no need to update the UI.
|
||||
return false;
|
||||
|
||||
m_shape = shape;
|
||||
m_custom_texture = cst_texture;
|
||||
m_custom_model = cst_model;
|
||||
m_type = new_type;
|
||||
|
||||
calc_bounding_boxes();
|
||||
@ -307,9 +240,9 @@ bool Bed3D::set_shape(const Pointfs& shape)
|
||||
|
||||
m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
m_texture.reset();
|
||||
m_model.reset();
|
||||
|
||||
// Set the origin and size for painting of the coordinate system axes.
|
||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||
@ -329,11 +262,12 @@ Point Bed3D::point_projection(const Point& point) const
|
||||
return m_polygon.point_projection(point);
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
|
||||
void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
render_axes();
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
@ -354,51 +288,11 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
|
||||
default:
|
||||
case Custom:
|
||||
{
|
||||
render_custom();
|
||||
render_custom(canvas, theta > 90.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Bed3D::render(float theta, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
if (m_shape.empty())
|
||||
return;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
{
|
||||
render_prusa("mk2", theta);
|
||||
break;
|
||||
}
|
||||
case MK3:
|
||||
{
|
||||
render_prusa("mk3", theta);
|
||||
break;
|
||||
}
|
||||
case SL1:
|
||||
{
|
||||
render_prusa("sl1", theta);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case Custom:
|
||||
{
|
||||
render_custom();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
if (!m_shape.empty())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
void Bed3D::calc_bounding_boxes() const
|
||||
{
|
||||
@ -423,7 +317,7 @@ void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
Polygons triangles;
|
||||
poly.triangulate(&triangles);
|
||||
|
||||
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom))
|
||||
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true))
|
||||
printf("Unable to create bed triangles\n");
|
||||
}
|
||||
|
||||
@ -495,31 +389,76 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
|
||||
return type;
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom) const
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
std::string tex_path = resources_dir() + "/icons/bed/" + key;
|
||||
if (!m_shape.empty())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
std::string model_path = resources_dir() + "/models/" + key;
|
||||
void Bed3D::render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const
|
||||
{
|
||||
if (!bottom)
|
||||
render_model(m_custom_model.empty() ? resources_dir() + "/models/" + key + "_bed.stl" : m_custom_model);
|
||||
|
||||
// use higher resolution images if graphic card and opengl version allow
|
||||
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
|
||||
render_texture(m_custom_texture.empty() ? resources_dir() + "/icons/bed/" + key + ".svg" : m_custom_texture, bottom, canvas);
|
||||
}
|
||||
|
||||
std::string filename = tex_path + ".svg";
|
||||
void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const
|
||||
{
|
||||
if (filename.empty())
|
||||
{
|
||||
m_texture.reset();
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
|
||||
{
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
m_texture.reset();
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
|
||||
if (boost::algorithm::iends_with(filename, ".svg"))
|
||||
{
|
||||
render_custom();
|
||||
// use higher resolution images if graphic card and opengl version allow
|
||||
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
|
||||
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
|
||||
{
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8))
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (boost::algorithm::iends_with(filename, ".png"))
|
||||
{
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_temp_texture.load_from_file(filename, false, GLTexture::None, false))
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_texture.load_from_file(filename, true, GLTexture::MultiThreaded, true))
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -536,292 +475,161 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom
|
||||
}
|
||||
else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu())
|
||||
{
|
||||
if (canvas != nullptr)
|
||||
canvas->stop_keeping_dirty();
|
||||
|
||||
canvas.stop_keeping_dirty();
|
||||
m_requires_canvas_update = false;
|
||||
}
|
||||
|
||||
if (!bottom)
|
||||
if (m_triangles.get_vertices_count() > 0)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 7.5, -0.03);
|
||||
else if (key == "mk3")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 5.5, 2.43);
|
||||
else if (key == "sl1")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 0.0, -0.03);
|
||||
if (m_shader.get_shader_program_id() == 0)
|
||||
m_shader.init("printbed.vs", "printbed.fs");
|
||||
|
||||
m_model.center_around(offset);
|
||||
|
||||
// update extended bounding box
|
||||
calc_bounding_boxes();
|
||||
}
|
||||
|
||||
if (!m_model.get_filename().empty())
|
||||
if (m_shader.is_initialized())
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
m_model.render();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
m_shader.start_using();
|
||||
m_shader.set_uniform("transparent_background", bottom);
|
||||
m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
if (m_vbo_id == 0)
|
||||
{
|
||||
glsafe(::glGenBuffers(1, &m_vbo_id));
|
||||
if (m_vbo_id == 0)
|
||||
{
|
||||
glsafe(::glGenBuffers(1, &m_vbo_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
|
||||
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
unsigned int stride = m_triangles.get_vertex_data_size();
|
||||
|
||||
GLint position_id = m_shader.get_attrib_location("v_position");
|
||||
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
|
||||
|
||||
// show the temporary texture while no compressed data is available
|
||||
GLuint tex_id = (GLuint)m_temp_texture.get_id();
|
||||
if (tex_id == 0)
|
||||
tex_id = (GLuint)m_texture.get_id();
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
|
||||
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
|
||||
|
||||
if (position_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
|
||||
}
|
||||
if (tex_coords_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(tex_coords_id));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
|
||||
}
|
||||
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
|
||||
|
||||
if (tex_coords_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(tex_coords_id));
|
||||
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
render_prusa_shader(bottom);
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::render_prusa_shader(bool transparent) const
|
||||
{
|
||||
if (m_shader.get_shader_program_id() == 0)
|
||||
m_shader.init("printbed.vs", "printbed.fs");
|
||||
|
||||
if (m_shader.is_initialized())
|
||||
{
|
||||
m_shader.start_using();
|
||||
m_shader.set_uniform("transparent_background", transparent);
|
||||
|
||||
unsigned int stride = m_triangles.get_vertex_data_size();
|
||||
|
||||
GLint position_id = m_shader.get_attrib_location("v_position");
|
||||
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
|
||||
|
||||
// show the temporary texture while no compressed data is available
|
||||
GLuint tex_id = (GLuint)m_temp_texture.get_id();
|
||||
if (tex_id == 0)
|
||||
tex_id = (GLuint)m_texture.get_id();
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
|
||||
|
||||
if (position_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
|
||||
}
|
||||
if (tex_coords_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(tex_coords_id));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
|
||||
}
|
||||
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
|
||||
|
||||
if (tex_coords_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(tex_coords_id));
|
||||
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
m_shader.stop_using();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Bed3D::render_prusa(const std::string& key, float theta) const
|
||||
{
|
||||
std::string tex_path = resources_dir() + "/icons/bed/" + key;
|
||||
|
||||
// use higher resolution images if graphic card allows
|
||||
GLint max_tex_size;
|
||||
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
|
||||
|
||||
// temporary set to lowest resolution
|
||||
max_tex_size = 2048;
|
||||
|
||||
if (max_tex_size >= 8192)
|
||||
tex_path += "_8192";
|
||||
else if (max_tex_size >= 4096)
|
||||
tex_path += "_4096";
|
||||
|
||||
std::string model_path = resources_dir() + "/models/" + key;
|
||||
|
||||
// use anisotropic filter if graphic card allows
|
||||
GLfloat max_anisotropy = 0.0f;
|
||||
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
|
||||
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
|
||||
|
||||
std::string filename = tex_path + "_top.png";
|
||||
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_top_texture.load_from_file(filename, true, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_anisotropy > 0.0f)
|
||||
{
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()));
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
|
||||
m_shader.stop_using();
|
||||
}
|
||||
}
|
||||
|
||||
filename = tex_path + "_bottom.png";
|
||||
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_bottom_texture.load_from_file(filename, true, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_anisotropy > 0.0f)
|
||||
{
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()));
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (theta <= 90.0f)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 7.5, -0.03);
|
||||
else if (key == "mk3")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 5.5, 2.43);
|
||||
else if (key == "sl1")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 0.0, -0.03);
|
||||
|
||||
m_model.center_around(offset);
|
||||
}
|
||||
|
||||
if (!m_model.get_filename().empty())
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
m_model.render();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnable(GL_TEXTURE_2D));
|
||||
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
|
||||
if (theta > 90.0f)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
|
||||
glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()));
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
|
||||
if (theta > 90.0f)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_TEXTURE_2D));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
void Bed3D::render_custom() const
|
||||
void Bed3D::render_model(const std::string& filename) const
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_texture.reset();
|
||||
#else
|
||||
m_top_texture.reset();
|
||||
m_bottom_texture.reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
if (filename.empty())
|
||||
return;
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename))
|
||||
{
|
||||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
Vec3d shift = m_bounding_box.center();
|
||||
shift(2) = -0.03;
|
||||
m_model.set_offset(shift);
|
||||
|
||||
// update extended bounding box
|
||||
calc_bounding_boxes();
|
||||
}
|
||||
|
||||
if (!m_model.get_filename().empty())
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
|
||||
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
|
||||
#else
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
|
||||
// draw grid
|
||||
unsigned int gridlines_vcount = m_gridlines.get_vertices_count();
|
||||
|
||||
// we need depth test for grid, otherwise it would disappear when looking the object from below
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glLineWidth(3.0f * m_scale_factor));
|
||||
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
|
||||
#else
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()));
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
m_model.render();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
|
||||
{
|
||||
if (m_custom_texture.empty() && m_custom_model.empty())
|
||||
{
|
||||
render_default(bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bottom)
|
||||
render_model(m_custom_model);
|
||||
|
||||
render_texture(m_custom_texture, bottom, canvas);
|
||||
}
|
||||
|
||||
void Bed3D::render_default(bool bottom) const
|
||||
{
|
||||
m_texture.reset();
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
bool has_model = !m_model.get_filename().empty();
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
if (!has_model && !bottom)
|
||||
{
|
||||
// draw background
|
||||
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
|
||||
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
}
|
||||
|
||||
// draw grid
|
||||
glsafe(::glLineWidth(3.0f * m_scale_factor));
|
||||
if (has_model && !bottom)
|
||||
glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f));
|
||||
else
|
||||
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
|
||||
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::reset()
|
||||
{
|
||||
if (m_vbo_id > 0)
|
||||
@ -830,7 +638,6 @@ void Bed3D::reset()
|
||||
m_vbo_id = 0;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
@ -3,9 +3,7 @@
|
||||
|
||||
#include "GLTexture.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
#include "GLShader.hpp"
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
@ -17,7 +15,6 @@ class GLCanvas3D;
|
||||
|
||||
class GeometryBuffer
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
@ -31,27 +28,17 @@ class GeometryBuffer
|
||||
};
|
||||
|
||||
std::vector<Vertex> m_vertices;
|
||||
#else
|
||||
std::vector<float> m_vertices;
|
||||
std::vector<float> m_tex_coords;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
public:
|
||||
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
|
||||
bool set_from_lines(const Lines& lines, float z);
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
const float* get_vertices_data() const;
|
||||
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
|
||||
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
|
||||
size_t get_position_offset() const { return 0; }
|
||||
size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
|
||||
#else
|
||||
const float* get_vertices() const { return m_vertices.data(); }
|
||||
const float* get_tex_coords() const { return m_tex_coords.data(); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; }
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
};
|
||||
|
||||
class Bed3D
|
||||
@ -87,12 +74,13 @@ public:
|
||||
private:
|
||||
EType m_type;
|
||||
Pointfs m_shape;
|
||||
std::string m_custom_texture;
|
||||
std::string m_custom_model;
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable BoundingBoxf3 m_extended_bounding_box;
|
||||
Polygon m_polygon;
|
||||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
mutable GLTexture m_texture;
|
||||
// temporary texture shown until the main texture has still no levels compressed
|
||||
mutable GLTexture m_temp_texture;
|
||||
@ -100,10 +88,6 @@ private:
|
||||
mutable bool m_requires_canvas_update;
|
||||
mutable Shader m_shader;
|
||||
mutable unsigned int m_vbo_id;
|
||||
#else
|
||||
mutable GLTexture m_top_texture;
|
||||
mutable GLTexture m_bottom_texture;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
mutable GLBed m_model;
|
||||
Axes m_axes;
|
||||
|
||||
@ -111,9 +95,7 @@ private:
|
||||
|
||||
public:
|
||||
Bed3D();
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
~Bed3D() { reset(); }
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
EType get_type() const { return m_type; }
|
||||
|
||||
@ -122,34 +104,26 @@ public:
|
||||
|
||||
const Pointfs& get_shape() const { return m_shape; }
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
bool set_shape(const Pointfs& shape);
|
||||
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void render(GLCanvas3D* canvas, float theta, float scale_factor) const;
|
||||
#else
|
||||
void render(float theta, float scale_factor) const;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void render_axes() const;
|
||||
void render(GLCanvas3D& canvas, float theta, float scale_factor) const;
|
||||
|
||||
private:
|
||||
void calc_bounding_boxes() const;
|
||||
void calc_triangles(const ExPolygon& poly);
|
||||
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
EType detect_type(const Pointfs& shape) const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const;
|
||||
void render_prusa_shader(bool transparent) const;
|
||||
#else
|
||||
void render_prusa(const std::string& key, float theta) const;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void render_custom() const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void render_axes() const;
|
||||
void render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const;
|
||||
void render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const;
|
||||
void render_model(const std::string& filename) const;
|
||||
void render_custom(GLCanvas3D& canvas, bool bottom) const;
|
||||
void render_default(bool bottom) const;
|
||||
void reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
@ -1736,13 +1736,7 @@ bool GLBed::on_init_from_file(const std::string& filename)
|
||||
|
||||
m_filename = filename;
|
||||
|
||||
ModelObject* model_object = model.objects.front();
|
||||
model_object->center_around_origin();
|
||||
|
||||
TriangleMesh mesh = model.mesh();
|
||||
mesh.repair();
|
||||
|
||||
m_volume.indexed_vertex_array.load_mesh(mesh);
|
||||
m_volume.indexed_vertex_array.load_mesh(model.mesh());
|
||||
|
||||
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
|
||||
set_color(color, 4);
|
||||
|
@ -4,23 +4,25 @@
|
||||
#include <wx/numformatter.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tooltip.h>
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
||||
#include "boost/nowide/iostream.hpp"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
|
||||
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
{
|
||||
SetFont(wxGetApp().normal_font());
|
||||
m_panel = new BedShapePanel(this);
|
||||
m_panel->build_panel(default_pt);
|
||||
m_panel->build_panel(default_pt, custom_texture, custom_model);
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_panel, 1, wxEXPAND);
|
||||
@ -51,59 +53,69 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||
const std::string BedShapePanel::NONE = "None";
|
||||
const std::string BedShapePanel::EMPTY_STRING = "";
|
||||
|
||||
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
{
|
||||
m_shape = default_pt.values;
|
||||
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
|
||||
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
|
||||
|
||||
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
|
||||
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
|
||||
|
||||
// shape options
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
auto optgroup = init_shape_options_page(_(L("Rectangular")));
|
||||
ConfigOptionDef def;
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.label = L("Size");
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
Option option(def, "rect_size");
|
||||
optgroup->append_single_option_line(option);
|
||||
ConfigOptionDef def;
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.label = L("Size");
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
Option option(def, "rect_size");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.label = L("Origin");
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
option = Option(def, "rect_origin");
|
||||
optgroup->append_single_option_line(option);
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.label = L("Origin");
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
option = Option(def, "rect_origin");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = init_shape_options_page(_(L("Circular")));
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.sidetext = L("mm");
|
||||
def.label = L("Diameter");
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
option = Option(def, "diameter");
|
||||
optgroup->append_single_option_line(option);
|
||||
optgroup = init_shape_options_page(_(L("Circular")));
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.sidetext = L("mm");
|
||||
def.label = L("Diameter");
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
option = Option(def, "diameter");
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = init_shape_options_page(_(L("Custom")));
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
auto shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
|
||||
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
shape_sizer->Add(shape_btn, 1, wxEXPAND);
|
||||
optgroup = init_shape_options_page(_(L("Custom")));
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
|
||||
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
shape_sizer->Add(shape_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(shape_sizer, 1, wxEXPAND);
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(shape_sizer, 1, wxEXPAND);
|
||||
|
||||
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
load_stl();
|
||||
}));
|
||||
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
load_stl();
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
wxPanel* texture_panel = init_texture_panel();
|
||||
wxPanel* model_panel = init_model_panel();
|
||||
|
||||
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e)
|
||||
{
|
||||
@ -112,13 +124,17 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||
|
||||
// right pane with preview canvas
|
||||
m_canvas = new Bed_2D(this);
|
||||
m_canvas->m_bed_shape = default_pt->values;
|
||||
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); });
|
||||
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); });
|
||||
|
||||
// main sizer
|
||||
auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
|
||||
if (m_canvas)
|
||||
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
|
||||
wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
left_sizer->Add(sbsizer, 0, wxEXPAND);
|
||||
left_sizer->Add(texture_panel, 1, wxEXPAND);
|
||||
left_sizer->Add(model_panel, 1, wxEXPAND);
|
||||
|
||||
wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
|
||||
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10);
|
||||
|
||||
SetSizerAndFit(top_sizer);
|
||||
|
||||
@ -134,20 +150,155 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||
// Create a panel for a rectangular / circular / custom bed shape.
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
|
||||
{
|
||||
auto panel = new wxPanel(m_shape_options_book);
|
||||
ConfigOptionsGroupShp optgroup;
|
||||
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
|
||||
wxPanel* panel = new wxPanel(m_shape_options_book);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
m_optgroups.push_back(optgroup);
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
m_shape_options_book->AddPage(panel, title);
|
||||
m_optgroups.push_back(optgroup);
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
m_shape_options_book->AddPage(panel, title);
|
||||
|
||||
return optgroup;
|
||||
return optgroup;
|
||||
}
|
||||
|
||||
wxPanel* BedShapePanel::init_texture_panel()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Texture")));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
|
||||
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
load_sizer->Add(load_btn, 1, wxEXPAND);
|
||||
|
||||
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
|
||||
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
|
||||
|
||||
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
|
||||
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
remove_sizer->Add(remove_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(filename_sizer, 1, wxEXPAND);
|
||||
sizer->Add(load_sizer, 1, wxEXPAND);
|
||||
sizer->Add(remove_sizer, 1, wxEXPAND);
|
||||
|
||||
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
load_texture();
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
m_custom_texture = NONE;
|
||||
update_shape();
|
||||
}));
|
||||
|
||||
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
|
||||
{
|
||||
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
|
||||
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
|
||||
if (lbl != nullptr)
|
||||
{
|
||||
wxString tooltip_text = (m_custom_texture == NONE) ? _(L("")) : _(m_custom_texture);
|
||||
wxToolTip* tooltip = lbl->GetToolTip();
|
||||
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
|
||||
lbl->SetToolTip(tooltip_text);
|
||||
}
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
|
||||
{
|
||||
e.Enable(m_custom_texture != NONE);
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
wxPanel* BedShapePanel::init_model_panel()
|
||||
{
|
||||
wxPanel* panel = new wxPanel(this);
|
||||
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Model")));
|
||||
|
||||
optgroup->label_width = 10;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
|
||||
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
load_sizer->Add(load_btn, 1, wxEXPAND);
|
||||
|
||||
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
|
||||
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
|
||||
|
||||
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
|
||||
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
remove_sizer->Add(remove_btn, 1, wxEXPAND);
|
||||
|
||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(filename_sizer, 1, wxEXPAND);
|
||||
sizer->Add(load_sizer, 1, wxEXPAND);
|
||||
sizer->Add(remove_sizer, 1, wxEXPAND);
|
||||
|
||||
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
load_model();
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||
{
|
||||
m_custom_model = NONE;
|
||||
update_shape();
|
||||
}));
|
||||
|
||||
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
|
||||
{
|
||||
e.SetText(_(boost::filesystem::path(m_custom_model).filename().string()));
|
||||
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
|
||||
if (lbl != nullptr)
|
||||
{
|
||||
wxString tooltip_text = (m_custom_model == NONE) ? _(L("")) : _(m_custom_model);
|
||||
wxToolTip* tooltip = lbl->GetToolTip();
|
||||
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
|
||||
lbl->SetToolTip(tooltip_text);
|
||||
}
|
||||
}));
|
||||
|
||||
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
|
||||
{
|
||||
e.Enable(m_custom_model != NONE);
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
panel->SetSizerAndFit(optgroup->sizer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
// Called from the constructor.
|
||||
@ -155,20 +306,20 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& tit
|
||||
// Deduce the bed shape type(rect, circle, custom)
|
||||
// This routine shall be smart enough if the user messes up
|
||||
// with the list of points in the ini file directly.
|
||||
void BedShapePanel::set_shape(ConfigOptionPoints* points)
|
||||
void BedShapePanel::set_shape(const ConfigOptionPoints& points)
|
||||
{
|
||||
auto polygon = Polygon::new_scale(points->values);
|
||||
auto polygon = Polygon::new_scale(points.values);
|
||||
|
||||
// is this a rectangle ?
|
||||
if (points->size() == 4) {
|
||||
auto lines = polygon.lines();
|
||||
if (points.size() == 4) {
|
||||
auto lines = polygon.lines();
|
||||
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
|
||||
// okay, it's a rectangle
|
||||
// find origin
|
||||
coordf_t x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points->values[0](0);
|
||||
y_max = y_min = points->values[0](1);
|
||||
for (auto pt : points->values)
|
||||
x_max = x_min = points.values[0](0);
|
||||
y_max = y_min = points.values[0](1);
|
||||
for (auto pt : points.values)
|
||||
{
|
||||
x_min = std::min(x_min, pt(0));
|
||||
x_max = std::max(x_max, pt(0));
|
||||
@ -219,8 +370,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
|
||||
}
|
||||
}
|
||||
|
||||
if (points->size() < 3) {
|
||||
// Invalid polygon.Revert to default bed dimensions.
|
||||
if (points.size() < 3) {
|
||||
// Invalid polygon.Revert to default bed dimensions.
|
||||
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
|
||||
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
|
||||
optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
@ -232,7 +383,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_shape_options_book->SetSelection(SHAPE_CUSTOM);
|
||||
// Copy the polygon to the canvas, make a copy of the array.
|
||||
m_loaded_bed_shape = points->values;
|
||||
m_loaded_shape = points.values;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
@ -277,11 +428,11 @@ void BedShapePanel::update_shape()
|
||||
x1 -= dx;
|
||||
y0 -= dy;
|
||||
y1 -= dy;
|
||||
m_canvas->m_bed_shape = { Vec2d(x0, y0),
|
||||
Vec2d(x1, y0),
|
||||
Vec2d(x1, y1),
|
||||
Vec2d(x0, y1)};
|
||||
}
|
||||
m_shape = { Vec2d(x0, y0),
|
||||
Vec2d(x1, y0),
|
||||
Vec2d(x1, y1),
|
||||
Vec2d(x0, y1) };
|
||||
}
|
||||
else if(page_idx == SHAPE_CIRCULAR) {
|
||||
double diameter;
|
||||
try{
|
||||
@ -293,16 +444,16 @@ void BedShapePanel::update_shape()
|
||||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
auto edges = 60;
|
||||
std::vector<Vec2d> points;
|
||||
for (size_t i = 1; i <= 60; ++i) {
|
||||
auto angle = i * twopi / edges;
|
||||
auto edges = 72;
|
||||
std::vector<Vec2d> points;
|
||||
for (size_t i = 1; i <= edges; ++i) {
|
||||
auto angle = i * twopi / edges;
|
||||
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
|
||||
}
|
||||
m_canvas->m_bed_shape = points;
|
||||
}
|
||||
m_shape = points;
|
||||
}
|
||||
else if (page_idx == SHAPE_CUSTOM)
|
||||
m_canvas->m_bed_shape = m_loaded_bed_shape;
|
||||
m_shape = m_loaded_shape;
|
||||
|
||||
update_preview();
|
||||
}
|
||||
@ -310,27 +461,27 @@ void BedShapePanel::update_shape()
|
||||
// Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
void BedShapePanel::load_stl()
|
||||
{
|
||||
auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "",
|
||||
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dialog->ShowModal() != wxID_OK) {
|
||||
dialog->Destroy();
|
||||
return;
|
||||
}
|
||||
wxArrayString input_file;
|
||||
dialog->GetPaths(input_file);
|
||||
dialog->Destroy();
|
||||
wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
std::string file_name = input_file[0].ToUTF8().data();
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".stl"))
|
||||
{
|
||||
show_error(this, _(L("Invalid file format.")));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
Model model;
|
||||
try {
|
||||
model = Model::read_from_file(file_name);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
auto msg = _(L("Error!")) + " " + file_name + " : " + e.what() + ".";
|
||||
show_error(this, msg);
|
||||
exit(1);
|
||||
model = Model::read_from_file(file_name);
|
||||
}
|
||||
catch (std::exception &) {
|
||||
show_error(this, _(L("Error! Invalid model")));
|
||||
return;
|
||||
}
|
||||
|
||||
auto mesh = model.mesh();
|
||||
auto expolygons = mesh.horizontal_projection();
|
||||
@ -349,7 +500,53 @@ void BedShapePanel::load_stl()
|
||||
for (auto pt : polygon.points)
|
||||
points.push_back(unscale(pt));
|
||||
|
||||
m_loaded_bed_shape = points;
|
||||
m_loaded_shape = points;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
void BedShapePanel::load_texture()
|
||||
{
|
||||
wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "",
|
||||
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
m_custom_texture = NONE;
|
||||
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg"))
|
||||
{
|
||||
show_error(this, _(L("Invalid file format.")));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
m_custom_texture = file_name;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
void BedShapePanel::load_model()
|
||||
{
|
||||
wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "",
|
||||
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
m_custom_model = NONE;
|
||||
|
||||
std::string file_name = dialog.GetPath().ToUTF8().data();
|
||||
if (!boost::algorithm::iends_with(file_name, ".stl"))
|
||||
{
|
||||
show_error(this, _(L("Invalid file format.")));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
m_custom_model = file_name;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
|
@ -16,27 +16,40 @@ namespace GUI {
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
static const std::string NONE;
|
||||
static const std::string EMPTY_STRING;
|
||||
|
||||
Bed_2D* m_canvas;
|
||||
std::vector<Vec2d> m_loaded_bed_shape;
|
||||
std::vector<Vec2d> m_shape;
|
||||
std::vector<Vec2d> m_loaded_shape;
|
||||
std::string m_custom_texture;
|
||||
std::string m_custom_model;
|
||||
|
||||
public:
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
|
||||
~BedShapePanel() {}
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE), m_custom_model(NONE) {}
|
||||
|
||||
void build_panel(ConfigOptionPoints* default_pt);
|
||||
void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
|
||||
|
||||
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
const std::vector<Vec2d>& get_shape() const { return m_shape; }
|
||||
const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; }
|
||||
const std::string& get_custom_model() const { return (m_custom_model != NONE) ? m_custom_model : EMPTY_STRING; }
|
||||
|
||||
private:
|
||||
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
void update_preview();
|
||||
wxPanel* init_texture_panel();
|
||||
wxPanel* init_model_panel();
|
||||
void set_shape(const ConfigOptionPoints& points);
|
||||
void update_preview();
|
||||
void update_shape();
|
||||
void load_stl();
|
||||
|
||||
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
std::vector<Vec2d> GetValue() { return m_canvas->m_bed_shape; }
|
||||
void load_texture();
|
||||
void load_model();
|
||||
|
||||
wxChoicebook* m_shape_options_book;
|
||||
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
||||
|
||||
friend class BedShapeDialog;
|
||||
};
|
||||
|
||||
class BedShapeDialog : public DPIDialog
|
||||
@ -45,10 +58,12 @@ class BedShapeDialog : public DPIDialog
|
||||
public:
|
||||
BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")),
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
|
||||
~BedShapeDialog() {}
|
||||
|
||||
void build_dialog(ConfigOptionPoints* default_pt);
|
||||
std::vector<Vec2d> GetValue() { return m_panel->GetValue(); }
|
||||
void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
|
||||
|
||||
const std::vector<Vec2d>& get_shape() const { return m_panel->get_shape(); }
|
||||
const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); }
|
||||
const std::string& get_custom_model() const { return m_panel->get_custom_model(); }
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
@ -532,15 +532,21 @@ PageBedShape::PageBedShape(ConfigWizard *parent)
|
||||
{
|
||||
append_text(_(L("Set the shape of your printer's bed.")));
|
||||
|
||||
shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
|
||||
append(shape_panel);
|
||||
}
|
||||
|
||||
void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
const auto points(shape_panel->GetValue());
|
||||
auto *opt = new ConfigOptionPoints(points);
|
||||
config.set_key_value("bed_shape", opt);
|
||||
const std::vector<Vec2d>& points = shape_panel->get_shape();
|
||||
const std::string& custom_texture = shape_panel->get_custom_texture();
|
||||
const std::string& custom_model = shape_panel->get_custom_model();
|
||||
config.set_key_value("bed_shape", new ConfigOptionPoints(points));
|
||||
config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture));
|
||||
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
|
||||
}
|
||||
|
||||
PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||
@ -1086,7 +1092,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
|
||||
|
||||
p->load_vendors();
|
||||
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
|
||||
"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
|
||||
"gcode_flavor", "bed_shape", "bed_custom_texture", "bed_custom_model", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
|
||||
}));
|
||||
|
||||
p->index = new ConfigWizardIndex(this);
|
||||
|
@ -116,87 +116,6 @@ void Size::set_scale_factor(int scale_factor)
|
||||
m_scale_factor = scale_factor;
|
||||
}
|
||||
|
||||
#if !ENABLE_TEXTURES_FROM_SVG
|
||||
GLCanvas3D::Shader::Shader()
|
||||
: m_shader(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
GLCanvas3D::Shader::~Shader()
|
||||
{
|
||||
_reset();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
||||
{
|
||||
if (is_initialized())
|
||||
return true;
|
||||
|
||||
m_shader = new GLShader();
|
||||
if (m_shader != nullptr)
|
||||
{
|
||||
if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str()))
|
||||
{
|
||||
std::cout << "Compilaton of shader failed:" << std::endl;
|
||||
std::cout << m_shader->last_error << std::endl;
|
||||
_reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::Shader::is_initialized() const
|
||||
{
|
||||
return (m_shader != nullptr);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::Shader::start_using() const
|
||||
{
|
||||
if (is_initialized())
|
||||
{
|
||||
m_shader->enable();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Shader::stop_using() const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->disable();
|
||||
}
|
||||
|
||||
void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->set_uniform(name.c_str(), value);
|
||||
}
|
||||
|
||||
void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->set_uniform(name.c_str(), matrix);
|
||||
}
|
||||
|
||||
const GLShader* GLCanvas3D::Shader::get_shader() const
|
||||
{
|
||||
return m_shader;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Shader::_reset()
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
{
|
||||
m_shader->release();
|
||||
delete m_shader;
|
||||
m_shader = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // !ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
GLCanvas3D::LayersEditing::LayersEditing()
|
||||
: m_enabled(false)
|
||||
, m_z_texture_id(0)
|
||||
@ -383,7 +302,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
|
||||
if (m_tooltip_texture.get_id() == 0)
|
||||
{
|
||||
std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
|
||||
if (!m_tooltip_texture.load_from_file(filename, false, true))
|
||||
if (!m_tooltip_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -415,7 +334,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co
|
||||
if (m_reset_texture.get_id() == 0)
|
||||
{
|
||||
std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png";
|
||||
if (!m_reset_texture.load_from_file(filename, false, true))
|
||||
if (!m_reset_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1642,19 +1561,10 @@ void GLCanvas3D::render()
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
_render_background();
|
||||
|
||||
// textured bed needs to be rendered after objects if the texture is transparent
|
||||
bool early_bed_render = m_bed.is_custom() || (theta <= 90.0f);
|
||||
if (early_bed_render)
|
||||
_render_bed(theta);
|
||||
|
||||
_render_objects();
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
|
||||
_render_axes();
|
||||
|
||||
if (!early_bed_render)
|
||||
_render_bed(theta);
|
||||
_render_bed(theta);
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
_render_selection_center();
|
||||
@ -3986,16 +3896,7 @@ void GLCanvas3D::_render_bed(float theta) const
|
||||
#if ENABLE_RETINA_GL
|
||||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif // ENABLE_RETINA_GL
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_bed.render(const_cast<GLCanvas3D*>(this), theta, scale_factor);
|
||||
#else
|
||||
m_bed.render(theta, scale_factor);
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_axes() const
|
||||
{
|
||||
m_bed.render_axes();
|
||||
m_bed.render(const_cast<GLCanvas3D&>(*this), theta, scale_factor);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_objects() const
|
||||
|
@ -158,32 +158,6 @@ class GLCanvas3D
|
||||
void reset() { first_volumes.clear(); }
|
||||
};
|
||||
|
||||
#if !ENABLE_TEXTURES_FROM_SVG
|
||||
class Shader
|
||||
{
|
||||
GLShader* m_shader;
|
||||
|
||||
public:
|
||||
Shader();
|
||||
~Shader();
|
||||
|
||||
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
|
||||
|
||||
bool is_initialized() const;
|
||||
|
||||
bool start_using() const;
|
||||
void stop_using() const;
|
||||
|
||||
void set_uniform(const std::string& name, float value) const;
|
||||
void set_uniform(const std::string& name, const float* matrix) const;
|
||||
|
||||
const GLShader* get_shader() const;
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
};
|
||||
#endif // !ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
class LayersEditing
|
||||
{
|
||||
public:
|
||||
@ -688,7 +662,6 @@ private:
|
||||
void _rectangular_selection_picking_pass() const;
|
||||
void _render_background() const;
|
||||
void _render_bed(float theta) const;
|
||||
void _render_axes() const;
|
||||
void _render_objects() const;
|
||||
void _render_selection() const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
|
@ -268,7 +268,6 @@ sub SetMatrix
|
||||
}
|
||||
*/
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
Shader::Shader()
|
||||
: m_shader(nullptr)
|
||||
{
|
||||
@ -363,6 +362,5 @@ void Shader::reset()
|
||||
m_shader = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -37,7 +37,6 @@ public:
|
||||
std::string last_error;
|
||||
};
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
class Shader
|
||||
{
|
||||
GLShader* m_shader;
|
||||
@ -66,7 +65,6 @@ public:
|
||||
private:
|
||||
void reset();
|
||||
};
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ GLTexture::~GLTexture()
|
||||
reset();
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bool compress)
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
reset();
|
||||
|
||||
@ -142,7 +142,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bo
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".png"))
|
||||
return load_from_png(filename, use_mipmaps, compress);
|
||||
return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@ -354,49 +354,10 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
unsigned int GLTexture::generate_mipmaps(wxImage& image, bool compress)
|
||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
int w = image.GetWidth();
|
||||
int h = image.GetHeight();
|
||||
GLint level = 0;
|
||||
std::vector<unsigned char> data(w * h * 4, 0);
|
||||
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
while ((w > 1) || (h > 1))
|
||||
{
|
||||
++level;
|
||||
|
||||
w = std::max(w / 2, 1);
|
||||
h = std::max(h / 2, 1);
|
||||
|
||||
int n_pixels = w * h;
|
||||
|
||||
image = image.ResampleBicubic(w, h);
|
||||
|
||||
unsigned char* img_rgb = image.GetData();
|
||||
unsigned char* img_alpha = image.GetAlpha();
|
||||
|
||||
data.resize(n_pixels * 4);
|
||||
for (int i = 0; i < n_pixels; ++i)
|
||||
{
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
return (unsigned int)level;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, bool compress)
|
||||
{
|
||||
// Load a PNG with an alpha channel.
|
||||
wxImage image;
|
||||
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
|
||||
@ -407,8 +368,32 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo
|
||||
|
||||
m_width = image.GetWidth();
|
||||
m_height = image.GetHeight();
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
bool requires_rescale = false;
|
||||
|
||||
if (compression_enabled && (compression_type == MultiThreaded))
|
||||
{
|
||||
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
|
||||
int width_rem = m_width % 4;
|
||||
int height_rem = m_height % 4;
|
||||
|
||||
if (width_rem != 0)
|
||||
{
|
||||
m_width += (4 - width_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
|
||||
if (height_rem != 0)
|
||||
{
|
||||
m_height += (4 - height_rem);
|
||||
requires_rescale = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (requires_rescale)
|
||||
image = image.ResampleBicubic(m_width, m_height);
|
||||
|
||||
int n_pixels = m_width * m_height;
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
reset();
|
||||
@ -440,28 +425,99 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (apply_anisotropy)
|
||||
{
|
||||
GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy();
|
||||
if (max_anisotropy > 1.0f)
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
}
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
{
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
|
||||
if (use_mipmaps)
|
||||
{
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
unsigned int levels_count = generate_mipmaps(image, compress);
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
// we do not need to generate all levels down to 1x1
|
||||
while ((lod_w > 16) || (lod_h > 16))
|
||||
{
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
lod_h = std::max(lod_h / 2, 1);
|
||||
n_pixels = lod_w * lod_h;
|
||||
|
||||
image = image.ResampleBicubic(lod_w, lod_h);
|
||||
|
||||
data.resize(n_pixels * 4);
|
||||
|
||||
img_rgb = image.GetData();
|
||||
img_alpha = image.GetAlpha();
|
||||
|
||||
for (int i = 0; i < n_pixels; ++i)
|
||||
{
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
if (compression_enabled)
|
||||
{
|
||||
if (compression_type == SingleThreaded)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
{
|
||||
// initializes the texture on GPU
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||
// and send the uncompressed data to the compressor
|
||||
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
}
|
||||
|
||||
if (!compression_enabled)
|
||||
{
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
m_source = filename;
|
||||
|
||||
if (compression_enabled && (compression_type == MultiThreaded))
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -472,7 +528,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr)
|
||||
{
|
||||
// printf("Could not open SVG image.\n");
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
@ -507,7 +562,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr)
|
||||
{
|
||||
// printf("Could not init rasterizer.\n");
|
||||
nsvgDelete(image);
|
||||
reset();
|
||||
return false;
|
||||
@ -579,6 +633,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
}
|
||||
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
@ -48,6 +48,13 @@ namespace GUI {
|
||||
};
|
||||
|
||||
public:
|
||||
enum ECompressionType : unsigned char
|
||||
{
|
||||
None,
|
||||
SingleThreaded,
|
||||
MultiThreaded
|
||||
};
|
||||
|
||||
struct UV
|
||||
{
|
||||
float u;
|
||||
@ -75,7 +82,7 @@ namespace GUI {
|
||||
GLTexture();
|
||||
virtual ~GLTexture();
|
||||
|
||||
bool load_from_file(const std::string& filename, bool use_mipmaps, bool compress);
|
||||
bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
// meanings of states: (std::pair<int, bool>)
|
||||
// first field (int):
|
||||
@ -101,11 +108,8 @@ namespace GUI {
|
||||
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
|
||||
protected:
|
||||
unsigned int generate_mipmaps(wxImage& image, bool compress);
|
||||
|
||||
private:
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, bool compress);
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
|
||||
friend class Compressor;
|
||||
|
@ -174,7 +174,7 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
|
||||
bool res = false;
|
||||
|
||||
if (!background_texture.filename.empty())
|
||||
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true);
|
||||
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, GLTexture::SingleThreaded, false);
|
||||
|
||||
if (res)
|
||||
m_background_texture.metadata = background_texture;
|
||||
|
@ -67,9 +67,12 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
|
||||
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
|
||||
|
||||
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
||||
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
|
||||
/* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1",
|
||||
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
||||
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
|
||||
|
||||
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
|
||||
|
||||
/* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1",
|
||||
};
|
||||
|
||||
std::string out = defaults[file_type];
|
||||
|
@ -44,6 +44,9 @@ enum FileType
|
||||
|
||||
FT_INI,
|
||||
FT_SVG,
|
||||
|
||||
FT_TEX,
|
||||
|
||||
FT_PNGZIP,
|
||||
|
||||
FT_SIZE,
|
||||
|
@ -43,7 +43,7 @@ bool GLGizmosManager::init()
|
||||
|
||||
if (!m_background_texture.metadata.filename.empty())
|
||||
{
|
||||
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, true))
|
||||
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false))
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
|
@ -1853,7 +1853,7 @@ struct Plater::priv
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||
// Sets m_bed.m_polygon to limit the object placement.
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
@ -1914,7 +1914,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
, main_frame(main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
|
||||
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
|
||||
@ -2017,11 +2017,21 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
|
||||
{
|
||||
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
|
||||
config->option<ConfigOptionString>("bed_custom_texture")->value,
|
||||
config->option<ConfigOptionString>("bed_custom_model")->value);
|
||||
});
|
||||
|
||||
// Preview events:
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
|
||||
{
|
||||
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
|
||||
config->option<ConfigOptionString>("bed_custom_texture")->value,
|
||||
config->option<ConfigOptionString>("bed_custom_model")->value);
|
||||
});
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
|
||||
@ -3625,9 +3635,9 @@ bool Plater::priv::can_mirror() const
|
||||
return get_selection().is_from_single_instance();
|
||||
}
|
||||
|
||||
void Plater::priv::set_bed_shape(const Pointfs& shape)
|
||||
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
|
||||
{
|
||||
bool new_shape = bed.set_shape(shape);
|
||||
bool new_shape = bed.set_shape(shape, custom_texture, custom_model);
|
||||
if (new_shape)
|
||||
{
|
||||
if (view3D) view3D->bed_shape_changed();
|
||||
@ -4559,7 +4569,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||
if (opt_key == "printer_technology")
|
||||
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
||||
else if (opt_key == "bed_shape") {
|
||||
else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) {
|
||||
bed_shape_changed = true;
|
||||
update_scheduled = true;
|
||||
}
|
||||
@ -4593,7 +4603,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
}
|
||||
|
||||
if (bed_shape_changed)
|
||||
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
|
||||
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
|
||||
p->config->option<ConfigOptionString>("bed_custom_model")->value);
|
||||
|
||||
if (update_scheduled)
|
||||
update();
|
||||
|
@ -411,7 +411,7 @@ const std::vector<std::string>& Preset::printer_options()
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"printer_technology",
|
||||
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
|
||||
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
||||
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
|
||||
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
||||
@ -512,6 +512,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"printer_technology",
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
|
||||
"bed_shape", "max_print_height",
|
||||
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
||||
"display_mirror_x", "display_mirror_y",
|
||||
|
@ -14,20 +14,20 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
|
||||
self(new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
|
||||
id == -1? wxID_ANY : id)),
|
||||
m_timer(new wxTimer(self)),
|
||||
m_prog (new wxGauge(self,
|
||||
wxGA_HORIZONTAL,
|
||||
100,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize)),
|
||||
m_cancelbutton(new wxButton(self,
|
||||
-1,
|
||||
_(L("Cancel")),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize))
|
||||
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id)
|
||||
: self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
|
||||
id == -1 ? wxID_ANY : id)}
|
||||
, m_prog{new wxGauge(self,
|
||||
wxGA_HORIZONTAL,
|
||||
100,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize)}
|
||||
, m_cancelbutton{new wxButton(self,
|
||||
-1,
|
||||
_(L("Cancel")),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize)}
|
||||
, m_timer{new wxTimer(self)}
|
||||
{
|
||||
m_prog->Hide();
|
||||
m_cancelbutton->Hide();
|
||||
|
@ -25,9 +25,9 @@ namespace Slic3r {
|
||||
class ProgressStatusBar
|
||||
{
|
||||
wxStatusBar *self; // we cheat! It should be the base class but: perl!
|
||||
wxTimer *m_timer;
|
||||
wxGauge *m_prog;
|
||||
wxButton *m_cancelbutton;
|
||||
std::unique_ptr<wxTimer> m_timer;
|
||||
public:
|
||||
|
||||
/// Cancel callback function type
|
||||
|
@ -1862,12 +1862,18 @@ void TabPrinter::build_fff()
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
const std::vector<Vec2d>& shape = dlg.get_shape();
|
||||
const std::string& custom_texture = dlg.get_custom_texture();
|
||||
const std::string& custom_model = dlg.get_custom_model();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
load_key_value("bed_custom_texture", custom_texture);
|
||||
load_key_value("bed_custom_model", custom_model);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
@ -2095,12 +2101,18 @@ void TabPrinter::build_sla()
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_texture"),
|
||||
*m_config->option<ConfigOptionString>("bed_custom_model"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
const std::vector<Vec2d>& shape = dlg.get_shape();
|
||||
const std::string& custom_texture = dlg.get_custom_texture();
|
||||
const std::string& custom_model = dlg.get_custom_model();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
load_key_value("bed_custom_texture", custom_texture);
|
||||
load_key_value("bed_custom_model", custom_model);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user