Merge branch 'main' into enh-update-wxwidgets

# Conflicts:
#	src/nanosvg/README.txt
#	src/nanosvg/nanosvg.h
#	src/nanosvg/nanosvgrast.h
#	src/slic3r/GUI/BitmapCache.cpp
#	src/slic3r/GUI/BitmapCache.hpp
#	src/slic3r/GUI/ImGuiWrapper.cpp
This commit is contained in:
Ocraftyone
2023-11-19 19:34:50 -05:00
467 changed files with 29648 additions and 17847 deletions
+20 -2
View File
@@ -91,8 +91,26 @@ void CBaseException::ShowLoadModules()
void CBaseException::ShowCallstack(HANDLE hThread, const CONTEXT* context)
{
OutputString(_T("Show CallStack:\r\n"));
LPSTACKINFO phead = StackWalker(hThread, context);
OutputString(_T("Show CallStack:\n"));
LPSTACKINFO phead = StackWalker(hThread, context);
// Show RVA of each call stack, so we can locate the symbol using pdb file
// To show the symbol, load the <szFaultingModule> in WinDBG with pdb file, then type the following commands:
// > lm which gives you the start address of each module, as well as module names
// > !dh <module name> list all module headers. Find the <virtual address> of the section given by
// the <section> output in the crash log
// > ln <module start address> + <section virtual address> + <offset> reveals the debug symbol
OutputString(_T("\nLogical Address:\n"));
TCHAR szFaultingModule[MAX_PATH];
DWORD section, offset;
for (LPSTACKINFO ps = phead; ps != nullptr; ps = ps->pNext) {
if (GetLogicalAddress((PVOID) ps->szFncAddr, szFaultingModule, sizeof(szFaultingModule), section, offset)) {
OutputString(_T("0x%X 0x%X:0x%X %s\n"), ps->szFncAddr, section, offset, szFaultingModule);
} else {
OutputString(_T("0x%X Unknown\n"), ps->szFncAddr);
}
}
FreeStackInformations(phead);
}
+15 -30
View File
@@ -1553,23 +1553,11 @@ int CLI::run(int argc, char **argv)
o->cut(Z, m_config.opt_float("cut"), &out);
}
#else
ModelObject* object = model.objects.front();
const BoundingBoxf3& box = object->bounding_box();
const float Margin = 20.0;
const float max_x = box.size()(0) / 2.0 + Margin;
const float min_x = -max_x;
const float max_y = box.size()(1) / 2.0 + Margin;
const float min_y = -max_y;
std::array<Vec3d, 4> plane_points;
plane_points[0] = { min_x, min_y, 0 };
plane_points[1] = { max_x, min_y, 0 };
plane_points[2] = { max_x, max_y, 0 };
plane_points[3] = { min_x, max_y, 0 };
for (Vec3d& point : plane_points) {
point += box.center();
}
model.objects.front()->cut(0, plane_points, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower);
Cut cut(model.objects.front(), 0, Geometry::translation_transform(m_config.opt_float("cut") * Vec3d::UnitZ()),
ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper);
auto cut_objects = cut.perform_with_plane();
for (ModelObject* obj : cut_objects)
model.add_object(*obj);
#endif
model.delete_object(size_t(0));
}
@@ -2279,12 +2267,12 @@ int CLI::run(int argc, char **argv)
else
colors.push_back("#FFFFFF");
std::vector<std::array<float, 4>> colors_out(colors.size());
unsigned char rgb_color[3] = {};
std::vector<ColorRGBA> colors_out(colors.size());
ColorRGBA rgb_color;
for (const std::string& color : colors) {
Slic3r::GUI::BitmapCache::parse_color(color, rgb_color);
Slic3r::decode_color(color, rgb_color);
size_t color_idx = &color - &colors.front();
colors_out[color_idx] = { float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f };
colors_out[color_idx] = rgb_color;
}
int gl_major, gl_minor, gl_verbos;
@@ -2353,19 +2341,16 @@ int CLI::run(int argc, char **argv)
// continue;
for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) {
const ModelInstance &model_instance = *model_object.instances[instance_idx];
glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true);
glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, false, true);
//glvolume_collection.volumes.back()->geometry_id = key.geometry_id;
std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00";
unsigned char rgb_color[3] = {};
Slic3r::GUI::BitmapCache::parse_color(color, rgb_color);
glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f);
ColorRGBA rgb_color;
Slic3r::decode_color(color, rgb_color);
glvolume_collection.volumes.back()->set_render_color(rgb_color);
std::array<float, 4> new_color;
new_color[0] = float(rgb_color[0]) / 255.f;
new_color[1] = float(rgb_color[1]) / 255.f;
new_color[2] = float(rgb_color[2]) / 255.f;
new_color[3] = 1.f;
ColorRGBA new_color;
new_color = rgb_color;
glvolume_collection.volumes.back()->set_color(new_color);
glvolume_collection.volumes.back()->printable = model_instance.printable;
}
+3 -3
View File
@@ -491,7 +491,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context)
}else
{
//调用错误一般是487(地址无效或者没有访问的权限、在符号表中未找到指定地址的相关信息)
this->OutputString(_T("Call SymGetSymFromAddr64 ,Address %08x Error:%08x\r\n"), sf.AddrPC.Offset, GetLastError());
//this->OutputString(_T("Call SymGetSymFromAddr64 ,Address %08x Error:%08x\n"), sf.AddrPC.Offset, GetLastError());
StringCchCopy(pCallStack->undFullName, STACKWALK_MAX_NAMELEN, textconv_helper::A2T_("Unknown"));
}
@@ -502,14 +502,14 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context)
pCallStack->uFileNum = pLine->LineNumber;
}else
{
this->OutputString(_T("Call SymGetLineFromAddr64 ,Address %08x Error:%08x\r\n"), sf.AddrPC.Offset, GetLastError());
//this->OutputString(_T("Call SymGetLineFromAddr64 ,Address %08x Error:%08x\n"), sf.AddrPC.Offset, GetLastError());
StringCchCopy(pCallStack->szFileName, MAX_PATH, textconv_helper::A2T_("Unknown file"));
pCallStack->uFileNum = -1;
}
//这里为了将获取函数信息失败的情况与正常的情况一起输出,防止用户在查看时出现误解
this->OutputString(_T("%08llx:%s [%s][%ld]\r\n"), pCallStack->szFncAddr, pCallStack->undFullName, pCallStack->szFileName, pCallStack->uFileNum);
this->OutputString(_T("%08llx:%s [%s][%ld]\n"), pCallStack->szFncAddr, pCallStack->undFullName, pCallStack->szFileName, pCallStack->uFileNum);
if (NULL == pHead)
{
pHead = pCallStack;
+2
View File
@@ -161,6 +161,7 @@ namespace ImGui
const wchar_t ClippyMarker = 0x0802;
const wchar_t InfoMarker = 0x0803;
const wchar_t SliderFloatEditBtnIcon = 0x0804;
const wchar_t ClipboardBtnIcon = 0x0805;
// BBS
const wchar_t CircleButtonIcon = 0x0810;
@@ -196,6 +197,7 @@ namespace ImGui
const wchar_t CloseBlockNotifButton = 0x0833;
const wchar_t CloseBlockNotifHoverButton = 0x0834;
const wchar_t BlockNotifErrorIcon = 0x0835;
const wchar_t ClipboardBtnDarkIcon = 0x0836;
// void MyFunction(const char* name, const MyMatrix44& v);
}
+12 -2
View File
@@ -6046,8 +6046,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->Pos = FindBestWindowPosForPopup(window);
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
window->Pos = FindBestWindowPosForPopup(window);
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
window->Pos = FindBestWindowPosForPopup(window);
// Orca: Allow fixed tooltip pos while still being clamped inside the render area
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_is_child_tooltip) {
if (window_pos_set_by_api) {
// Hack: add ImGuiWindowFlags_Popup so it does not follow cursor
ImGuiWindowFlags old_flags = window->Flags;
window->Flags |= ImGuiWindowFlags_Popup;
window->Pos = FindBestWindowPosForPopup(window);
window->Flags = old_flags;
} else {
window->Pos = FindBestWindowPosForPopup(window);
}
}
// Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
// When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
+3
View File
@@ -93,6 +93,9 @@ public:
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
private:
// Source definition of the print bed geometry (PrintConfig::printable_area)
std::vector<Vec2d> m_bed_shape;
+8
View File
@@ -56,6 +56,8 @@ set(lisbslic3r_sources
Clipper2Utils.cpp
Clipper2Utils.hpp
ClipperZUtils.hpp
Color.cpp
Color.hpp
Config.cpp
Config.hpp
CurveAnalyzer.cpp
@@ -200,12 +202,17 @@ set(lisbslic3r_sources
BlacklistedLibraryCheck.hpp
LocalesUtils.cpp
LocalesUtils.hpp
CutUtils.cpp
CutUtils.hpp
Model.cpp
Model.hpp
ModelArrange.hpp
ModelArrange.cpp
MultiMaterialSegmentation.cpp
MultiMaterialSegmentation.hpp
Measure.hpp
Measure.cpp
MeasureUtils.hpp
CustomGCode.cpp
CustomGCode.hpp
Arrange.hpp
@@ -305,6 +312,7 @@ set(lisbslic3r_sources
Surface.hpp
SurfaceCollection.cpp
SurfaceCollection.hpp
SurfaceMesh.hpp
SVG.cpp
SVG.hpp
Technologies.hpp
+420
View File
@@ -0,0 +1,420 @@
#include "libslic3r.h"
#include "Color.hpp"
#include <random>
static const float INV_255 = 1.0f / 255.0f;
namespace Slic3r {
// Conversion from RGB to HSV color space
// The input RGB values are in the range [0, 1]
// The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
static void RGBtoHSV(float r, float g, float b, float& h, float& s, float& v)
{
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
const float max_comp = std::max(std::max(r, g), b);
const float min_comp = std::min(std::min(r, g), b);
const float delta = max_comp - min_comp;
if (delta > 0.0f) {
if (max_comp == r)
h = 60.0f * (std::fmod(((g - b) / delta), 6.0f));
else if (max_comp == g)
h = 60.0f * (((b - r) / delta) + 2.0f);
else if (max_comp == b)
h = 60.0f * (((r - g) / delta) + 4.0f);
s = (max_comp > 0.0f) ? delta / max_comp : 0.0f;
}
else {
h = 0.0f;
s = 0.0f;
}
v = max_comp;
while (h < 0.0f) { h += 360.0f; }
while (h > 360.0f) { h -= 360.0f; }
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
}
// Conversion from HSV to RGB color space
// The input HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
// The output RGB values are in the range [0, 1]
static void HSVtoRGB(float h, float s, float v, float& r, float& g, float& b)
{
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
const float chroma = v * s;
const float h_prime = std::fmod(h / 60.0f, 6.0f);
const float x = chroma * (1.0f - std::abs(std::fmod(h_prime, 2.0f) - 1.0f));
const float m = v - chroma;
if (0.0f <= h_prime && h_prime < 1.0f) {
r = chroma;
g = x;
b = 0.0f;
}
else if (1.0f <= h_prime && h_prime < 2.0f) {
r = x;
g = chroma;
b = 0.0f;
}
else if (2.0f <= h_prime && h_prime < 3.0f) {
r = 0.0f;
g = chroma;
b = x;
}
else if (3.0f <= h_prime && h_prime < 4.0f) {
r = 0.0f;
g = x;
b = chroma;
}
else if (4.0f <= h_prime && h_prime < 5.0f) {
r = x;
g = 0.0f;
b = chroma;
}
else if (5.0f <= h_prime && h_prime < 6.0f) {
r = chroma;
g = 0.0f;
b = x;
}
else {
r = 0.0f;
g = 0.0f;
b = 0.0f;
}
r += m;
g += m;
b += m;
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
}
class Randomizer
{
std::random_device m_rd;
public:
float random_float(float min, float max) {
std::mt19937 rand_generator(m_rd());
std::uniform_real_distribution<float> distrib(min, max);
return distrib(rand_generator);
}
};
ColorRGB::ColorRGB(float r, float g, float b)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) })
{
}
ColorRGB::ColorRGB(unsigned char r, unsigned char g, unsigned char b)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGB::operator < (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
else if (m_data[i] > other.m_data[i])
return false;
}
return false;
}
bool ColorRGB::operator > (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
else if (m_data[i] < other.m_data[i])
return false;
}
return false;
}
ColorRGB ColorRGB::operator + (const ColorRGB& other) const
{
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGB ColorRGB::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA::ColorRGBA(float r, float g, float b, float a)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f), std::clamp(a, 0.0f, 1.0f) })
{
}
ColorRGBA::ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f), std::clamp(a * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGBA::operator < (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
else if (m_data[i] > other.m_data[i])
return false;
}
return false;
}
bool ColorRGBA::operator > (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
else if (m_data[i] < other.m_data[i])
return false;
}
return false;
}
ColorRGBA ColorRGBA::operator + (const ColorRGBA& other) const
{
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA ColorRGBA::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
ret.m_data[3] = this->m_data[3];
return ret;
}
ColorRGB operator * (float value, const ColorRGB& other) { return other * value; }
ColorRGBA operator * (float value, const ColorRGBA& other) { return other * value; }
ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGB complementary(const ColorRGB& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b() };
}
ColorRGBA complementary(const ColorRGBA& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b(), color.a() };
}
ColorRGB saturate(const ColorRGB& color, float factor)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
s = std::clamp(s * factor, 0.0f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGBA saturate(const ColorRGBA& color, float factor)
{
return to_rgba(saturate(to_rgb(color), factor), color.a());
}
ColorRGB opposite(const ColorRGB& color)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
h += 65.0f; // 65 instead 60 to avoid circle values
if (h > 360.0f)
h -= 360.0f;
Randomizer rnd;
s = rnd.random_float(0.65f, 1.0f);
v = rnd.random_float(0.65f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGB opposite(const ColorRGB& a, const ColorRGB& b)
{
float ha, sa, va;
RGBtoHSV(a.r(), a.g(), a.b(), ha, sa, va);
float hb, sb, vb;
RGBtoHSV(b.r(), b.g(), b.b(), hb, sb, vb);
float delta_h = std::abs(ha - hb);
float start_h = (delta_h > 180.0f) ? std::min(ha, hb) : std::max(ha, hb);
start_h += 5.0f; // to avoid circle change of colors for 120 deg
if (delta_h < 180.0f)
delta_h = 360.0f - delta_h;
Randomizer rnd;
float out_h = start_h + 0.5f * delta_h;
if (out_h > 360.0f)
out_h -= 360.0f;
float out_s = rnd.random_float(0.65f, 1.0f);
float out_v = rnd.random_float(0.65f, 1.0f);
float out_r, out_g, out_b;
HSVtoRGB(out_h, out_s, out_v, out_r, out_g, out_b);
return { out_r, out_g, out_b };
}
bool can_decode_color(const std::string &color)
{
return (color.size() == 7 && color.front() == '#') || (color.size() == 9 && color.front() == '#');
}
bool decode_color(const std::string& color_in, ColorRGB& color_out)
{
ColorRGBA rgba;
if (!decode_color(color_in, rgba))
return false;
color_out = to_rgb(rgba);
return true;
}
bool decode_color(const std::string& color_in, ColorRGBA& color_out)
{
auto hex_digit_to_int = [](const char c) {
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
};
color_out = ColorRGBA::BLACK();
if (can_decode_color(color_in)) {
const char *c = color_in.data() + 1;
if (color_in.size() == 7) {
for (unsigned int i = 0; i < 3; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
} else {
for (unsigned int i = 0; i < 4; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
}
} else
return false;
assert(0.0f <= color_out.r() && color_out.r() <= 1.0f);
assert(0.0f <= color_out.g() && color_out.g() <= 1.0f);
assert(0.0f <= color_out.b() && color_out.b() <= 1.0f);
assert(0.0f <= color_out.a() && color_out.a() <= 1.0f);
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out)
{
colors_out = std::vector<ColorRGB>(colors_in.size(), ColorRGB::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out)
{
colors_out = std::vector<ColorRGBA>(colors_in.size(), ColorRGBA::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
std::string encode_color(const ColorRGB& color)
{
char buffer[64];
::sprintf(buffer, "#%02X%02X%02X", color.r_uchar(), color.g_uchar(), color.b_uchar());
return std::string(buffer);
}
std::string encode_color(const ColorRGBA& color) { return encode_color(to_rgb(color)); }
ColorRGB to_rgb(const ColorRGBA& other_rgba) { return { other_rgba.r(), other_rgba.g(), other_rgba.b() }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), 1.0f }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), alpha }; }
ColorRGBA picking_decode(unsigned int id)
{
return {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff)) * INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b) { return r + (g << 8) + (b << 16); }
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace Slic3r
+175
View File
@@ -0,0 +1,175 @@
#ifndef slic3r_Color_hpp_
#define slic3r_Color_hpp_
#include <array>
#include <algorithm>
namespace Slic3r {
class ColorRGB
{
std::array<float, 3> m_data{1.0f, 1.0f, 1.0f};
public:
ColorRGB() = default;
ColorRGB(float r, float g, float b);
ColorRGB(unsigned char r, unsigned char g, unsigned char b);
ColorRGB(const ColorRGB& other) = default;
ColorRGB& operator = (const ColorRGB& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGB& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGB& other) const { return !operator==(other); }
bool operator < (const ColorRGB& other) const;
bool operator > (const ColorRGB& other) const;
ColorRGB operator + (const ColorRGB& other) const;
ColorRGB operator * (float value) const;
const float* const data() const { return m_data.data(); }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 2);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
static const ColorRGB BLACK() { return { 0.0f, 0.0f, 0.0f }; }
static const ColorRGB BLUE() { return { 0.0f, 0.0f, 1.0f }; }
static const ColorRGB BLUEISH() { return { 0.5f, 0.5f, 1.0f }; }
static const ColorRGB CYAN() { return { 0.0f, 1.0f, 1.0f }; }
static const ColorRGB DARK_GRAY() { return { 0.25f, 0.25f, 0.25f }; }
static const ColorRGB DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f }; }
static const ColorRGB GRAY() { return { 0.5f, 0.5f, 0.5f }; }
static const ColorRGB GREEN() { return { 0.0f, 1.0f, 0.0f }; }
static const ColorRGB GREENISH() { return { 0.5f, 1.0f, 0.5f }; }
static const ColorRGB LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f }; }
static const ColorRGB MAGENTA() { return { 1.0f, 0.0f, 1.0f }; }
static const ColorRGB ORANGE() { return { 0.92f, 0.50f, 0.26f }; }
static const ColorRGB RED() { return { 1.0f, 0.0f, 0.0f }; }
static const ColorRGB REDISH() { return { 1.0f, 0.5f, 0.5f }; }
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }
static const ColorRGB Z() { return { 0.0f, 0.0f, 0.75f }; }
};
class ColorRGBA
{
std::array<float, 4> m_data{ 1.0f, 1.0f, 1.0f, 1.0f };
public:
ColorRGBA() = default;
ColorRGBA(float r, float g, float b, float a);
ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
ColorRGBA(const ColorRGBA& other) = default;
ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGBA& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGBA& other) const { return !operator==(other); }
bool operator < (const ColorRGBA& other) const;
bool operator > (const ColorRGBA& other) const;
ColorRGBA operator + (const ColorRGBA& other) const;
ColorRGBA operator * (float value) const;
const float* const data() const { return m_data.data(); }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
float a() const { return m_data[3]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void a(float a) { m_data[3] = std::clamp(a, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 3);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
unsigned char a_uchar() const { return static_cast<unsigned char>(m_data[3] * 255.0f); }
bool is_transparent() const { return m_data[3] < 1.0f; }
static const ColorRGBA BLACK() { return { 0.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA BLUE() { return { 0.0f, 0.0f, 1.0f, 1.0f }; }
static const ColorRGBA BLUEISH() { return { 0.5f, 0.5f, 1.0f, 1.0f }; }
static const ColorRGBA CYAN() { return { 0.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA DARK_GRAY() { return { 0.25f, 0.25f, 0.25f, 1.0f }; }
static const ColorRGBA DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f, 1.0f }; }
static const ColorRGBA GRAY() { return { 0.5f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA GREEN() { return { 0.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA GREENISH() { return { 0.5f, 1.0f, 0.5f, 1.0f }; }
static const ColorRGBA LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f, 1.0f }; }
static const ColorRGBA MAGENTA() { return { 1.0f, 0.0f, 1.0f, 1.0f }; }
static const ColorRGBA ORANGE() { return { 0.923f, 0.504f, 0.264f, 1.0f }; }
static const ColorRGBA RED() { return { 1.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA REDISH() { return { 1.0f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA YELLOW() { return { 1.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA WHITE() { return { 1.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255, 1.0f}; }
static const ColorRGBA X() { return { 0.75f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA Y() { return { 0.0f, 0.75f, 0.0f, 1.0f }; }
static const ColorRGBA Z() { return { 0.0f, 0.0f, 0.75f, 1.0f }; }
};
ColorRGB operator * (float value, const ColorRGB& other);
ColorRGBA operator * (float value, const ColorRGBA& other);
ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t);
ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t);
ColorRGB complementary(const ColorRGB& color);
ColorRGBA complementary(const ColorRGBA& color);
ColorRGB saturate(const ColorRGB& color, float factor);
ColorRGBA saturate(const ColorRGBA& color, float factor);
ColorRGB opposite(const ColorRGB& color);
ColorRGB opposite(const ColorRGB& a, const ColorRGB& b);
bool can_decode_color(const std::string& color);
bool decode_color(const std::string& color_in, ColorRGB& color_out);
bool decode_color(const std::string& color_in, ColorRGBA& color_out);
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out);
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out);
std::string encode_color(const ColorRGB& color);
std::string encode_color(const ColorRGBA& color);
ColorRGB to_rgb(const ColorRGBA& other_rgba);
ColorRGBA to_rgba(const ColorRGB& other_rgb);
ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha);
ColorRGBA picking_decode(unsigned int id);
unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b);
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace Slic3r
#endif /* slic3r_Color_hpp_ */
+662
View File
@@ -0,0 +1,662 @@
///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "CutUtils.hpp"
#include "Geometry.hpp"
#include "libslic3r.h"
#include "Model.hpp"
#include "TriangleMeshSlicer.hpp"
#include "TriangleSelector.hpp"
#include "ObjectID.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
using namespace Geometry;
static void apply_tolerance(ModelVolume* vol)
{
ModelVolume::CutInfo& cut_info = vol->cut_info;
assert(cut_info.is_connector);
if (!cut_info.is_processed)
return;
Vec3d sf = vol->get_scaling_factor();
// make a "hole" wider
sf[X] += double(cut_info.radius_tolerance);
sf[Y] += double(cut_info.radius_tolerance);
// make a "hole" dipper
sf[Z] += double(cut_info.height_tolerance);
vol->set_scaling_factor(sf);
// correct offset in respect to the new depth
Vec3d rot_norm = rotation_transform(vol->get_rotation()) * Vec3d::UnitZ();
if (rot_norm.norm() != 0.0)
rot_norm.normalize();
double z_offset = 0.5 * static_cast<double>(cut_info.height_tolerance);
if (cut_info.connector_type == CutConnectorType::Plug ||
cut_info.connector_type == CutConnectorType::Snap)
z_offset -= 0.05; // add small Z offset to better preview
vol->set_offset(vol->get_offset() + rot_norm * z_offset);
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->set_type(type);
vol->name = src_volume->name + suffix;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
vol->cut_info = src_volume->cut_info;
}
static void process_volume_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh)
{
const auto volume_matrix = volume->get_matrix();
const Transformation cut_transformation = Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1 * cut_transformation.get_offset());
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
static void process_connector_cut( ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels)
{
assert(volume->cut_info.is_connector);
volume->cut_info.set_processed();
const auto volume_matrix = volume->get_matrix();
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (volume->cut_info.connector_type != CutConnectorType::Dowel) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume* vol = nullptr;
if (volume->cut_info.connector_type == CutConnectorType::Snap) {
TriangleMesh mesh = TriangleMesh(its_make_cylinder(1.0, 1.0, PI / 180.));
vol = upper->add_volume(std::move(mesh));
vol->set_transformation(volume->get_transformation());
vol->set_type(ModelVolumeType::NEGATIVE_VOLUME);
vol->cut_info = volume->cut_info;
vol->name = volume->name;
}
else
vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
apply_tolerance(vol);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
}
else {
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject* dowel{ nullptr };
// Clone the object to duplicate instances, materials etc.
volume->get_object()->clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume* vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
dowels.push_back(dowel);
}
// Cut the dowel
apply_tolerance(volume);
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh);
// add small Z offset to better preview
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
// Add cut parts to the related objects
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
}
}
static void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
{
const auto volume_matrix = instance_matrix * volume->get_matrix();
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Transformation(volume_matrix));
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
lower->add_volume(*volume);
}
static void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower)
{
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
if (!lower_mesh.empty()) {
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
upper->volumes.back()->cut_info.is_from_upper = false;
}
return;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty())
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
}
static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx,
const Transform3d& cut_matrix = Transform3d::Identity(),
bool place_on_cut = false, bool flip = false)
{
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < object->instances.size(); ++i) {
auto& obj_instance = object->instances[i];
const double rot_z = obj_instance->get_rotation().z();
Transformation inst_trafo = Transformation(obj_instance->get_transformation().get_matrix(false, false, true));
// add respect to mirroring
if (obj_instance->is_left_handed())
inst_trafo = inst_trafo * Transformation(scale_transform(Vec3d(-1, 1, 1)));
obj_instance->set_transformation(inst_trafo);
Vec3d rotation = Vec3d::Zero();
if (!flip && !place_on_cut) {
if ( i != src_instance_idx)
rotation[Z] = rot_z;
}
else {
Transform3d rotation_matrix = Transform3d::Identity();
if (flip)
rotation_matrix = rotation_transform(PI * Vec3d::UnitX());
if (place_on_cut)
rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse();
if (i != src_instance_idx)
rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix;
rotation = Transformation(rotation_matrix).get_rotation();
}
obj_instance->set_rotation(rotation);
}
}
Cut::Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes/*= ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts*/)
: m_instance(instance), m_cut_matrix(cut_matrix), m_attributes(attributes)
{
m_model = Model();
if (object)
m_model.add_object(*object);
}
void Cut::post_process(ModelObject* object, ModelObjectPtrs& cut_object_ptrs, bool keep, bool place_on_cut, bool flip)
{
if (!object) return;
if (keep && !object->volumes.empty()) {
reset_instance_transformation(object, m_instance, m_cut_matrix, place_on_cut, flip);
cut_object_ptrs.push_back(object);
}
else
m_model.objects.push_back(object); // will be deleted in m_model.clear_objects();
}
void Cut::post_process(ModelObject* upper, ModelObject* lower, ModelObjectPtrs& cut_object_ptrs)
{
post_process(upper, cut_object_ptrs,
m_attributes.has(ModelObjectCutAttribute::KeepUpper),
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
m_attributes.has(ModelObjectCutAttribute::FlipUpper));
post_process(lower, cut_object_ptrs,
m_attributes.has(ModelObjectCutAttribute::KeepLower),
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
m_attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || m_attributes.has(ModelObjectCutAttribute::FlipLower));
}
void Cut::finalize(const ModelObjectPtrs& objects)
{
//clear model from temporarry objects
m_model.clear_objects();
// add to model result objects
m_model.objects = objects;
}
const ModelObjectPtrs& Cut::perform_with_plane()
{
if (!m_attributes.has(ModelObjectCutAttribute::KeepUpper) && !m_attributes.has(ModelObjectCutAttribute::KeepLower)) {
m_model.clear_objects();
return m_model.objects;
}
ModelObject* mo = m_model.objects.front();
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// Clone the object to duplicate instances, materials etc.
ModelObject* upper{ nullptr };
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper))
mo->clone_for_cut(&upper);
ModelObject* lower{ nullptr };
if (m_attributes.has(ModelObjectCutAttribute::KeepLower) && !m_attributes.has(ModelObjectCutAttribute::KeepAsParts))
mo->clone_for_cut(&lower);
std::vector<ModelObject*> dowels;
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
const auto instance_matrix = mo->instances[m_instance]->get_transformation().get_matrix(true);
const Transformation cut_transformation = Transformation(m_cut_matrix);
const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset());
for (ModelVolume* volume : mo->volumes) {
volume->reset_extra_facets();
if (!volume->is_model_part()) {
if (volume->cut_info.is_processed)
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, m_attributes, upper, lower);
else
process_connector_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower, dowels);
}
else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, m_cut_matrix, m_attributes, upper, lower);
}
// Post-process cut parts
if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && upper->volumes.empty()) {
m_model = Model();
m_model.objects.push_back(upper);
return m_model.objects;
}
ModelObjectPtrs cut_object_ptrs;
if (m_attributes.has(ModelObjectCutAttribute::KeepAsParts) && !upper->volumes.empty()) {
reset_instance_transformation(upper, m_instance, m_cut_matrix);
cut_object_ptrs.push_back(upper);
}
else {
// Delete all modifiers which are not intersecting with solid parts bounding box
auto delete_extra_modifiers = [this](ModelObject* mo) {
if (!mo) return;
const BoundingBoxf3 obj_bb = mo->instance_bounding_box(m_instance);
const Transform3d inst_matrix = mo->instances[m_instance]->get_transformation().get_matrix();
for (int i = int(mo->volumes.size()) - 1; i >= 0; --i)
if (const ModelVolume* vol = mo->volumes[i];
!vol->is_model_part() && !vol->is_cut_connector()) {
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
if (!obj_bb.intersects(bb))
mo->delete_volume(i);
}
};
post_process(upper, lower, cut_object_ptrs);
delete_extra_modifiers(upper);
delete_extra_modifiers(lower);
if (m_attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
reset_instance_transformation(dowel, m_instance);
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
cut_object_ptrs.push_back(dowel);
}
}
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
finalize(cut_object_ptrs);
return m_model.objects;
}
static void distribute_modifiers_from_object(ModelObject* from_obj, const int instance_idx, ModelObject* to_obj1, ModelObject* to_obj2)
{
auto obj1_bb = to_obj1 ? to_obj1->instance_bounding_box(instance_idx) : BoundingBoxf3();
auto obj2_bb = to_obj2 ? to_obj2->instance_bounding_box(instance_idx) : BoundingBoxf3();
const Transform3d inst_matrix = from_obj->instances[instance_idx]->get_transformation().get_matrix();
for (ModelVolume* vol : from_obj->volumes)
if (!vol->is_model_part()) {
// Don't add modifiers which are processed connectors
if (vol->cut_info.is_connector && !vol->cut_info.is_processed)
continue;
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
// Don't add modifiers which are not intersecting with solid parts
if (obj1_bb.intersects(bb))
to_obj1->add_volume(*vol);
if (obj2_bb.intersects(bb))
to_obj2->add_volume(*vol);
}
}
static void merge_solid_parts_inside_object(ModelObjectPtrs& objects)
{
for (ModelObject* mo : objects) {
TriangleMesh mesh;
// Merge all SolidPart but not Connectors
for (const ModelVolume* mv : mo->volumes) {
if (mv->is_model_part() && !mv->is_cut_connector()) {
TriangleMesh m = mv->mesh();
m.transform(mv->get_matrix());
mesh.merge(m);
}
}
if (!mesh.empty()) {
ModelVolume* new_volume = mo->add_volume(mesh);
new_volume->name = mo->name;
// Delete all merged SolidPart but not Connectors
for (int i = int(mo->volumes.size()) - 2; i >= 0; --i) {
const ModelVolume* mv = mo->volumes[i];
if (mv->is_model_part() && !mv->is_cut_connector())
mo->delete_volume(i);
}
// Ensuring that volumes start with solid parts for proper slicing
mo->sort_volumes(true);
}
}
}
const ModelObjectPtrs& Cut::perform_by_contour(std::vector<Part> parts, int dowels_count)
{
ModelObject* cut_mo = m_model.objects.front();
// Clone the object to duplicate instances, materials etc.
ModelObject* upper{ nullptr };
if (m_attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_mo->clone_for_cut(&upper);
ModelObject* lower{ nullptr };
if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower);
const size_t cut_parts_cnt = parts.size();
bool has_modifiers = false;
// Distribute SolidParts to the Upper/Lower object
for (size_t id = 0; id < cut_parts_cnt; ++id) {
if (parts[id].is_modifier)
has_modifiers = true; // modifiers will be added later to the related parts
else if (ModelObject* obj = (parts[id].selected ? upper : lower))
obj->add_volume(*(cut_mo->volumes[id]));
}
if (has_modifiers) {
// Distribute Modifiers to the Upper/Lower object
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
}
ModelObjectPtrs cut_object_ptrs;
ModelVolumePtrs& volumes = cut_mo->volumes;
if (volumes.size() == cut_parts_cnt) {
// Means that object is cut without connectors
// Just add Upper and Lower objects to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
// Now merge all model parts together:
merge_solid_parts_inside_object(cut_object_ptrs);
// replace initial objects in model with cut object
finalize(cut_object_ptrs);
}
else if (volumes.size() > cut_parts_cnt) {
// Means that object is cut with connectors
// All volumes are distributed to Upper / Lower object,
// So we dont need them anymore
for (size_t id = 0; id < cut_parts_cnt; id++)
delete* (volumes.begin() + id);
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
// Perform cut just to get connectors
Cut cut(cut_mo, m_instance, m_cut_matrix, m_attributes);
const ModelObjectPtrs& cut_connectors_obj = cut.perform_with_plane();
assert(dowels_count > 0 ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
// Connectors from upper object
for (const ModelVolume* volume : cut_connectors_obj[0]->volumes)
upper->add_volume(*volume, volume->type());
// Connectors from lower object
for (const ModelVolume* volume : cut_connectors_obj[1]->volumes)
lower->add_volume(*volume, volume->type());
// Add Upper and Lower objects to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
// Now merge all model parts together:
merge_solid_parts_inside_object(cut_object_ptrs);
// replace initial objects in model with cut object
finalize(cut_object_ptrs);
// Add Dowel-connectors as separate objects to model
if (cut_connectors_obj.size() >= 3)
for (size_t id = 2; id < cut_connectors_obj.size(); id++)
m_model.add_object(*cut_connectors_obj[id]);
}
return m_model.objects;
}
const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts/* = false*/)
{
ModelObject* cut_mo = m_model.objects.front();
// Clone the object to duplicate instances, materials etc.
ModelObject* upper{ nullptr };
cut_mo->clone_for_cut(&upper);
ModelObject* lower{ nullptr };
cut_mo->clone_for_cut(&lower);
const double groove_half_depth = 0.5 * double(groove.depth);
Model tmp_model_for_cut = Model();
Model tmp_model = Model();
tmp_model.add_object(*cut_mo);
ModelObject* tmp_object = tmp_model.objects.front();
auto add_volumes_from_cut = [](ModelObject* object, const ModelObjectCutAttribute attribute, const Model& tmp_model_for_cut) {
const auto& volumes = tmp_model_for_cut.objects.front()->volumes;
for (const ModelVolume* volume : volumes)
if (volume->is_model_part()) {
if ((attribute == ModelObjectCutAttribute::KeepUpper && volume->is_from_upper()) ||
(attribute != ModelObjectCutAttribute::KeepUpper && !volume->is_from_upper())) {
ModelVolume* new_vol = object->add_volume(*volume);
new_vol->reset_from_upper();
}
}
};
auto cut = [this, add_volumes_from_cut]
(ModelObject* object, const Transform3d& cut_matrix, const ModelObjectCutAttribute add_volumes_attribute, Model& tmp_model_for_cut) {
Cut cut(object, m_instance, cut_matrix);
tmp_model_for_cut = Model();
tmp_model_for_cut.add_object(*cut.perform_with_plane().front());
assert(!tmp_model_for_cut.objects.empty());
object->clear_volumes();
add_volumes_from_cut(object, add_volumes_attribute, tmp_model_for_cut);
reset_instance_transformation(object, m_instance);
};
// cut by upper plane
const Transform3d cut_matrix_upper = translation_transform(rotation_m * (groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
{
cut(tmp_object, cut_matrix_upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// cut by lower plane
const Transform3d cut_matrix_lower = translation_transform(rotation_m * (-groove_half_depth * Vec3d::UnitZ())) * m_cut_matrix;
{
cut(tmp_object, cut_matrix_lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
}
// cut middle part with 2 angles and add parts to related upper/lower objects
const double h_side_shift = 0.5 * double(groove.width + groove.depth / tan(groove.flaps_angle));
// cut by angle1 plane
{
const Transform3d cut_matrix_angle1 = translation_transform(rotation_m * (-h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
cut(tmp_object, cut_matrix_angle1, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// cut by angle2 plane
{
const Transform3d cut_matrix_angle2 = translation_transform(rotation_m * (h_side_shift * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
cut(tmp_object, cut_matrix_angle2, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
add_volumes_from_cut(lower, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// apply tolerance to the middle part
{
const double h_groove_shift_tolerance = groove_half_depth - (double)groove.depth_tolerance;
const Transform3d cut_matrix_lower_tolerance = translation_transform(rotation_m * (-h_groove_shift_tolerance * Vec3d::UnitZ())) * m_cut_matrix;
cut(tmp_object, cut_matrix_lower_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
const double h_side_shift_tolerance = h_side_shift - 0.5 * double(groove.width_tolerance);
const Transform3d cut_matrix_angle1_tolerance = translation_transform(rotation_m * (-h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, -groove.flaps_angle, -groove.angle));
cut(tmp_object, cut_matrix_angle1_tolerance, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
const Transform3d cut_matrix_angle2_tolerance = translation_transform(rotation_m * (h_side_shift_tolerance * Vec3d::UnitX())) * m_cut_matrix * rotation_transform(Vec3d(0, groove.flaps_angle, groove.angle));
cut(tmp_object, cut_matrix_angle2_tolerance, ModelObjectCutAttribute::KeepUpper, tmp_model_for_cut);
}
// this part can be added to the upper object now
add_volumes_from_cut(upper, ModelObjectCutAttribute::KeepLower, tmp_model_for_cut);
ModelObjectPtrs cut_object_ptrs;
if (keep_as_parts) {
// add volumes from lower object to the upper, but mark them as a lower
const auto& volumes = lower->volumes;
for (const ModelVolume* volume : volumes) {
ModelVolume* new_vol = upper->add_volume(*volume);
new_vol->cut_info.is_from_upper = false;
}
// add modifiers
for (const ModelVolume* volume : cut_mo->volumes)
if (!volume->is_model_part())
upper->add_volume(*volume);
cut_object_ptrs.push_back(upper);
// add lower object to the cut_object_ptrs just to correct delete it from the Model destructor and avoid memory leaks
cut_object_ptrs.push_back(lower);
}
else {
// add modifiers if object has any
for (const ModelVolume* volume : cut_mo->volumes)
if (!volume->is_model_part()) {
distribute_modifiers_from_object(cut_mo, m_instance, upper, lower);
break;
}
assert(!upper->volumes.empty() && !lower->volumes.empty());
// Add Upper and Lower parts to cut_object_ptrs
post_process(upper, lower, cut_object_ptrs);
// Now merge all model parts together:
merge_solid_parts_inside_object(cut_object_ptrs);
}
finalize(cut_object_ptrs);
return m_model.objects;
}
} // namespace Slic3r
+70
View File
@@ -0,0 +1,70 @@
///|/ Copyright (c) Prusa Research 2023 Oleksandra Iushchenko @YuSanka
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_CutUtils_hpp_
#define slic3r_CutUtils_hpp_
#include "enum_bitmask.hpp"
#include "Point.hpp"
#include "Model.hpp"
#include <vector>
namespace Slic3r {
using ModelObjectPtrs = std::vector<ModelObject*>;
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, KeepAsParts, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, InvalidateCutInfo };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
class Cut {
Model m_model;
int m_instance;
const Transform3d m_cut_matrix;
ModelObjectCutAttributes m_attributes;
void post_process(ModelObject* object, ModelObjectPtrs& objects, bool keep, bool place_on_cut, bool flip);
void post_process(ModelObject* upper_object, ModelObject* lower_object, ModelObjectPtrs& objects);
void finalize(const ModelObjectPtrs& objects);
public:
Cut(const ModelObject* object, int instance, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes = ModelObjectCutAttribute::KeepUpper |
ModelObjectCutAttribute::KeepLower |
ModelObjectCutAttribute::KeepAsParts );
~Cut() { m_model.clear_objects(); }
struct Groove
{
float depth{ 0.f };
float width{ 0.f };
float flaps_angle{ 0.f };
float angle{ 0.f };
float depth_init{ 0.f };
float width_init{ 0.f };
float flaps_angle_init{ 0.f };
float angle_init{ 0.f };
float depth_tolerance{ 0.1f };
float width_tolerance{ 0.1f };
};
struct Part
{
bool selected;
bool is_modifier;
};
const ModelObjectPtrs& perform_with_plane();
const ModelObjectPtrs& perform_by_contour(std::vector<Part> parts, int dowels_count);
const ModelObjectPtrs& perform_with_groove(const Groove& groove, const Transform3d& rotation_m, bool keep_as_parts = false);
}; // namespace Cut
} // namespace Slic3r
#endif /* slic3r_CutUtils_hpp_ */
+2 -5
View File
@@ -56,12 +56,9 @@ ExtrusionEntityCollection::operator ExtrusionPaths() const
return paths;
}
ExtrusionEntity* ExtrusionEntityCollection::clone() const
ExtrusionEntity *ExtrusionEntityCollection::clone() const
{
ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this);
for (size_t i = 0; i < coll->entities.size(); ++i)
coll->entities[i] = this->entities[i]->clone();
return coll;
return new ExtrusionEntityCollection(*this);
}
void ExtrusionEntityCollection::reverse()
+101 -92
View File
@@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) 2020 Henner Zeller
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "../libslic3r.h"
#include "../Exception.hpp"
#include "../Model.hpp"
@@ -741,8 +746,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
{
int volume_id;
int type;
float radius;
float height;
float r_tolerance;
float h_tolerance;
};
@@ -758,10 +761,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//typedef std::map<Id, ComponentsList> IdToAliasesMap;
typedef std::vector<Instance> InstancesList;
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
//typedef std::map<Id, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
/*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/
@@ -951,7 +954,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//IdToGeometryMap m_orig_geometries; // backup & restore
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
IdToCutObjectInfoMap m_cut_object_infos;
IdToCutObjectInfoMap m_cut_object_infos;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges;
/*IdToSlaSupportPointsMap m_sla_support_points;
@@ -1013,7 +1016,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions);
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
@@ -1955,11 +1958,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
if (cut_object_info != m_cut_object_infos.end()) {
model_object->cut_id = cut_object_info->second.id;
int vol_cnt = int(model_object->volumes.size());
for (auto connector : cut_object_info->second.connectors) {
assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size()));
model_object->volumes[connector.volume_id]->cut_info =
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.radius, connector.height, connector.r_tolerance, connector.h_tolerance, true);
if (connector.volume_id < 0 || connector.volume_id >= vol_cnt) {
add_error("Invalid connector is found");
continue;
}
model_object->volumes[connector.volume_id]->cut_info =
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
}
}
}
@@ -2324,7 +2330,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions)
{
if (stat.m_uncomp_size > 0) {
std::string buffer((size_t) stat.m_uncomp_size, 0);
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading cut information data to buffer");
@@ -2332,12 +2338,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
}
std::istringstream iss(buffer); // wrap returned xml to istringstream
pt::ptree objects_tree;
pt::ptree objects_tree;
pt::read_xml(iss, objects_tree);
for (const auto &object : objects_tree.get_child("objects")) {
for (const auto& object : objects_tree.get_child("objects")) {
pt::ptree object_tree = object.second;
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
if (obj_idx <= 0) {
add_error("Found invalid object id");
continue;
@@ -2349,30 +2355,33 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
continue;
}
CutObjectBase cut_id;
std::vector<CutObjectInfo::Connector> connectors;
CutObjectBase cut_id;
std::vector<CutObjectInfo::Connector> connectors;
for (const auto &obj_cut_info : object_tree) {
for (const auto& obj_cut_info : object_tree) {
if (obj_cut_info.first == "cut_id") {
pt::ptree cut_id_tree = obj_cut_info.second;
cut_id = CutObjectBase(ObjectID(cut_id_tree.get<size_t>("<xmlattr>.id")), cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
cut_id = CutObjectBase(ObjectID( cut_id_tree.get<size_t>("<xmlattr>.id")),
cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
}
if (obj_cut_info.first == "connectors") {
pt::ptree cut_connectors_tree = obj_cut_info.second;
for (const auto &cut_connector : cut_connectors_tree) {
if (cut_connector.first != "connector") continue;
pt::ptree connector_tree = cut_connector.second;
CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"), connector_tree.get<int>("<xmlattr>.type"),
connector_tree.get<float>("<xmlattr>.radius", 0.f), connector_tree.get<float>("<xmlattr>.height", 0.f),
connector_tree.get<float>("<xmlattr>.r_tolerance"), connector_tree.get<float>("<xmlattr>.h_tolerance")};
for (const auto& cut_connector : cut_connectors_tree) {
if (cut_connector.first != "connector")
continue;
pt::ptree connector_tree = cut_connector.second;
CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"),
connector_tree.get<int>("<xmlattr>.type"),
connector_tree.get<float>("<xmlattr>.r_tolerance"),
connector_tree.get<float>("<xmlattr>.h_tolerance")};
connectors.emplace_back(connector);
}
}
}
CutObjectInfo cut_info{cut_id, connectors};
m_cut_object_infos.insert({obj_idx, cut_info});
CutObjectInfo cut_info {cut_id, connectors};
m_cut_object_infos.insert({ obj_idx, cut_info });
}
}
}
@@ -5260,6 +5269,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: change volume to seperate objects
bool _add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const;
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const;
bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
@@ -5270,7 +5280,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: add project embedded preset files
bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false);
bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model);
bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config);
bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr);
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
@@ -6233,7 +6242,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
continue;
volume_count++;
if (m_share_mesh) {
auto iter = m_shared_meshes.find(volume->mesh_ptr());
auto iter = m_shared_meshes.find(volume->mesh_ptr().get());
if (iter != m_shared_meshes.end())
{
const ModelVolume* shared_volume = iter->second.second;
@@ -6248,7 +6257,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
continue;
}
}
const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr(), {&object_data, volume}});
const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}});
}
if (m_from_backup_save)
volume_id = (volume_count << 16 | backup_id);
@@ -6704,6 +6713,69 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects) {
object_cnt++;
if (!object->is_cut())
continue;
pt::ptree& obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store info for cut_id
pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
// store cut_id atributes
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
int volume_idx = -1;
for (const ModelVolume* volume : object->volumes) {
++volume_idx;
if (volume->is_cut_connector()) {
pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
}
}
}
if (!tree.empty()) {
std::ostringstream oss;
pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "><connectors", ">\n <connectors");
boost::replace_all(out, "></connectors>", ">\n </connectors>");
boost::replace_all(out, "><connector", ">\n <connector");
boost::replace_all(out, "></connector>", ">\n </connector>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add cut information file to archive");
return false;
}
}
return true;
}
bool _BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
{
assert(is_decimal_separator_point());
@@ -7266,69 +7338,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject *object : model.objects) {
object_cnt++;
pt::ptree &obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store info for cut_id
pt::ptree &cut_id_tree = obj_tree.add("cut_id", "");
// store cut_id atributes
cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
int volume_idx = -1;
for (const ModelVolume *volume : object->volumes) {
++volume_idx;
if (volume->is_cut_connector()) {
pt::ptree &connectors_tree = obj_tree.add("connectors.connector", "");
connectors_tree.put("<xmlattr>.volume_id", volume_idx);
connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
connectors_tree.put("<xmlattr>.radius", volume->cut_info.radius);
connectors_tree.put("<xmlattr>.height", volume->cut_info.height);
connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
}
}
}
if (!tree.empty()) {
std::ostringstream oss;
pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><cut_id", ">\n <cut_id");
boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
boost::replace_all(out, "><connectors", ">\n <connectors");
boost::replace_all(out, "></connectors>", ">\n </connectors>");
boost::replace_all(out, "><connector", ">\n <connector");
boost::replace_all(out, "></connector>", ">\n </connector>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void *) out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add cut information file to archive");
return false;
}
}
return true;
}
bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config)
{
std::stringstream stream;
+7 -2
View File
@@ -2578,6 +2578,7 @@ this->placeholder_parser().set("z_offset", new ConfigOptionFloat(m_config.z_offs
m_writer.extruders(),
// Modifies
print.m_print_statistics));
print.m_print_statistics.initial_tool = initial_extruder_id;
if (!is_bbl_printers) {
file.write_format("; total filament used [g] = %.2lf\n",
print.m_print_statistics.total_weight);
@@ -5448,8 +5449,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
old_retract_length = m_config.retraction_length.get_at(previous_extruder_id);
old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id);
old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id);
wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id];
wipe_volume *= m_config.flush_multiplier;
if (m_config.purge_in_prime_tower) {
wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id];
wipe_volume *= m_config.flush_multiplier;
} else {
wipe_volume = m_config.prime_volume;
}
old_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(previous_extruder_id) / filament_area);
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
//BBS: must clean m_start_gcode_filament
+7 -4
View File
@@ -57,17 +57,20 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb,
std::string encoded;
encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size));
encoded.resize(boost::beast::detail::base64::encode((void *) encoded.data(), (const void *) compressed->data,
compressed->size));
output((boost::format("; thumbnail begin %dx%d %d\n") % data.width % data.height % encoded.size()).str().c_str());
compressed->size));
output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size())
.str()
.c_str());
while (encoded.size() > max_row_length) {
output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str());
encoded = encoded.substr(max_row_length);
}
// Orca write remaining ecoded data
if (encoded.size() > 0)
output((boost::format("; %s\n") % encoded).str().c_str());
output("; thumbnail end\n");
output((boost::format("; %s end\n") % compressed->tag()).str().c_str());
}
throw_if_canceled();
}
@@ -81,4 +84,4 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb,
} // namespace Slic3r::GCodeThumbnails
#endif // slic3r_GCodeThumbnails_hpp_
#endif // slic3r_GCodeThumbnails_hpp_
+10 -2
View File
@@ -738,8 +738,16 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume()
const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON);
// Extract purging volumes for each extruder pair:
std::vector<std::vector<float>> wipe_volumes;
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
if (m_print_config_ptr->purge_in_prime_tower) {
for (unsigned int i = 0; i < number_of_extruders; ++i)
wipe_volumes.push_back(
std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
} else {
// populate wipe_volumes with prime_volume
for (unsigned int i = 0; i < number_of_extruders; ++i) {
wipe_volumes.push_back(std::vector<float>(number_of_extruders, m_print_config_ptr->prime_volume));
}
}
unsigned int current_extruder_id = -1;
for (int i = 0; i < m_layer_tools.size(); ++i) {
+6 -5
View File
@@ -1021,7 +1021,6 @@ void WipeTower2::toolchange_Change(
// This is where we want to place the custom gcodes. We will use placeholders for this.
// These will be substituted by the actual gcodes when the gcode is generated.
writer.append("[filament_end_gcode]\n");
writer.append("[change_filament_gcode]\n");
// Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc)
@@ -1093,7 +1092,8 @@ void WipeTower2::toolchange_Wipe(
float dy = (is_first_layer() ? 1.f : m_extra_spacing) * m_perimeter_width; // Don't use the extra spacing for the first layer.
// All the calculations in all other places take the spacing into account for all the layers.
const float target_speed = is_first_layer() ? m_first_layer_speed * 60.f : m_infill_speed * 60.f;
// If spare layers are excluded->if 1 or less toolchange has been done, it must be sill the first layer, too.So slow down.
const float target_speed = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers) ? m_first_layer_speed * 60.f : std::min(5400.f, m_infill_speed * 60.f);
float wipe_speed = 0.33f * target_speed;
// if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
@@ -1161,9 +1161,10 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
float feedrate = first_layer ? m_first_layer_speed * 60.f : m_infill_speed * 60.f;
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
// If spare layers are excluded -> if 1 or less toolchange has been done, it must be still the first layer, too. So slow down.
bool first_layer = is_first_layer() || (m_num_tool_changes <= 1 && m_no_sparse_layers);
float feedrate = first_layer ? m_first_layer_speed * 60.f : std::min(5400.f, m_infill_speed * 60.f);
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
WipeTower::box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth-(current_depth-m_perimeter_width)),
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
+141 -90
View File
@@ -1,3 +1,17 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/Geometry.pm:
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Jose Luis Perez Diez
///|/ Copyright (c) 2013 Anders Sundman
///|/ Copyright (c) 2013 Jesse Vincent
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r.h"
#include "Exception.hpp"
#include "Geometry.hpp"
@@ -320,46 +334,103 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation,
return transform;
}
void assemble_transform(Transform3d& transform, const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror)
{
transform = translation * rotation * scale * mirror;
}
Transform3d assemble_transform(const Transform3d& translation, const Transform3d& rotation, const Transform3d& scale, const Transform3d& mirror)
{
Transform3d transform;
assemble_transform(transform, translation, rotation, scale, mirror);
return transform;
}
void translation_transform(Transform3d& transform, const Vec3d& translation)
{
transform = Transform3d::Identity();
transform.translate(translation);
}
Transform3d translation_transform(const Vec3d& translation)
{
Transform3d transform;
translation_transform(transform, translation);
return transform;
}
void rotation_transform(Transform3d& transform, const Vec3d& rotation)
{
transform = Transform3d::Identity();
transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX()));
}
Transform3d rotation_transform(const Vec3d& rotation)
{
Transform3d transform;
rotation_transform(transform, rotation);
return transform;
}
void scale_transform(Transform3d& transform, double scale)
{
return scale_transform(transform, scale * Vec3d::Ones());
}
void scale_transform(Transform3d& transform, const Vec3d& scale)
{
transform = Transform3d::Identity();
transform.scale(scale);
}
Transform3d scale_transform(double scale)
{
return scale_transform(scale * Vec3d::Ones());
}
Transform3d scale_transform(const Vec3d& scale)
{
Transform3d transform;
scale_transform(transform, scale);
return transform;
}
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
{
// reference: http://www.gregslabaugh.net/publications/euler.pdf
Vec3d angles1 = Vec3d::Zero();
Vec3d angles2 = Vec3d::Zero();
// BBS: rotation_matrix(2, 0) may be slighterly larger than 1 due to numerical accuracy
if (std::abs(std::abs(rotation_matrix(2, 0)) - 1.0) < 1e-5 || std::abs(rotation_matrix(2, 0))>1)
{
angles1(2) = 0.0;
if (rotation_matrix(2, 0) < 0.0) // == -1.0
{
angles1(1) = 0.5 * (double)PI;
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
if (std::abs(std::abs(rotation_matrix(2, 0)) - 1.0) < 1e-5 || std::abs(rotation_matrix(2, 0))>1) {
angles1.z() = 0.0;
if (rotation_matrix(2, 0) < 0.0) { // == -1.0
angles1.y() = 0.5 * double(PI);
angles1.x() = angles1.z() + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
}
else // == 1.0
{
angles1(1) = - 0.5 * (double)PI;
angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
else { // == 1.0
angles1.y() = - 0.5 * double(PI);
angles1.x() = - angles1.y() + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
}
angles2 = angles1;
}
else
{
angles1(1) = -::asin(rotation_matrix(2, 0));
double inv_cos1 = 1.0 / ::cos(angles1(1));
angles1(0) = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1);
angles1(2) = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1);
else {
angles1.y() = -::asin(rotation_matrix(2, 0));
const double inv_cos1 = 1.0 / ::cos(angles1.y());
angles1.x() = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1);
angles1.z() = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1);
angles2(1) = (double)PI - angles1(1);
double inv_cos2 = 1.0 / ::cos(angles2(1));
angles2(0) = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2);
angles2(2) = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2);
angles2.y() = double(PI) - angles1.y();
const double inv_cos2 = 1.0 / ::cos(angles2.y());
angles2.x() = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2);
angles2.z() = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2);
}
// The following euristic is the best found up to now (in the sense that it works fine with the greatest number of edge use-cases)
// but there are other use-cases were it does not
// We need to improve it
double min_1 = angles1.cwiseAbs().minCoeff();
double min_2 = angles2.cwiseAbs().minCoeff();
bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm()));
const double min_1 = angles1.cwiseAbs().minCoeff();
const double min_2 = angles2.cwiseAbs().minCoeff();
const bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm()));
return use_1 ? angles1 : angles2;
}
@@ -375,6 +446,14 @@ Vec3d extract_euler_angles(const Transform3d& transform)
return extract_euler_angles(m);
}
static Transform3d extract_rotation_matrix(const Transform3d& trafo)
{
Matrix3d rotation;
Matrix3d scale;
trafo.computeRotationScaling(&rotation, &scale);
return Transform3d(rotation);
}
void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix)
{
double epsilon = 1e-5;
@@ -409,28 +488,6 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl
}
}
Transform3d translation_transform(const Vec3d &translation)
{
Transform3d transform = Transform3d::Identity();
transform.translate(translation);
return transform;
}
Transform3d rotation_transform(const Vec3d& rotation)
{
Transform3d transform = Transform3d::Identity();
transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX()));
return transform;
}
Transformation::Flags::Flags()
: dont_translate(true)
, dont_rotate(true)
, dont_scale(true)
, dont_mirror(true)
{
}
bool Transformation::Flags::needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
{
return (this->dont_translate != dont_translate) || (this->dont_rotate != dont_rotate) || (this->dont_scale != dont_scale) || (this->dont_mirror != dont_mirror);
@@ -456,35 +513,38 @@ Transformation::Transformation(const Transform3d& transform)
void Transformation::set_offset(const Vec3d& offset)
{
set_offset(X, offset(0));
set_offset(Y, offset(1));
set_offset(Z, offset(2));
set_offset(X, offset.x());
set_offset(Y, offset.y());
set_offset(Z, offset.z());
}
void Transformation::set_offset(Axis axis, double offset)
{
if (m_offset(axis) != offset)
{
if (m_offset(axis) != offset) {
m_offset(axis) = offset;
m_dirty = true;
}
}
Transform3d Transformation::get_rotation_matrix() const
{
return extract_rotation_matrix(m_matrix);
}
void Transformation::set_rotation(const Vec3d& rotation)
{
set_rotation(X, rotation(0));
set_rotation(Y, rotation(1));
set_rotation(Z, rotation(2));
set_rotation(X, rotation.x());
set_rotation(Y, rotation.y());
set_rotation(Z, rotation.z());
}
void Transformation::set_rotation(Axis axis, double rotation)
{
rotation = angle_to_0_2PI(rotation);
if (is_approx(std::abs(rotation), 2.0 * (double)PI))
if (is_approx(std::abs(rotation), 2.0 * double(PI)))
rotation = 0.0;
if (m_rotation(axis) != rotation)
{
if (m_rotation(axis) != rotation) {
m_rotation(axis) = rotation;
m_dirty = true;
}
@@ -492,15 +552,14 @@ void Transformation::set_rotation(Axis axis, double rotation)
void Transformation::set_scaling_factor(const Vec3d& scaling_factor)
{
set_scaling_factor(X, scaling_factor(0));
set_scaling_factor(Y, scaling_factor(1));
set_scaling_factor(Z, scaling_factor(2));
set_scaling_factor(X, scaling_factor.x());
set_scaling_factor(Y, scaling_factor.y());
set_scaling_factor(Z, scaling_factor.z());
}
void Transformation::set_scaling_factor(Axis axis, double scaling_factor)
{
if (m_scaling_factor(axis) != std::abs(scaling_factor))
{
if (m_scaling_factor(axis) != std::abs(scaling_factor)) {
m_scaling_factor(axis) = std::abs(scaling_factor);
m_dirty = true;
}
@@ -508,9 +567,9 @@ void Transformation::set_scaling_factor(Axis axis, double scaling_factor)
void Transformation::set_mirror(const Vec3d& mirror)
{
set_mirror(X, mirror(0));
set_mirror(Y, mirror(1));
set_mirror(Z, mirror(2));
set_mirror(X, mirror.x());
set_mirror(Y, mirror.y());
set_mirror(Z, mirror.z());
}
void Transformation::set_mirror(Axis axis, double mirror)
@@ -521,8 +580,7 @@ void Transformation::set_mirror(Axis axis, double mirror)
else if (abs_mirror != 1.0)
mirror /= abs_mirror;
if (m_mirror(axis) != mirror)
{
if (m_mirror(axis) != mirror) {
m_mirror(axis) = mirror;
m_dirty = true;
}
@@ -540,9 +598,8 @@ void Transformation::set_from_transform(const Transform3d& transform)
// we can only detect if the matrix contains a left handed reference system
// in which case we reorient it back to right handed by mirroring the x axis
Vec3d mirror = Vec3d::Ones();
if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0)
{
mirror(0) = -1.0;
if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) {
mirror.x() = -1.0;
// remove mirror
m3x3.col(0) *= -1.0;
}
@@ -579,8 +636,7 @@ void Transformation::reset()
const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
{
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
{
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror)) {
m_matrix = Geometry::assemble_transform(
dont_translate ? Vec3d::Zero() : m_offset,
dont_rotate ? Vec3d::Zero() : m_rotation,
@@ -609,8 +665,7 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
// Just set the inverse.
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
}
else if (is_rotation_ninety_degrees(instance_transformation.get_rotation()))
{
else if (is_rotation_ninety_degrees(instance_transformation.get_rotation())) {
// Anisotropic scaling, rotation by multiples of ninety degrees.
Eigen::Matrix3d instance_rotation_trafo =
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
@@ -643,8 +698,8 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
out.set_scaling_factor(Vec3d(std::abs(scale.x()), std::abs(scale.y()), std::abs(scale.z())));
out.set_mirror(Vec3d(scale.x() > 0 ? 1. : -1, scale.y() > 0 ? 1. : -1, scale.z() > 0 ? 1. : -1));
}
else
{
@@ -663,19 +718,15 @@ Transform3d transform3d_from_string(const std::string& transform_str)
assert(is_decimal_separator_point()); // for atof
Transform3d transform = Transform3d::Identity();
if (!transform_str.empty())
{
if (!transform_str.empty()) {
std::vector<std::string> mat_elements_str;
boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on);
unsigned int size = (unsigned int)mat_elements_str.size();
if (size == 16)
{
const unsigned int size = (unsigned int)mat_elements_str.size();
if (size == 16) {
unsigned int i = 0;
for (unsigned int r = 0; r < 4; ++r)
{
for (unsigned int c = 0; c < 4; ++c)
{
for (unsigned int r = 0; r < 4; ++r) {
for (unsigned int c = 0; c < 4; ++c) {
transform(r, c) = ::atof(mat_elements_str[i++].c_str());
}
}
@@ -689,17 +740,17 @@ Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot
{
return
// From the current coordinate system to world.
Eigen::AngleAxisd(rot_xyz_to(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to(0), Vec3d::UnitX()) *
Eigen::AngleAxisd(rot_xyz_to.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rot_xyz_to.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rot_xyz_to.x(), Vec3d::UnitX()) *
// From world to the initial coordinate system.
Eigen::AngleAxisd(-rot_xyz_from(0), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from(1), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from(2), Vec3d::UnitZ());
Eigen::AngleAxisd(-rot_xyz_from.x(), Vec3d::UnitX()) * Eigen::AngleAxisd(-rot_xyz_from.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(-rot_xyz_from.z(), Vec3d::UnitZ());
}
// This should only be called if it is known, that the two rotations only differ in rotation around the Z axis.
double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
{
Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
Vec3d axis = angle_axis.axis();
double angle = angle_axis.angle();
const Eigen::AngleAxisd angle_axis(rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
const Vec3d axis = angle_axis.axis();
const double angle = angle_axis.angle();
#ifndef NDEBUG
if (std::abs(angle) > 1e-8) {
assert(std::abs(axis.x()) < 1e-8);
+70 -25
View File
@@ -1,3 +1,18 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Lukáš Hejl @hejllukas
///|/ Copyright (c) 2017 Eyal Soha @eyal0
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/Geometry.pm:
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Jose Luis Perez Diez
///|/ Copyright (c) 2013 Anders Sundman
///|/ Copyright (c) 2013 Jesse Vincent
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Geometry_hpp_
#define slic3r_Geometry_hpp_
@@ -324,7 +339,8 @@ bool arrange(
// 4) rotate Y
// 5) rotate Z
// 6) translate
void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(),
const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
// Returns the transform obtained by assembling the given transformations in the following order:
// 1) mirror
@@ -333,7 +349,45 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation = Vec3d
// 4) rotate Y
// 5) rotate Z
// 6) translate
Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(), const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
Transform3d assemble_transform(const Vec3d& translation = Vec3d::Zero(), const Vec3d& rotation = Vec3d::Zero(),
const Vec3d& scale = Vec3d::Ones(), const Vec3d& mirror = Vec3d::Ones());
// Sets the given transform by multiplying the given transformations in the following order:
// T = translation * rotation * scale * mirror
void assemble_transform(Transform3d& transform, const Transform3d& translation = Transform3d::Identity(),
const Transform3d& rotation = Transform3d::Identity(), const Transform3d& scale = Transform3d::Identity(),
const Transform3d& mirror = Transform3d::Identity());
// Returns the transform obtained by multiplying the given transformations in the following order:
// T = translation * rotation * scale * mirror
Transform3d assemble_transform(const Transform3d& translation = Transform3d::Identity(), const Transform3d& rotation = Transform3d::Identity(),
const Transform3d& scale = Transform3d::Identity(), const Transform3d& mirror = Transform3d::Identity());
// Sets the given transform by assembling the given translation
void translation_transform(Transform3d& transform, const Vec3d& translation);
// Returns the transform obtained by assembling the given translation
Transform3d translation_transform(const Vec3d& translation);
// Sets the given transform by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
void rotation_transform(Transform3d& transform, const Vec3d& rotation);
// Returns the transform obtained by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
Transform3d rotation_transform(const Vec3d& rotation);
// Sets the given transform by assembling the given scale factors
void scale_transform(Transform3d& transform, double scale);
void scale_transform(Transform3d& transform, const Vec3d& scale);
// Returns the transform obtained by assembling the given scale factors
Transform3d scale_transform(double scale);
Transform3d scale_transform(const Vec3d& scale);
// Returns the euler angles extracted from the given rotation matrix
// Warning -> The matrix should not contain any scale or shear !!!
@@ -346,40 +400,29 @@ Vec3d extract_euler_angles(const Transform3d& transform);
// get rotation from two vectors.
// Default output is axis-angle. If rotation_matrix pointer is provided, also output rotation matrix
// Euler angles can be obtained by extract_euler_angles()
void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix = nullptr);
// Returns the transform obtained by assembling the given translation
Transform3d translation_transform(const Vec3d &translation);
// Returns the transform obtained by assembling the given rotations in the following order:
// 1) rotate X
// 2) rotate Y
// 3) rotate Z
Transform3d rotation_transform(const Vec3d &rotation);
void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d &rotation_axis, double &phi, Matrix3d *rotation_matrix = nullptr);
class Transformation
{
struct Flags
{
bool dont_translate;
bool dont_rotate;
bool dont_scale;
bool dont_mirror;
Flags();
bool dont_translate{ true };
bool dont_rotate{ true };
bool dont_scale{ true };
bool dont_mirror{ true };
bool needs_update(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const;
void set(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror);
};
Vec3d m_offset; // In unscaled coordinates
Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point
Vec3d m_scaling_factor; // Scaling factors along the three axes
Vec3d m_mirror; // Mirroring along the three axes
Vec3d m_offset{ Vec3d::Zero() }; // In unscaled coordinates
Vec3d m_rotation{ Vec3d::Zero() }; // Rotation around the three axes, in radians around mesh center point
Vec3d m_scaling_factor{ Vec3d::Ones() }; // Scaling factors along the three axes
Vec3d m_mirror{ Vec3d::Ones() }; // Mirroring along the three axes
mutable Transform3d m_matrix;
mutable Transform3d m_matrix{ Transform3d::Identity() };
mutable Flags m_flags;
mutable bool m_dirty;
mutable bool m_dirty{ false };
public:
Transformation();
@@ -397,6 +440,8 @@ public:
const Vec3d& get_rotation() const { return m_rotation; }
double get_rotation(Axis axis) const { return m_rotation(axis); }
Transform3d get_rotation_matrix() const;
void set_rotation(const Vec3d& rotation);
void set_rotation(Axis axis, double rotation);
@@ -457,7 +502,7 @@ extern double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to
// Is the angle close to a multiple of 90 degrees?
inline bool is_rotation_ninety_degrees(double a)
{
a = fmod(std::abs(a), 0.5 * M_PI);
a = fmod(std::abs(a), 0.5 * PI);
if (a > 0.25 * PI)
a = 0.5 * PI - a;
return a < 0.001;
+7 -1
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Circle.hpp"
#include "../Polygon.hpp"
@@ -108,7 +112,7 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
return out;
}
Circled circle_ransac(const Vec2ds& input, size_t iterations)
Circled circle_ransac(const Vec2ds& input, size_t iterations, double* min_error)
{
if (input.size() < 3)
return Circled::make_invalid();
@@ -132,6 +136,8 @@ Circled circle_ransac(const Vec2ds& input, size_t iterations)
circle_best = c;
}
}
if (min_error)
*min_error = err_min;
return circle_best;
}
+5 -1
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Geometry_Circle_hpp_
#define slic3r_Geometry_Circle_hpp_
@@ -102,7 +106,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
// Find circle using RANSAC randomized algorithm.
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20);
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20, double* min_error = nullptr);
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
template<typename Vector, typename Points>
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
///|/ Copyright (c) Prusa Research 2022 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_Measure_hpp_
#define Slic3r_Measure_hpp_
#include <optional>
#include <memory>
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
Edge = 1 << 1,
Circle = 1 << 2,
Plane = 1 << 3
};
class SurfaceFeature {
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
// For points, return the point.
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
// For edges, return start and end.
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
// For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point.
std::tuple<int, Vec3d, Vec3d> get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); }
// For anything, return an extra point that should also be considered a part of this.
std::optional<Vec3d> get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; }
bool operator == (const SurfaceFeature& other) const {
if (this->m_type != other.m_type) return false;
switch (this->m_type)
{
case SurfaceFeatureType::Undef: { break; }
case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); }
case SurfaceFeatureType::Edge: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) ||
(this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1));
}
case SurfaceFeatureType::Plane:
case SurfaceFeatureType::Circle: {
return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON);
}
}
return false;
}
bool operator != (const SurfaceFeature& other) const {
return !operator == (other);
}
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
Vec3d m_pt2{ Vec3d::Zero() };
std::optional<Vec3d> m_pt3;
double m_value{ 0.0 };
};
class MeasuringImpl;
class Measuring {
public:
// Construct the measurement object on a given its.
explicit Measuring(const indexed_triangle_set& its);
~Measuring();
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
// Return total number of planes.
int get_num_of_planes() const;
// Returns a list of triangle indices for given plane.
const std::vector<int>& get_plane_triangle_indices(int idx) const;
// Returns the surface features of the plane with the given index
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id) const;
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
std::unique_ptr<MeasuringImpl> priv;
};
struct DistAndPoints {
DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {}
double dist;
Vec3d from;
Vec3d to;
};
struct AngleAndEdges {
AngleAndEdges(double angle_, const Vec3d& center_, const std::pair<Vec3d, Vec3d>& e1_, const std::pair<Vec3d, Vec3d>& e2_, double radius_, bool coplanar_)
: angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {}
double angle;
Vec3d center;
std::pair<Vec3d, Vec3d> e1;
std::pair<Vec3d, Vec3d> e2;
double radius;
bool coplanar;
static const AngleAndEdges Dummy;
};
struct MeasurementResult {
std::optional<AngleAndEdges> angle;
std::optional<DistAndPoints> distance_infinite;
std::optional<DistAndPoints> distance_strict;
std::optional<Vec3d> distance_xyz;
bool has_distance_data() const {
return distance_infinite.has_value() || distance_strict.has_value();
}
bool has_any_data() const {
return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value();
}
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
inline Vec3d edge_direction(const SurfaceFeature& edge) {
assert(edge.get_type() == SurfaceFeatureType::Edge);
return edge_direction(edge.get_edge());
}
inline Vec3d plane_normal(const SurfaceFeature& plane) {
assert(plane.get_type() == SurfaceFeatureType::Plane);
return std::get<1>(plane.get_plane());
}
inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; }
inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; }
inline bool are_parallel(const std::pair<Vec3d, Vec3d>& e1, const std::pair<Vec3d, Vec3d>& e2) {
return are_parallel(e1.second - e1.first, e2.second - e2.first);
}
inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_parallel(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_perpendicular(edge_direction(f1), plane_normal(f2));
else
return false;
}
inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) {
if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge)
return are_perpendicular(edge_direction(f1), edge_direction(f2));
else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane)
return are_parallel(edge_direction(f1), plane_normal(f2));
else
return false;
}
} // namespace Measure
} // namespace Slic3r
#endif // Slic3r_Measure_hpp_
+390
View File
@@ -0,0 +1,390 @@
///|/ Copyright (c) Prusa Research 2022 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef Slic3r_MeasureUtils_hpp_
#define Slic3r_MeasureUtils_hpp_
#include <initializer_list>
namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
{
public:
Polynomial1(std::initializer_list<double> values)
{
// C++ 11 will call the default constructor for
// Polynomial1<Real> p{}, so it is guaranteed that
// values.size() > 0.
m_coefficient.resize(values.size());
std::copy(values.begin(), values.end(), m_coefficient.begin());
EliminateLeadingZeros();
}
// Construction and destruction. The first constructor creates a
// polynomial of the specified degree but sets all coefficients to
// zero (to ensure initialization). You are responsible for setting
// the coefficients, presumably with the degree-term set to a nonzero
// number. In the second constructor, the degree is the number of
// initializers plus 1, but then adjusted so that coefficient[degree]
// is not zero (unless all initializer values are zero).
explicit Polynomial1(uint32_t degree)
: m_coefficient(static_cast<size_t>(degree) + 1, 0.0)
{}
// Eliminate any leading zeros in the polynomial, except in the case
// the degree is 0 and the coefficient is 0. The elimination is
// necessary when arithmetic operations cause a decrease in the degree
// of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) =
// (2 + 3*x). The inputs both have degree 2, so the result is created
// with degree 2. After the addition we find that the degree is in
// fact 1 and resize the array of coefficients. This function is
// called internally by the arithmetic operators, but it is exposed in
// the public interface in case you need it for your own purposes.
void EliminateLeadingZeros()
{
const size_t size = m_coefficient.size();
if (size > 1) {
const double zero = 0.0;
int32_t leading;
for (leading = static_cast<int32_t>(size) - 1; leading > 0; --leading) {
if (m_coefficient[leading] != zero)
break;
}
m_coefficient.resize(++leading);
}
}
// Set all coefficients to the specified value.
void SetCoefficients(double value)
{
std::fill(m_coefficient.begin(), m_coefficient.end(), value);
}
inline uint32_t GetDegree() const
{
// By design, m_coefficient.size() > 0.
return static_cast<uint32_t>(m_coefficient.size() - 1);
}
inline const double& operator[](uint32_t i) const { return m_coefficient[i]; }
inline double& operator[](uint32_t i) { return m_coefficient[i]; }
// Evaluate the polynomial. If the polynomial is invalid, the
// function returns zero.
double operator()(double t) const
{
int32_t i = static_cast<int32_t>(m_coefficient.size());
double result = m_coefficient[--i];
for (--i; i >= 0; --i) {
result *= t;
result += m_coefficient[i];
}
return result;
}
protected:
// The class is designed so that m_coefficient.size() >= 1.
std::vector<double> m_coefficient;
};
inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
Polynomial1 result(p0Degree + p1Degree);
result.SetCoefficients(0.0);
for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) {
for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) {
result[i0 + i1] += p0[i0] * p1[i1];
}
}
return result;
}
inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] + p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1)
{
const uint32_t p0Degree = p0.GetDegree();
const uint32_t p1Degree = p1.GetDegree();
uint32_t i;
if (p0Degree >= p1Degree) {
Polynomial1 result(p0Degree);
for (i = 0; i <= p1Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p0Degree; ++i) {
result[i] = p0[i];
}
result.EliminateLeadingZeros();
return result;
}
else {
Polynomial1 result(p1Degree);
for (i = 0; i <= p0Degree; ++i) {
result[i] = p0[i] - p1[i];
}
for (/**/; i <= p1Degree; ++i) {
result[i] = -p1[i];
}
result.EliminateLeadingZeros();
return result;
}
}
inline Polynomial1 operator * (double scalar, const Polynomial1& p)
{
const uint32_t degree = p.GetDegree();
Polynomial1 result(degree);
for (uint32_t i = 0; i <= degree; ++i) {
result[i] = scalar * p[i];
}
return result;
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
{
public:
// General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c'
// must have at least d+1 elements and the output array 'root' must
// have at least d elements.
// Find the roots on (-infinity,+infinity).
static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots)
{
if (degree >= 0 && c != nullptr) {
const double zero = 0.0;
while (degree >= 0 && c[degree] == zero) {
--degree;
}
if (degree > 0) {
// Compute the Cauchy bound.
const double one = 1.0;
const double invLeading = one / c[degree];
double maxValue = zero;
for (int32_t i = 0; i < degree; ++i) {
const double value = std::fabs(c[i] * invLeading);
if (value > maxValue)
maxValue = value;
}
const double bound = one + maxValue;
return FindRecursive(degree, c, -bound, bound, maxIterations, roots);
}
else if (degree == 0)
// The polynomial is a nonzero constant.
return 0;
else {
// The polynomial is identically zero.
roots[0] = zero;
return 1;
}
}
else
// Invalid degree or c.
return 0;
}
// If you know that p(tmin) * p(tmax) <= 0, then there must be at
// least one root in [tmin, tmax]. Compute it using bisection.
static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root)
{
const double zero = 0.0;
double pmin = Evaluate(degree, c, tmin);
if (pmin == zero) {
root = tmin;
return true;
}
double pmax = Evaluate(degree, c, tmax);
if (pmax == zero) {
root = tmax;
return true;
}
if (pmin * pmax > zero)
// It is not known whether the interval bounds a root.
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
root = 0.5 * (tmin + tmax);
// This test is designed for 'float' or 'double' when tmin
// and tmax are consecutive floating-point numbers.
if (root == tmin || root == tmax)
break;
const double p = Evaluate(degree, c, root);
const double product = p * pmin;
if (product < zero) {
tmax = root;
pmax = p;
}
else if (product > zero) {
tmin = root;
pmin = p;
}
else
break;
}
return true;
}
// Support for the Find functions.
static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots)
{
// The base of the recursion.
const double zero = 0.0;
double root = zero;
if (degree == 1) {
int32_t numRoots;
if (c[1] != zero) {
root = -c[0] / c[1];
numRoots = 1;
}
else if (c[0] == zero) {
root = zero;
numRoots = 1;
}
else
numRoots = 0;
if (numRoots > 0 && tmin <= root && root <= tmax) {
roots[0] = root;
return 1;
}
return 0;
}
// Find the roots of the derivative polynomial scaled by 1/degree.
// The scaling avoids the factorial growth in the coefficients;
// for example, without the scaling, the high-order term x^d
// becomes (d!)*x through multiple differentiations. With the
// scaling we instead get x. This leads to better numerical
// behavior of the root finder.
const int32_t derivDegree = degree - 1;
std::vector<double> derivCoeff(static_cast<size_t>(derivDegree) + 1);
std::vector<double> derivRoots(derivDegree);
for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) {
derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree;
}
const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]);
int32_t numRoots = 0;
if (numDerivRoots > 0) {
// Find root on [tmin,derivRoots[0]].
if (Find(degree, c, tmin, derivRoots[0], maxIterations, root))
roots[numRoots++] = root;
// Find root on [derivRoots[i],derivRoots[i+1]].
for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) {
if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root))
roots[numRoots++] = root;
}
// Find root on [derivRoots[numDerivRoots-1],tmax].
if (Find(degree, c, derivRoots[static_cast<size_t>(numDerivRoots) - 1], tmax, maxIterations, root))
roots[numRoots++] = root;
}
else {
// The polynomial is monotone on [tmin,tmax], so has at most one root.
if (Find(degree, c, tmin, tmax, maxIterations, root))
roots[numRoots++] = root;
}
return numRoots;
}
static double Evaluate(int32_t degree, const double* c, double t)
{
int32_t i = degree;
double result = c[i];
while (--i >= 0) {
result = t * result + c[i];
}
return result;
}
};
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If
// the maximum absolute component occurs at index i, then the orthogonal
// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components
// zero. The index addition i+1 is computed modulo N.
inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength)
{
double cmax = std::fabs(v[0]);
int32_t imax = 0;
for (int32_t i = 1; i < 3; ++i) {
double c = std::fabs(v[i]);
if (c > cmax) {
cmax = c;
imax = i;
}
}
Vec3d result = Vec3d::Zero();
int32_t inext = imax + 1;
if (inext == 3)
inext = 0;
result[imax] = v[inext];
result[inext] = -v[imax];
if (unitLength) {
const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext];
const double invLength = 1.0 / std::sqrt(sqrDistance);
result[imax] *= invLength;
result[inext] *= invLength;
}
return result;
}
} // namespace Slic3r
} // namespace Measure
#endif // Slic3r_MeasureUtils_hpp_
+26 -516
View File
@@ -1,3 +1,16 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Boleslaw Ciesielski
///|/ Copyright (c) 2019 John Drake @foxox
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
///|/
///|/ ported from lib/Slic3r/Model.pm:
///|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966
///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Model.hpp"
#include "libslic3r.h"
#include "BuildVolume.hpp"
@@ -1693,68 +1706,6 @@ bool ModelObject::has_connectors() const
return false;
}
indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes)
{
indexed_triangle_set connector_mesh;
int sectorCount {1};
switch (CutConnectorShape(connector_attributes.shape)) {
case CutConnectorShape::Triangle:
sectorCount = 3;
break;
case CutConnectorShape::Square:
sectorCount = 4;
break;
case CutConnectorShape::Circle:
sectorCount = 360;
break;
case CutConnectorShape::Hexagon:
sectorCount = 6;
break;
default:
break;
}
if (connector_attributes.style == CutConnectorStyle::Prizm)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
return connector_mesh;
}
void ModelObject::apply_cut_connectors(const std::string &name)
{
if (cut_connectors.empty())
return;
using namespace Geometry;
size_t connector_id = cut_id.connectors_cnt();
for (const CutConnector &connector : cut_connectors) {
TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs));
// Mesh will be centered when loading.
ModelVolume *new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME);
Transform3d translate_transform = Transform3d::Identity();
translate_transform.translate(connector.pos);
Transform3d scale_transform = Transform3d::Identity();
scale_transform.scale(Vec3f(connector.radius, connector.radius, connector.height).cast<double>());
// Transform the new modifier to be aligned inside the instance
new_volume->set_transformation(translate_transform * connector.rotation_m * scale_transform);
new_volume->cut_info = {connector.attribs.type, connector.radius, connector.height, connector.radius_tolerance, connector.height_tolerance};
new_volume->name = name + "-" + std::to_string(++connector_id);
}
cut_id.increase_connectors_cnt(cut_connectors.size());
// delete all connectors
cut_connectors.clear();
}
void ModelObject::invalidate_cut()
{
this->cut_id.invalidate();
@@ -1770,43 +1721,10 @@ void ModelObject::delete_connectors()
}
}
void ModelObject::synchronize_model_after_cut()
{
for (ModelObject *obj : m_model->objects) {
if (obj == this || obj->cut_id.is_equal(this->cut_id)) continue;
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
obj->cut_id.copy(this->cut_id);
}
}
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
{
// we don't save cut information, if result will not contains all parts of initial object
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) ||
!attributes.has(ModelObjectCutAttribute::KeepLower) ||
attributes.has(ModelObjectCutAttribute::InvalidateCutInfo))
return;
if (cut_id.id().invalid())
cut_id.init();
{
int cut_obj_cnt = -1;
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::KeepLower))
cut_obj_cnt++;
if (attributes.has(ModelObjectCutAttribute::CreateDowels))
cut_obj_cnt++;
if (cut_obj_cnt > 0)
cut_id.increase_check_sum(size_t(cut_obj_cnt));
}
}
void ModelObject::clone_for_cut(ModelObject **obj)
{
(*obj) = ModelObject::new_clone(*this);
(*obj)->set_model(nullptr);
(*obj)->set_model(this->get_model());
(*obj)->sla_support_points.clear();
(*obj)->sla_drain_holes.clear();
(*obj)->sla_points_status = sla::PointsStatus::NoPoints;
@@ -1814,189 +1732,11 @@ void ModelObject::clone_for_cut(ModelObject **obj)
(*obj)->input_file.clear();
}
Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array<Vec3d, 4>& plane_points)
void ModelVolume::reset_extra_facets()
{
Vec3d mid_point = {0.0, 0.0, 0.0};
for (auto pt : plane_points)
mid_point += pt;
mid_point /= (double) plane_points.size();
Vec3d movement = -mid_point;
Vec3d v01 = plane_points[1] - plane_points[0];
Vec3d v12 = plane_points[2] - plane_points[1];
Vec3d plane_normal = v01.cross(v12);
plane_normal.normalize();
Vec3d axis = {0.0, 0.0, 0.0};
double phi = 0.0;
Matrix3d matrix;
matrix.setIdentity();
Geometry::rotation_from_two_vectors(plane_normal, {0.0, 0.0, 1.0}, axis, phi, &matrix);
Vec3d angles = Geometry::extract_euler_angles(matrix);
movement = matrix * movement;
Transform3d transfo;
transfo.setIdentity();
transfo.translate(movement);
transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX()));
return transfo;
}
void ModelObject::process_connector_cut(
ModelVolume *volume,
const Transform3d & instance_matrix,
const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject *upper, ModelObject *lower,
std::vector<ModelObject *> &dowels,
Vec3d &local_dowels_displace)
{
assert(volume->cut_info.is_connector);
volume->cut_info.set_processed();
const auto volume_matrix = volume->get_matrix();
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (volume->cut_info.connector_type != CutConnectorType::Dowel) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume *vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume *vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
}
else {
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject *dowel{nullptr};
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume *vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
dowels.push_back(dowel);
}
// Cut the dowel
volume->apply_tolerance();
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, Transform3d::Identity(), cut_matrix, attributes, upper_mesh, lower_mesh);
// add small Z offset to better preview
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
// Add cut parts to the related objects
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
}
}
void ModelObject::process_modifier_cut(
ModelVolume *volume,
const Transform3d &instance_matrix,
const Transform3d &inverse_cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject *upper,
ModelObject *lower)
{
const auto volume_matrix = instance_matrix * volume->get_matrix();
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(volume_matrix));
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut))
upper->add_volume(*volume);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut))
lower->add_volume(*volume);
}
void ModelObject::process_volume_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes,
TriangleMesh & upper_mesh,
TriangleMesh & lower_mesh)
{
const auto volume_matrix = volume->get_matrix();
using namespace Geometry;
const Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix);
const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).inverse()
* translation_transform(-1 * cut_transformation.get_offset());
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
void ModelObject::process_solid_part_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
const std::array<Vec3d, 4> &plane_points,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
Vec3d & local_displace)
{
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
if (attributes.has(ModelObjectCutAttribute::CutToParts)) {
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A");
add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B");
return;
}
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
add_cut_volume(upper_mesh, upper, volume, cut_matrix);
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) {
add_cut_volume(lower_mesh, lower, volume, cut_matrix);
// Compute the displacement (in instance coordinates) to be applied to place the upper parts
// The upper part displacement is set to half of the lower part bounding box
// this is done in hope at least a part of the upper part will always be visible and draggable
local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0));
}
this->supported_facets.reset();
this->seam_facets.reset();
this->mmu_segmentation_facets.reset();
}
static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance)
@@ -2073,215 +1813,6 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan
}
}
// BBS: replace z with plane_points
ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes)
{
if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower))
return {};
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
// apply cut attributes for object
apply_cut_attributes(attributes);
ModelObject* upper{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
clone_for_cut(&upper);
ModelObject* lower{ nullptr };
if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts))
clone_for_cut(&lower);
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = Geometry::assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), // BBS: do apply Z-rotation
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
// BBS
//z -= instances[instance]->get_offset().z();
for (Vec3d& point : plane_points) {
point -= instances[instance]->get_offset();
}
Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
Transform3d cut_matrix = inverse_cut_matrix.inverse();
std::vector<ModelObject *> dowels;
// Displacement (in instance coordinates) to be applied to place the upper parts
Vec3d local_displace = Vec3d::Zero();
Vec3d local_dowels_displace = Vec3d::Zero();
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->supported_facets.reset();
volume->seam_facets.reset();
volume->mmu_segmentation_facets.reset();
if (! volume->is_model_part()) {
if (volume->cut_info.is_processed) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
//Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points);
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
}
else {
process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace);
}
}
else if (! volume->mesh().empty()) {
process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace);
}
}
ModelObjectPtrs res;
if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) {
reset_instance_transformation(upper, instance, cut_matrix);
res.push_back(upper);
}
else {
if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) {
reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper),
attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace);
res.push_back(upper);
}
if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) {
reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower),
attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower));
res.push_back(lower);
}
if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) {
for (auto dowel : dowels) {
reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace);
local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0));
dowel->name += "-Dowel-" + dowel->volumes[0]->name;
res.push_back(dowel);
}
}
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
synchronize_model_after_cut();
return res;
}
// BBS
ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders, double smoothing_alpha, int segment_number)
{
BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - start";
// Clone the object to duplicate instances, materials etc.
ModelObject* upper = ModelObject::new_clone(*this);
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->clear_volumes();
upper->input_file.clear();
// Because transformations are going to be applied to meshes directly,
// we reset transformation of all instances and volumes,
// except for translation and Z-rotation on instances, which are preserved
// in the transformation matrix and not applied to the mesh transform.
// const auto instance_matrix = instances[instance]->get_matrix(true);
const auto instance_matrix = Geometry::assemble_transform(
Vec3d::Zero(), // don't apply offset
instances[instance]->get_rotation(), // BBS: keep Z-rotation
instances[instance]->get_scaling_factor(),
instances[instance]->get_mirror()
);
for (ModelVolume* volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->supported_facets.reset();
volume->seam_facets.reset();
if (!volume->is_model_part()) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
upper->add_volume(*volume);
}
else if (!volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
auto mesh_segments = MeshBoolean::cgal::segment(mesh, smoothing_alpha, segment_number);
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
unsigned int extruder_counter = 0;
for (int idx=0;idx<mesh_segments.size();idx++)
{
auto& mesh_segment = mesh_segments[idx];
if (mesh_segment.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(mesh_segment);
vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_" + std::to_string(idx);
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
#if 0
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
#else
vol->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
#endif
}
}
}
}
ModelObjectPtrs res;
if (upper->volumes.size() > 0) {
upper->invalidate_bounding_box();
// Reset instance transformation except offset and Z-rotation
for (size_t i = 0; i < instances.size(); i++) {
auto& instance = upper->instances[i];
const Vec3d offset = instance->get_offset();
// BBS
//const double rot_z = instance->get_rotation()(2);
instance->set_transformation(Geometry::Transformation());
instance->set_offset(offset);
// BBS
//instance->set_rotation(Vec3d(0.0, 0.0, rot_z));
}
res.push_back(upper);
}
BOOST_LOG_TRIVIAL(trace) << "ModelObject::segment - end";
return res;
}
void ModelObject::split(ModelObjectPtrs* new_objects)
{
std::vector<TriangleMesh> all_meshes;
@@ -2758,6 +2289,14 @@ int ModelObject::get_repaired_errors_count(const int vol_idx /*= -1*/) const
stats.facets_reversed + stats.backwards_edges;
}
bool ModelObject::has_solid_mesh() const
{
for (const ModelVolume* volume : volumes)
if (volume->is_model_part())
return true;
return false;
}
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
@@ -2801,35 +2340,6 @@ bool ModelVolume::is_splittable() const
return m_is_splittable == 1;
}
void ModelVolume::apply_tolerance()
{
assert(cut_info.is_connector);
if (!cut_info.is_processed)
return;
Vec3d sf = get_scaling_factor();
// make a "hole" wider
double size_scale = 1.f;
if (abs(cut_info.radius - 0) < EPSILON) // For compatibility with old files
size_scale = 1.f + double(cut_info.radius_tolerance);
else
size_scale = (double(cut_info.radius) + double(cut_info.radius_tolerance)) / double(cut_info.radius);
sf[X] *= size_scale;
sf[Y] *= size_scale;
// make a "hole" dipper
double height_scale = 1.f;
if (abs(cut_info.height - 0) < EPSILON) // For compatibility with old files
height_scale = 1.f + double(cut_info.height_tolerance);
else
height_scale = (double(cut_info.height) + double(cut_info.height_tolerance)) / double(cut_info.height);
sf[Z] *= height_scale;
set_scaling_factor(sf);
}
// BBS
std::vector<int> ModelVolume::get_extruders() const
{
+86 -105
View File
@@ -246,79 +246,92 @@ private:
};
enum class CutConnectorType : int {
Plug,
Dowel,
Undef
Plug
, Dowel
, Snap
, Undef
};
enum class CutConnectorStyle : int {
Prizm,
Frustum,
Undef
Prism
, Frustum
, Undef
//,Claw
};
enum class CutConnectorShape : int {
Triangle,
Square,
Hexagon,
Circle,
Undef
Triangle
, Square
, Hexagon
, Circle
, Undef
//,D-shape
};
struct CutConnectorAttributes
{
CutConnectorType type{CutConnectorType::Plug};
CutConnectorStyle style{CutConnectorStyle::Prizm};
CutConnectorShape shape{CutConnectorShape::Circle};
CutConnectorType type{ CutConnectorType::Plug };
CutConnectorStyle style{ CutConnectorStyle::Prism };
CutConnectorShape shape{ CutConnectorShape::Circle };
CutConnectorAttributes() {}
CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) : type(t), style(st), shape(sh) {}
CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh)
: type(t), style(st), shape(sh)
{}
CutConnectorAttributes(const CutConnectorAttributes &rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {}
CutConnectorAttributes(const CutConnectorAttributes& rhs) :
CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {}
bool operator==(const CutConnectorAttributes &other) const;
bool operator==(const CutConnectorAttributes& other) const;
bool operator!=(const CutConnectorAttributes &other) const { return !(other == (*this)); }
bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); }
bool operator<(const CutConnectorAttributes &other) const
{
return this->type < other.type || (this->type == other.type && this->style < other.style) ||
(this->type == other.type && this->style == other.style && this->shape < other.shape);
bool operator<(const CutConnectorAttributes& other) const {
return this->type < other.type ||
(this->type == other.type && this->style < other.style) ||
(this->type == other.type && this->style == other.style && this->shape < other.shape);
}
template<class Archive> inline void serialize(Archive &ar) { ar(type, style, shape); }
template<class Archive> inline void serialize(Archive& ar) {
ar(type, style, shape);
}
};
struct CutConnector
{
Vec3d pos;
Transform3d rotation_m;
float radius;
float height;
float radius_tolerance; // [0.f : 1.f]
float height_tolerance; // [0.f : 1.f]
Vec3d pos;
Transform3d rotation_m;
float radius;
float height;
float radius_tolerance;// [0.f : 1.f]
float height_tolerance;// [0.f : 1.f]
float z_angle {0.f};
CutConnectorAttributes attribs;
CutConnector() : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {}
CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes)
CutConnector()
: pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f), z_angle(0.f)
{}
CutConnector(const CutConnector &rhs) : CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {}
CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, float za, CutConnectorAttributes attributes)
: pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), z_angle(za), attribs(attributes)
{}
bool operator==(const CutConnector &other) const;
CutConnector(const CutConnector& rhs) :
CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.z_angle, rhs.attribs) {}
bool operator!=(const CutConnector &other) const { return !(other == (*this)); }
bool operator==(const CutConnector& other) const;
template<class Archive> inline void serialize(Archive &ar) { ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); }
bool operator!=(const CutConnector& other) const { return !(other == (*this)); }
template<class Archive> inline void serialize(Archive& ar) {
ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, z_angle, attribs);
}
};
using CutConnectors = std::vector<CutConnector>;
// Declared outside of ModelVolume, so it could be forward declared.
enum class ModelVolumeType : int {
INVALID = -1,
@@ -326,13 +339,9 @@ enum class ModelVolumeType : int {
NEGATIVE_VOLUME,
PARAMETER_MODIFIER,
SUPPORT_BLOCKER,
SUPPORT_ENFORCER
SUPPORT_ENFORCER,
};
enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, CutToParts, InvalidateCutInfo };
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute);
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
@@ -371,6 +380,10 @@ public:
// Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes;
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
CutConnectors cut_connectors;
CutObjectBase cut_id;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment
@@ -380,10 +393,6 @@ public:
// BBS: save for compare with new load volumes
std::vector<ObjectID> volume_ids;
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
CutConnectors cut_connectors;
CutObjectBase cut_id;
Model* get_model() { return m_model; }
const Model* get_model() const { return m_model; }
// BBS: production extension
@@ -480,52 +489,13 @@ public:
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(const std::string &name);
// invalidate cut state for this object and its connectors/volumes
void invalidate_cut();
// delete volumes which are marked as connector for this object
void delete_connectors();
void synchronize_model_after_cut();
void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj);
Transform3d calculate_cut_plane_inverse_matrix(const std::array<Vec3d, 4> &plane_points);
void process_connector_cut(ModelVolume *volume,
const Transform3d & instance_matrix,
const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject *upper, ModelObject *lower,
std::vector<ModelObject *> &dowels,
Vec3d &local_dowels_displace);
void process_modifier_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & inverse_cut_matrix,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower);
void process_volume_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
ModelObjectCutAttributes attributes,
TriangleMesh & upper_mesh,
TriangleMesh & lower_mesh);
void process_solid_part_cut(ModelVolume * volume,
const Transform3d & instance_matrix,
const Transform3d & cut_matrix,
const std::array<Vec3d, 4> &plane_points,
ModelObjectCutAttributes attributes,
ModelObject * upper,
ModelObject * lower,
Vec3d & local_displace);
// BBS: replace z with plane_points
ModelObjectPtrs cut(size_t instance, std::array<Vec3d, 4> plane_points, ModelObjectCutAttributes attributes);
// BBS
ModelObjectPtrs segment(size_t instance, unsigned int max_extruders, double smoothing_alpha = 0.5, int segment_number = 5);
void split(ModelObjectPtrs* new_objects);
void split(ModelObjectPtrs*new_objects);
void merge();
// BBS: Boolean opts - Musang King
@@ -553,6 +523,10 @@ public:
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_repaired_errors_count(const int vol_idx = -1) const;
// Detect if object has at least one solid mash
bool has_solid_mesh() const;
bool is_cut() const { return cut_id.id().valid(); }
bool has_connectors() const;
private:
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
@@ -853,37 +827,45 @@ public:
};
Source source;
// struct used by cut command
// struct used by cut command
// It contains information about connetors
struct CutInfo
{
bool is_connector{false};
bool is_processed{true};
CutConnectorType connector_type{CutConnectorType::Plug};
float radius{0.f};
float height{0.f};
float radius_tolerance{0.f}; // [0.f : 1.f]
float height_tolerance{0.f}; // [0.f : 1.f]
bool is_from_upper{ true };
bool is_connector{ false };
bool is_processed{ true };
CutConnectorType connector_type{ CutConnectorType::Plug };
float radius_tolerance{ 0.f };// [0.f : 1.f]
float height_tolerance{ 0.f };// [0.f : 1.f]
CutInfo() = default;
CutInfo(CutConnectorType type, float radius_, float height_, float rad_tolerance, float h_tolerance, bool processed = false)
: is_connector(true), is_processed(processed), connector_type(type)
, radius(radius_), height(height_), radius_tolerance(rad_tolerance), height_tolerance(h_tolerance)
CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) :
is_connector(true),
is_processed(processed),
connector_type(type),
radius_tolerance(rad_tolerance),
height_tolerance(h_tolerance)
{}
void set_processed() { is_processed = true; }
void invalidate() { is_connector = false; }
void invalidate() { is_connector = false; }
void reset_from_upper() { is_from_upper = true; }
template<class Archive> inline void serialize(Archive &ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); }
template<class Archive> inline void serialize(Archive& ar) {
ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance);
}
};
CutInfo cut_info;
CutInfo cut_info;
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
void invalidate_cut_info() { cut_info.invalidate(); }
bool is_from_upper() const { return cut_info.is_from_upper; }
void reset_from_upper() { cut_info.reset_from_upper(); }
bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; }
void invalidate_cut_info() { cut_info.invalidate(); }
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
const TriangleMesh* mesh_ptr() const { return m_mesh.get(); }
std::shared_ptr<const TriangleMesh> mesh_ptr() const { return m_mesh; }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
@@ -922,6 +904,7 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; }
void reset_extra_facets();
void set_material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
@@ -931,8 +914,6 @@ public:
bool is_splittable() const;
void apply_tolerance();
// BBS
std::vector<int> get_extruders() const;
void update_extruder_count(size_t extruder_count);
+16 -5
View File
@@ -899,7 +899,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
offset_top_surface = 0;
// don't takes into account too thin areas
// skip if the exposed area is smaller than "min_width_top_surface"
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), config->min_width_top_surface.get_abs_value(perimeter_width));
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width))));
Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface);
@@ -1404,7 +1404,7 @@ void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area)
}
// Reorient loop direction
static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole)
static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole, bool reverse_internal_only)
{
if (steep_overhang_hole || steep_overhang_contour) {
for (auto entity : entities) {
@@ -1412,7 +1412,18 @@ static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_
ExtrusionLoop *eloop = static_cast<ExtrusionLoop *>(entity);
// Only reverse when needed
bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour;
if (need_reverse) {
bool isExternal = false;
if(reverse_internal_only){
for(auto path : eloop->paths){
if(path.role() == erExternalPerimeter){
isExternal = true;
break;
}
}
}
if (need_reverse && !isExternal) {
eloop->make_clockwise();
}
}
@@ -1710,7 +1721,7 @@ void PerimeterGenerator::process_classic()
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole);
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole);
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only);
// if brim will be printed, reverse the order of perimeters so that
// we continue inwards after having finished the brim
@@ -2232,7 +2243,7 @@ void PerimeterGenerator::process_arachne()
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only);
this->loops->append(extrusion_coll);
}
+1 -1
View File
@@ -55,7 +55,7 @@ using Vec2f = Eigen::Matrix<float, 2, 1, Eigen::DontAlign>;
using Vec3f = Eigen::Matrix<float, 3, 1, Eigen::DontAlign>;
using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
// BBS
using Vec4f = Eigen::Matrix<float, 4, 1, Eigen::DontAlign>;
using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
using Points = std::vector<Point>;
+1 -1
View File
@@ -726,7 +726,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
static std::vector<std::string> s_Preset_print_options {
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold",
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only",
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
+16 -11
View File
@@ -20,6 +20,7 @@
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Config.hpp"
#include "Exception.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
@@ -2463,7 +2464,7 @@ void Print::_make_wipe_tower()
}
this->throw_if_canceled();
if (!m_config.purge_in_prime_tower) {
if (is_BBL_printer()) {
// in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_config.prime_volume, m_wipe_tower_data.tool_ordering.first_extruder(),
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z);
@@ -2587,17 +2588,20 @@ void Print::_make_wipe_tower()
for (const auto extruder_id : layer_tools.extruders) {
if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */ extruder_id !=
current_extruder_id) {
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
volume_to_wipe *= m_config.flush_multiplier;
// Not all of that can be used for infill purging:
volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
float volume_to_wipe = m_config.prime_volume;
if (m_config.purge_in_prime_tower) {
volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
volume_to_wipe *= m_config.flush_multiplier;
// Not all of that can be used for infill purging:
volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// try to assign some infills/objects for the wiping:
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
volume_to_wipe);
// try to assign some infills/objects for the wiping:
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
volume_to_wipe);
// add back the minimal amount toforce on the wipe tower:
volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// add back the minimal amount toforce on the wipe tower:
volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
}
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height,
@@ -2712,6 +2716,7 @@ DynamicConfig PrintStatistics::config() const
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
config.set_key_value("initial_tool", new ConfigOptionInt(static_cast<int>(this->initial_tool)));
return config;
}
@@ -2721,7 +2726,7 @@ DynamicConfig PrintStatistics::placeholders()
for (const std::string &key : {
"print_time", "normal_print_time", "silent_print_time",
"used_filament", "extruded_volume", "total_cost", "total_weight",
"total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
"initial_tool", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
return config;
}
+2
View File
@@ -742,6 +742,7 @@ struct PrintStatistics
double total_weight;
double total_wipe_tower_cost;
double total_wipe_tower_filament;
unsigned int initial_tool;
std::map<size_t, double> filament_stats;
// Config with the filled in print statistics.
@@ -759,6 +760,7 @@ struct PrintStatistics
total_weight = 0.;
total_wipe_tower_cost = 0.;
total_wipe_tower_filament = 0.;
initial_tool = 0;
filament_stats.clear();
}
};
+10 -3
View File
@@ -840,7 +840,15 @@ void PrintConfigDef::init_fff_params()
def->label = L("Reverse on odd");
def->full_label = L("Overhang reversal");
def->category = L("Quality");
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhang.");
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhangs.\n\nThis setting can also help reduce part warping due to the reduction of stresses in the part walls.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("overhang_reverse_internal_only", coBool);
def->label = L("Reverse only internal perimeters");
def->full_label = L("Reverse only internal perimeters");
def->category = L("Quality");
def->tooltip = L("Apply the reverse perimeters logic only on internal perimeters. \n\nThis setting greatly reduces part stresses as they are now distributed in alternating directions. This should reduce part warping while also maintaining external wall quality. This feature can be very useful for warp prone material, like ABS/ASA, and also for elastic filaments, like TPU and Silk PLA. It can also help reduce warping on floating regions over supports.\n\nFor this setting to be the most effective, it is recomended to set the Reverse Threshold to 0 so that all internal walls print in alternating directions on odd layers irrespective of their overhang degree.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -1454,7 +1462,6 @@ void PrintConfigDef::init_fff_params()
"Can't be zero");
def->sidetext = L("mm³/s");
def->min = 0;
def->max = 200;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 2. });
@@ -2817,7 +2824,7 @@ def = this->add("filament_loading_speed", coFloats);
def->tooltip = L("User can self-define the project file name when export");
def->full_width = true;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[0]}_{print_time}.gcode"));
def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[initial_tool]}_{print_time}.gcode"));
def = this->add("make_overhang_printable", coBool);
def->label = L("Make overhang printable");
+1
View File
@@ -877,6 +877,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))
((ConfigOptionBool, hole_to_polyhole_twisted))
((ConfigOptionBool, overhang_reverse))
((ConfigOptionBool, overhang_reverse_internal_only))
((ConfigOptionFloatOrPercent, overhang_reverse_threshold))
)
+1
View File
@@ -1097,6 +1097,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "fuzzy_skin_point_distance"
|| opt_key == "detect_overhang_wall"
|| opt_key == "overhang_reverse"
|| opt_key == "overhang_reverse_internal_only"
|| opt_key == "overhang_reverse_threshold"
//BBS
|| opt_key == "enable_overhang_speed"
+24 -4
View File
@@ -110,10 +110,30 @@ public:
void set_maj(int maj) { ver.major = maj; }
void set_min(int min) { ver.minor = min; }
void set_patch(int patch) { ver.patch = patch; }
void set_metadata(boost::optional<const std::string&> meta) { ver.metadata = meta ? strdup(*meta) : nullptr; }
void set_metadata(const char *meta) { ver.metadata = meta ? strdup(meta) : nullptr; }
void set_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; }
void set_prerelease(const char *pre) { ver.prerelease = pre ? strdup(pre) : nullptr; }
void set_metadata(boost::optional<const std::string &> meta)
{
if (ver.metadata)
free(ver.metadata);
ver.metadata = meta ? strdup(*meta) : nullptr;
}
void set_metadata(const char *meta)
{
if (ver.metadata)
free(ver.metadata);
ver.metadata = meta ? strdup(meta) : nullptr;
}
void set_prerelease(boost::optional<const std::string &> pre)
{
if (ver.prerelease)
free(ver.prerelease);
ver.prerelease = pre ? strdup(*pre) : nullptr;
}
void set_prerelease(const char *pre)
{
if (ver.prerelease)
free(ver.prerelease);
ver.prerelease = pre ? strdup(pre) : nullptr;
}
// Comparison
bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; }
+167
View File
@@ -0,0 +1,167 @@
///|/ Copyright (c) Prusa Research 2022 Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_SurfaceMesh_hpp_
#define slic3r_SurfaceMesh_hpp_
#include <admesh/stl.h>
#include <libslic3r/TriangleMesh.hpp>
#include "boost/container/small_vector.hpp"
namespace Slic3r {
class TriangleMesh;
enum Face_index : int;
class Halfedge_index {
friend class SurfaceMesh;
public:
Halfedge_index() : m_face(Face_index(-1)), m_side(0) {}
Face_index face() const { return m_face; }
unsigned char side() const { return m_side; }
bool is_invalid() const { return int(m_face) < 0; }
bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); }
bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; }
private:
Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {}
Face_index m_face;
unsigned char m_side;
};
class Vertex_index {
friend class SurfaceMesh;
public:
Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {}
bool is_invalid() const { return int(m_face) < 0; }
bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex.
private:
Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {}
Face_index m_face;
unsigned char m_vertex_idx;
};
class SurfaceMesh {
public:
explicit SurfaceMesh(const indexed_triangle_set& its)
: m_its(its),
m_face_neighbors(its_face_neighbors_par(its))
{}
SurfaceMesh(const SurfaceMesh&) = delete;
SurfaceMesh& operator=(const SurfaceMesh&) = delete;
Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); }
Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); }
Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; }
Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; }
Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; }
Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); }
Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); }
Halfedge_index opposite(Halfedge_index h) const {
if (h.is_invalid())
return h;
int face_idx = m_face_neighbors[h.m_face][h.m_side];
Halfedge_index h_candidate = halfedge(Face_index(face_idx));
if (h_candidate.is_invalid())
return Halfedge_index(); // invalid
for (int i=0; i<3; ++i) {
if (is_same_vertex(source(h_candidate), target(h))) {
// Meshes in PrusaSlicer should be fixed enough for the following not to happen.
assert(is_same_vertex(target(h_candidate), source(h)));
return h_candidate;
}
h_candidate = next(h_candidate);
}
return Halfedge_index(); // invalid
}
Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); }
Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); }
Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); }
Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); }
Halfedge_index halfedge(Vertex_index source, Vertex_index target) const
{
Halfedge_index hi(source.m_face, source.m_vertex_idx);
assert(! hi.is_invalid());
const Vertex_index orig_target = this->target(hi);
Vertex_index current_target = orig_target;
while (! is_same_vertex(current_target, target)) {
hi = next_around_source(hi);
if (hi.is_invalid())
break;
current_target = this->target(hi);
if (is_same_vertex(current_target, orig_target))
return Halfedge_index(); // invalid
}
return hi;
}
const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; }
size_t degree(Vertex_index v) const
{
// In case the mesh is broken badly, the loop might end up to be infinite,
// never getting back to the first halfedge. Remember list of all half-edges
// and trip if any is encountered for the second time.
Halfedge_index h_first = halfedge(v);
boost::container::small_vector<Halfedge_index, 10> he_visited;
Halfedge_index h = next_around_target(h_first);
size_t degree = 2;
while (! h.is_invalid() && h != h_first) {
he_visited.emplace_back(h);
h = next_around_target(h);
if (std::find(he_visited.begin(), he_visited.end(), h) == he_visited.end())
return 0;
++degree;
}
return h.is_invalid() ? 0 : degree - 1;
}
size_t degree(Face_index f) const {
size_t total = 0;
for (unsigned char i=0; i<3; ++i) {
size_t d = degree(Vertex_index(f, i));
if (d == 0)
return 0;
total += d;
}
assert(total - 6 >= 0);
return total - 6; // we counted 3 halfedges from f, and one more for each neighbor
}
bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; }
bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; }
Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; }
private:
const std::vector<Vec3i> m_face_neighbors;
const indexed_triangle_set& m_its;
};
} //namespace Slic3r
#endif // slic3r_SurfaceMesh_hpp_
+2 -2
View File
@@ -12,8 +12,6 @@
#define ENABLE_RENDER_SELECTION_CENTER 0
// Shows an imgui dialog with camera related data
#define ENABLE_CAMERA_STATISTICS 0
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
#define ENABLE_RENDER_PICKING_PASS 0
// Enable extracting thumbnails from selected gcode and save them as png files
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG 0
// Disable synchronization of unselected instances
@@ -61,6 +59,8 @@
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
// Enable fit print volume command for circular printbeds
#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
// Enable picking using raytracing
#define ENABLE_RAYCAST_PICKING_DEBUG 0
#endif // _prusaslicer_technologies_h_
+236 -55
View File
@@ -1,3 +1,18 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard
///|/ Copyright (c) 2014 Miro Hrončok @hroncok
///|/ Copyright (c) 2014 Petr Ledvina @ledvinap
///|/
///|/ ported from lib/Slic3r/TriangleMesh.pm:
///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2012 - 2013 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Exception.hpp"
#include "TriangleMesh.hpp"
#include "TriangleMeshSlicer.hpp"
@@ -958,6 +973,51 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa)
return mesh;
}
indexed_triangle_set its_make_frustum(double r, double h, double fa)
{
indexed_triangle_set mesh;
size_t n_steps = (size_t)ceil(2. * PI / fa);
double angle_step = 2. * PI / n_steps;
auto &vertices = mesh.vertices;
auto &facets = mesh.indices;
vertices.reserve(2 * n_steps + 2);
facets.reserve(4 * n_steps);
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3f(0.f, 0.f, 0.f));
vertices.emplace_back(Vec3f(0.f, 0.f, float(h)));
// for each line along the polygon approximating the top/bottom of the
// circle, generate four points and four facets (2 for the wall, 2 for the
// top and bottom.
// Special case: Last line shares 2 vertices with the first line.
Vec2f vec_top = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, 0.5f*r);
Vec2f vec_botton = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, r);
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
for (size_t i = 1; i < n_steps; ++i) {
vec_top = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, 0.5f*float(r));
vec_botton = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, float(r));
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, id - 1, id - 3); // top
facets.emplace_back(id, 1, id - 2); // bottom
facets.emplace_back(id, id - 2, id - 3); // upper-right of side
facets.emplace_back(id, id - 3, id - 1); // bottom-left of side
}
// Connect the last set of vertices with the first.
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, 2, id - 1);
facets.emplace_back( 3, 1, id);
facets.emplace_back(id, 2, 3);
facets.emplace_back(id, id - 1, 2);
return mesh;
}
indexed_triangle_set its_make_cone(double r, double h, double fa)
{
indexed_triangle_set mesh;
@@ -984,61 +1044,6 @@ indexed_triangle_set its_make_cone(double r, double h, double fa)
return mesh;
}
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j + 0.25 * M_PI;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_make_pyramid(float base, float height)
{
float a = base / 2.f;
@@ -1116,6 +1121,182 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
return mesh;
}
// Generates mesh for a frustum dowel centered about the origin, using the count of sectors
// Note: This function uses code for sphere generation, but for stackCount = 2;
indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount)
{
int stackCount = 2;
float sectorStep = float(2. * M_PI / sectorCount);
float stackStep = float(M_PI / stackCount);
indexed_triangle_set mesh;
auto& vertices = mesh.vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle))));
else
for (int j = 0; j < sectorCount; ++j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j + 0.25 * M_PI;
vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>());
}
}
auto& facets = mesh.indices;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(k1, k2, k1_next);
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(k1_next, k2, k2_next);
}
k1 = k1_next;
k2 = k2_next;
}
}
return mesh;
}
indexed_triangle_set its_make_snap(double r, double h, float space_proportion, float bulge_proportion)
{
const float radius = (float)r;
const float height = (float)h;
const size_t sectors_cnt = 10; //(float)fa;
const float halfPI = 0.5f * (float)PI;
const float space_len = space_proportion * radius;
const float b_len = radius;
const float m_len = (1 + bulge_proportion) * radius;
const float t_len = 0.5f * radius;
const float b_height = 0.f;
const float m_height = 0.5f * height;
const float t_height = height;
const float b_angle = acos(space_len/b_len);
const float t_angle = acos(space_len/t_len);
const float b_angle_step = b_angle / (float)sectors_cnt;
const float t_angle_step = t_angle / (float)sectors_cnt;
const Vec2f b_vec = Eigen::Vector2f(0, b_len);
const Vec2f t_vec = Eigen::Vector2f(0, t_len);
auto add_side_vertices = [b_vec, t_vec, b_height, m_height, t_height](std::vector<stl_vertex>& vertices, float b_angle, float t_angle, const Vec2f& m_vec) {
Vec2f b_pt = Eigen::Rotation2Df(b_angle) * b_vec;
Vec2f m_pt = Eigen::Rotation2Df(b_angle) * m_vec;
Vec2f t_pt = Eigen::Rotation2Df(t_angle) * t_vec;
vertices.emplace_back(Vec3f(b_pt(0), b_pt(1), b_height));
vertices.emplace_back(Vec3f(m_pt(0), m_pt(1), m_height));
vertices.emplace_back(Vec3f(t_pt(0), t_pt(1), t_height));
};
auto add_side_facets = [](std::vector<stl_triangle_vertex_indices>& facets, int vertices_cnt, int frst_id, int scnd_id) {
int id = vertices_cnt - 1;
facets.emplace_back(frst_id, id - 2, id - 5);
facets.emplace_back(id - 2, id - 1, id - 5);
facets.emplace_back(id - 1, id - 4, id - 5);
facets.emplace_back(id - 4, id - 1, id);
facets.emplace_back(id, id - 3, id - 4);
facets.emplace_back(id, scnd_id, id - 3);
};
const float f = (b_len - m_len) / m_len; // Flattening
auto get_m_len = [b_len, f](float angle) {
const float rad_sqr = b_len * b_len;
const float sin_sqr = sin(angle) * sin(angle);
const float f_sqr = (1-f)*(1-f);
return sqrtf(rad_sqr / (1 + (1 / f_sqr - 1) * sin_sqr));
};
auto add_sub_mesh = [add_side_vertices, add_side_facets, get_m_len,
b_height, t_height, b_angle, t_angle, b_angle_step, t_angle_step]
(indexed_triangle_set& mesh, float center_x, float angle_rotation, int frst_vertex_id) {
auto& vertices = mesh.vertices;
auto& facets = mesh.indices;
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3f(center_x, 0.f, b_height));
vertices.emplace_back(Vec3f(center_x, 0.f, t_height));
float b_angle_start = angle_rotation - b_angle;
float t_angle_start = angle_rotation - t_angle;
const float b_angle_stop = angle_rotation + b_angle;
const int frst_id = frst_vertex_id;
const int scnd_id = frst_id + 1;
// add first side vertices and internal facets
{
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
int id = (int)vertices.size() - 1;
facets.emplace_back(frst_id, id - 2, id - 1);
facets.emplace_back(frst_id, id - 1, id);
facets.emplace_back(frst_id, id, scnd_id);
}
// add d side vertices and facets
while (!is_approx(b_angle_start, b_angle_stop)) {
b_angle_start += b_angle_step;
t_angle_start += t_angle_step;
const Vec2f m_vec = Eigen::Vector2f(0, get_m_len(b_angle_start));
add_side_vertices(vertices, b_angle_start, t_angle_start, m_vec);
add_side_facets(facets, (int)vertices.size(), frst_id, scnd_id);
}
// add last internal facets to close the mesh
{
int id = (int)vertices.size() - 1;
facets.emplace_back(frst_id, scnd_id, id);
facets.emplace_back(frst_id, id, id - 1);
facets.emplace_back(frst_id, id - 1, id - 2);
}
};
indexed_triangle_set mesh;
mesh.vertices.reserve(2 * (3 * (2 * sectors_cnt + 1) + 2));
mesh.indices.reserve(2 * (6 * 2 * sectors_cnt + 6));
add_sub_mesh(mesh, -space_len, halfPI , 0);
add_sub_mesh(mesh, space_len, 3 * halfPI, (int)mesh.vertices.size());
return mesh;
}
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
{
std::vector<Vec3f> dst_vertices;
+13
View File
@@ -1,3 +1,14 @@
///|/ Copyright (c) Prusa Research 2017 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
///|/ Copyright (c) 2019 Sijmen Schoon
///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/TriangleMesh.pm:
///|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2012 - 2013 Mark Hindess
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_TriangleMesh_hpp_
#define slic3r_TriangleMesh_hpp_
@@ -337,9 +348,11 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }
+4 -75
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "Tesselate.hpp"
@@ -2300,79 +2304,4 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
}
}
// BBS: implement plane cut with cgal
static Vec3d calc_plane_normal(const std::array<Vec3d, 4>& plane_points)
{
Vec3d v01 = plane_points[1] - plane_points[0];
Vec3d v12 = plane_points[2] - plane_points[1];
Vec3d plane_normal = v01.cross(v12);
plane_normal.normalize();
return plane_normal;
}
void cut_mesh
(
const indexed_triangle_set& mesh, // model object coordinate
std::array<Vec3d, 4> plane_points, // model object coordinate
indexed_triangle_set* upper,
indexed_triangle_set* lower,
bool triangulate_caps
)
{
assert(upper || lower);
if (upper == nullptr && lower == nullptr)
return;
BOOST_LOG_TRIVIAL(trace) << "cut_mesh - slicing object";
Vec3d plane_normal = calc_plane_normal(plane_points);
if (std::abs(plane_normal(0)) < EPSILON && std::abs(plane_normal(1)) < EPSILON) {
cut_mesh(mesh, plane_points[0](2), upper, lower);
return;
}
// BBS
if (std::abs(plane_normal(2)) < EPSILON) {
// keep the side on the normal direction
}
else if (plane_normal(2) < 0.0) {
std::reverse(plane_points.begin(), plane_points.end());
}
plane_normal = calc_plane_normal(plane_points);
Vec3d mid_point = { 0.0, 0.0, 0.0 };
for (auto pt : plane_points)
mid_point += pt;
mid_point /= (double)plane_points.size();
Vec3d movement = -mid_point;
Vec3d axis = { 0.0, 0.0, 0.0 };
double phi = 0.0;
Matrix3d matrix;
matrix.setIdentity();
Geometry::rotation_from_two_vectors(plane_normal, { 0.0, 0.0, 1.0 }, axis, phi, &matrix);
Vec3d angles = Geometry::extract_euler_angles(matrix);
movement = matrix * movement;
Transform3d transfo;
transfo.setIdentity();
transfo.translate(movement);
transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX()));
indexed_triangle_set mesh_temp = mesh;
its_transform(mesh_temp, transfo);
cut_mesh(mesh_temp, 0., upper, lower);
Transform3d transfo_inv = transfo.inverse();
if (upper) {
its_transform(*upper, transfo_inv);
}
if (lower) {
its_transform(*lower, transfo_inv);
}
}
} // namespace Slic3r
+5 -9
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2022 Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_TriangleMeshSlicer_hpp_
#define slic3r_TriangleMeshSlicer_hpp_
@@ -130,14 +134,6 @@ void cut_mesh(
indexed_triangle_set *lower,
bool triangulate_caps = true);
// BBS
void cut_mesh(
const indexed_triangle_set &mesh,
std::array<Vec3d, 4> plane_points,
indexed_triangle_set *upper,
indexed_triangle_set *lower,
bool triangulate_caps = true);
}
} // namespace Slic3r
#endif // slic3r_TriangleMeshSlicer_hpp_
+5
View File
@@ -160,6 +160,11 @@ void flush_logs();
// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded.
typedef std::string local_encoded_string;
// Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character.
// Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence.
extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0);
extern size_t get_utf8_sequence_length(const char *seq, size_t size);
// Convert an UTF-8 encoded string into local coding.
// On Windows, the UTF-8 string is converted to a local 8-bit code page.
// On OSX and Linux, this function does no conversion and returns a copy of the source string.
+76
View File
@@ -1,3 +1,9 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, David Kocík @kocikdav, Roman Beránek @zavorka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Justin Schuh @jschuh
///|/ Copyright (c) Slic3r 2013 - 2015 Alessandro Ranellucci @alranel
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "Utils.hpp"
#include "I18N.hpp"
@@ -1030,6 +1036,76 @@ bool is_shapes_dir(const std::string& dir)
namespace Slic3r {
size_t get_utf8_sequence_length(const std::string& text, size_t pos)
{
assert(pos < text.size());
return get_utf8_sequence_length(text.c_str() + pos, text.size() - pos);
}
size_t get_utf8_sequence_length(const char *seq, size_t size)
{
size_t length = 0;
unsigned char c = seq[0];
if (c < 0x80) { // 0x00-0x7F
// is ASCII letter
length++;
}
// Bytes 0x80 to 0xBD are trailer bytes in a multibyte sequence.
// pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes.
else if (c < 0xC0) { // 0x80-0xBF
length++;
while (length < size) {
c = seq[length];
if (c < 0x80 || c >= 0xC0) {
break; // prevent overrun
}
length++; // add a utf-8 trailer byte
}
}
// Bytes 0xC0 to 0xFD are header bytes in a multibyte sequence.
// The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence.
else if (c < 0xE0) { // 0xC0-0xDF
// add a utf-8 sequence (2 bytes)
if (2 > size) {
return size; // prevent overrun
}
length += 2;
}
else if (c < 0xF0) { // 0xE0-0xEF
// add a utf-8 sequence (3 bytes)
if (3 > size) {
return size; // prevent overrun
}
length += 3;
}
else if (c < 0xF8) { // 0xF0-0xF7
// add a utf-8 sequence (4 bytes)
if (4 > size) {
return size; // prevent overrun
}
length += 4;
}
else if (c < 0xFC) { // 0xF8-0xFB
// add a utf-8 sequence (5 bytes)
if (5 > size) {
return size; // prevent overrun
}
length += 5;
}
else if (c < 0xFE) { // 0xFC-0xFD
// add a utf-8 sequence (6 bytes)
if (6 > size) {
return size; // prevent overrun
}
length += 6;
}
else { // 0xFE-0xFF
// not a utf-8 sequence
length++;
}
return length;
}
// Encode an UTF-8 string to the local code page.
std::string encode_path(const char *src)
{
+23 -9
View File
@@ -1,3 +1,12 @@
#/|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
#/|/ Copyright (c) 2023 Pedro Lamas @PedroLamas
#/|/ Copyright (c) 2020 Sergey Kovalev @RandoMan70
#/|/ Copyright (c) 2021 Boleslaw Ciesielski
#/|/ Copyright (c) 2019 Spencer Owen @spuder
#/|/ Copyright (c) 2019 Stephan Reichhelm @stephanr
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
cmake_minimum_required(VERSION 3.13)
project(libslic3r_gui)
@@ -99,6 +108,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GLShader.hpp
GUI/GLCanvas3D.hpp
GUI/GLCanvas3D.cpp
GUI/SceneRaycaster.hpp
GUI/SceneRaycaster.cpp
GUI/OpenGLManager.hpp
GUI/OpenGLManager.cpp
GUI/Selection.hpp
@@ -115,24 +126,26 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoRotate.hpp
GUI/Gizmos/GLGizmoScale.cpp
GUI/Gizmos/GLGizmoScale.hpp
GUI/Gizmos/GLGizmoSlaSupports.cpp
GUI/Gizmos/GLGizmoSlaSupports.hpp
#GUI/Gizmos/GLGizmoSlaSupports.cpp
#GUI/Gizmos/GLGizmoSlaSupports.hpp
GUI/Gizmos/GLGizmoFdmSupports.cpp
GUI/Gizmos/GLGizmoFdmSupports.hpp
GUI/Gizmos/GLGizmoFlatten.cpp
GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoAdvancedCut.cpp
GUI/Gizmos/GLGizmoAdvancedCut.hpp
GUI/Gizmos/GLGizmoHollow.cpp
GUI/Gizmos/GLGizmoHollow.hpp
GUI/Gizmos/GLGizmoCut.cpp
GUI/Gizmos/GLGizmoCut.hpp
#GUI/Gizmos/GLGizmoHollow.cpp
#GUI/Gizmos/GLGizmoHollow.hpp
GUI/Gizmos/GLGizmoPainterBase.cpp
GUI/Gizmos/GLGizmoPainterBase.hpp
GUI/Gizmos/GLGizmoSimplify.cpp
GUI/Gizmos/GLGizmoSimplify.hpp
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
GUI/Gizmos/GLGizmoFaceDetector.cpp
GUI/Gizmos/GLGizmoFaceDetector.hpp
#GUI/Gizmos/GLGizmoFaceDetector.cpp
#GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoMeasure.cpp
GUI/Gizmos/GLGizmoMeasure.hpp
GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp
GUI/Gizmos/GLGizmoText.cpp
@@ -173,6 +186,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_App.hpp
GUI/GUI_Utils.cpp
GUI/GUI_Utils.hpp
GUI/GUI_Geometry.cpp
GUI/GUI_Geometry.hpp
GUI/I18N.cpp
GUI/I18N.hpp
GUI/MainFrame.cpp
@@ -348,7 +363,6 @@ set(SLIC3R_GUI_SOURCES
GUI/Mouse3DController.hpp
GUI/IMSlider.cpp
GUI/IMSlider.hpp
GUI/IMSlider_Utils.hpp
GUI/Notebook.cpp
GUI/Notebook.hpp
GUI/TabButton.cpp
+153 -138
View File
@@ -12,6 +12,8 @@
#include "GUI_App.hpp"
#include "GUI_Colors.hpp"
#include "GLCanvas3D.hpp"
#include "Plater.hpp"
#include "Camera.hpp"
#include <GL/glew.h>
@@ -26,13 +28,58 @@
#endif
static const float GROUND_Z = -0.04f;
static const std::array<float, 4> DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f };
static const std::array<float, 4> DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f };
static const std::array<float, 4> PICKING_MODEL_COLOR = { 0.0f, 0.0f, 0.0f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_MODEL_COLOR_DARK = { 0.255f, 0.255f, 0.283f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_SOLID_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 1.0f };
static const Slic3r::ColorRGBA DEFAULT_TRANSPARENT_GRID_COLOR = { 0.9f, 0.9f, 0.9f, 0.6f };
namespace Slic3r {
namespace GUI {
bool init_model_from_poly(GLModel &model, const ExPolygon &poly, float z)
{
if (poly.empty())
return false;
const std::vector<Vec2f> triangles = triangulate_expolygon_2f(poly, NORMALS_UP);
if (triangles.empty() || triangles.size() % 3 != 0)
return false;
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3T2 };
init_data.reserve_vertices(triangles.size());
init_data.reserve_indices(triangles.size() / 3);
Vec2f min = triangles.front();
Vec2f max = min;
for (const Vec2f &v : triangles) {
min = min.cwiseMin(v).eval();
max = max.cwiseMax(v).eval();
}
const Vec2f size = max - min;
if (size.x() <= 0.0f || size.y() <= 0.0f)
return false;
Vec2f inv_size = size.cwiseInverse();
inv_size.y() *= -1.0f;
// vertices + indices
unsigned int vertices_counter = 0;
for (const Vec2f &v : triangles) {
const Vec3f p = {v.x(), v.y(), z};
init_data.add_vertex(p, (Vec2f)(v - min).cwiseProduct(inv_size).eval());
++vertices_counter;
if (vertices_counter % 3 == 0)
init_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
}
model.init_from(std::move(init_data));
return true;
}
/*
bool GeometryBuffer::set_from_triangles(const std::vector<Vec2f> &triangles, float z)
{
if (triangles.empty()) {
@@ -131,41 +178,45 @@ const float* GeometryBuffer::get_vertices_data() const
{
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
}
*/
const float Bed3D::Axes::DefaultStemRadius = 0.5f;
const float Bed3D::Axes::DefaultStemLength = 25.0f;
const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius;
const float Bed3D::Axes::DefaultTipLength = 5.0f;
std::array<float, 4> Bed3D::AXIS_X_COLOR = decode_color_to_float_array("#FF0000");
std::array<float, 4> Bed3D::AXIS_Y_COLOR = decode_color_to_float_array("#00FF00");
std::array<float, 4> Bed3D::AXIS_Z_COLOR = decode_color_to_float_array("#0000FF");
ColorRGBA Bed3D::AXIS_X_COLOR = ColorRGBA::X();
ColorRGBA Bed3D::AXIS_Y_COLOR = ColorRGBA::Y();
ColorRGBA Bed3D::AXIS_Z_COLOR = ColorRGBA::Z();
void Bed3D::update_render_colors()
{
Bed3D::AXIS_X_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_X]);
Bed3D::AXIS_Y_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Y]);
Bed3D::AXIS_Z_COLOR = GLColor(RenderColor::colors[RenderCol_Axis_Z]);
Bed3D::AXIS_X_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_X]);
Bed3D::AXIS_Y_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_Y]);
Bed3D::AXIS_Z_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Axis_Z]);
}
void Bed3D::load_render_colors()
{
RenderColor::colors[RenderCol_Axis_X] = IMColor(Bed3D::AXIS_X_COLOR);
RenderColor::colors[RenderCol_Axis_Y] = IMColor(Bed3D::AXIS_Y_COLOR);
RenderColor::colors[RenderCol_Axis_Z] = IMColor(Bed3D::AXIS_Z_COLOR);
RenderColor::colors[RenderCol_Axis_X] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_X_COLOR);
RenderColor::colors[RenderCol_Axis_Y] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_Y_COLOR);
RenderColor::colors[RenderCol_Axis_Z] = ImGuiWrapper::to_ImVec4(Bed3D::AXIS_Z_COLOR);
}
void Bed3D::Axes::render() const
void Bed3D::Axes::render()
{
auto render_axis = [this](const Transform3f& transform) {
glsafe(::glPushMatrix());
glsafe(::glMultMatrixf(transform.data()));
auto render_axis = [this](GLShaderProgram* shader, const Transform3d& transform) {
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("view_model_matrix", view_matrix * transform);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * transform.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
m_arrow.render();
glsafe(::glPopMatrix());
};
if (!m_arrow.is_initialized())
const_cast<GLModel*>(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length));
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
@@ -177,16 +228,16 @@ void Bed3D::Axes::render() const
shader->set_uniform("emission_factor", 0.0f);
// x axis
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_X_COLOR);
render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast<float>());
m_arrow.set_color(AXIS_X_COLOR);
render_axis(shader, Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }));
// y axis
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_Y_COLOR);
render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast<float>());
m_arrow.set_color(AXIS_Y_COLOR);
render_axis(shader, Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }));
// z axis
const_cast<GLModel*>(&m_arrow)->set_color(-1, AXIS_Z_COLOR);
render_axis(Geometry::assemble_transform(m_origin).cast<float>());
m_arrow.set_color(AXIS_Z_COLOR);
render_axis(shader, Geometry::assemble_transform(m_origin));
shader->stop_using();
@@ -259,25 +310,9 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
//BBS: add part plate logic
//BBS add default bed
#if 1
ExPolygon poly{ Polygon::new_scale(printable_area) };
#else
ExPolygon poly;
for (const Vec2d& p : printable_area) {
poly.contour.append(Point(scale_(p(0) + m_position.x()), scale_(p(1) + m_position.y())));
}
#endif
calc_triangles(poly);
//no need gridline for 3dbed
//const BoundingBox& bed_bbox = poly.contour.bounding_box();
//calc_gridlines(poly, bed_bbox);
//m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];
m_triangles.reset();
if (with_reset) {
this->release_VBOs();
//m_texture.reset();
m_model.reset();
}
@@ -290,6 +325,10 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
// unregister from picking
// BBS: remove the bed picking logic
// wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed);
// Let the calee to update the UI.
return true;
}
@@ -325,33 +364,28 @@ void Bed3D::on_change_color_mode(bool is_dark)
m_is_dark = is_dark;
}
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes)
void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes)
{
render_internal(canvas, bottom, scale_factor, show_axes);
render_internal(canvas, view_matrix, projection_matrix, bottom, scale_factor, show_axes);
}
/*void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor)
{
render_internal(canvas, bottom, scale_factor, false, false, true);
}*/
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes)
{
float* factor = const_cast<float*>(&m_scale_factor);
*factor = scale_factor;
m_scale_factor = scale_factor;
if (show_axes)
render_axes();
glsafe(::glEnable(GL_DEPTH_TEST));
m_model.set_color(-1, m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
switch (m_type)
{
case Type::System: { render_system(canvas, bottom); break; }
case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom); break; }
default:
case Type::Custom: { render_custom(canvas, bottom); break; }
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom); break; }
}
glsafe(::glDisable(GL_DEPTH_TEST));
@@ -388,39 +422,6 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box(bool consider_model_offset) cons
return out;
}
void Bed3D::calc_triangles(const ExPolygon& poly)
{
if (! m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z))
BOOST_LOG_TRIVIAL(error) << "Unable to create bed triangles";
}
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
{
/*Polylines axes_lines;
for (coord_t x = bed_bbox.min.x(); x <= bed_bbox.max.x(); x += scale_(10.0)) {
Polyline line;
line.append(Point(x, bed_bbox.min.y()));
line.append(Point(x, bed_bbox.max.y()));
axes_lines.push_back(line);
}
for (coord_t y = bed_bbox.min.y(); y <= bed_bbox.max.y(); y += scale_(10.0)) {
Polyline line;
line.append(Point(bed_bbox.min.x(), y));
line.append(Point(bed_bbox.max.x(), y));
axes_lines.push_back(line);
}
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON)));
// append bed contours
Lines contour_lines = to_lines(poly);
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
if (!m_gridlines.set_from_lines(gridlines, GROUND_Z))
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";*/
}
// Try to match the print bed shape with the shape of an active profile. If such a match exists,
// return the print bed model.
std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Pointfs& shape)
@@ -454,22 +455,22 @@ std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Point
return { Type::Custom, {}, {} };
}
void Bed3D::render_axes() const
void Bed3D::render_axes()
{
if (m_build_volume.valid())
m_axes.render();
}
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const
void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom)
{
if (!bottom)
render_model();
render_model(view_matrix, projection_matrix);
/*if (show_texture)
render_texture(bottom, canvas);*/
}
/*void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
/*void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
{
GLTexture* texture = const_cast<GLTexture*>(&m_texture);
GLTexture* temp_texture = const_cast<GLTexture*>(&m_temp_texture);
@@ -537,6 +538,9 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const
GLShaderProgram* shader = wxGetApp().get_shader("printbed");
if (shader != nullptr) {
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
shader->set_uniform("transparent_background", bottom);
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
@@ -624,9 +628,10 @@ void Bed3D::update_model_offset() const
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = calc_extended_bounding_box();
}
GeometryBuffer Bed3D::update_bed_triangles() const
void Bed3D::update_bed_triangles()
{
GeometryBuffer new_triangles;
m_triangles.reset();
Vec3d shift = m_extended_bounding_box.center();
shift(2) = -0.03;
Vec3d* model_offset_ptr = const_cast<Vec3d*>(&m_model_offset);
@@ -634,7 +639,7 @@ GeometryBuffer Bed3D::update_bed_triangles() const
//BBS: TODO: hack for default bed
BoundingBoxf3 build_volume;
if (!m_build_volume.valid()) return new_triangles;
if (!m_build_volume.valid()) return;
auto bed_ext = get_extents(m_bed_shape);
(*model_offset_ptr)(0) = m_build_volume.bounding_volume2d().min.x() - bed_ext.min.x();
(*model_offset_ptr)(1) = m_build_volume.bounding_volume2d().min.y() - bed_ext.min.y();
@@ -646,105 +651,115 @@ GeometryBuffer Bed3D::update_bed_triangles() const
new_bed_shape.push_back(new_point);
}
ExPolygon poly{ Polygon::new_scale(new_bed_shape) };
if (!new_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z)) {
;
if (!init_model_from_poly(m_triangles, poly, GROUND_Z)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to update plate triangles\n";
}
// update extended bounding box
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = calc_extended_bounding_box();
return new_triangles;
}
void Bed3D::render_model() const
void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
if (m_model_filename.empty())
return;
GLModel* model = const_cast<GLModel*>(&m_model);
if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) {
model->set_color(-1, m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
if (m_model.get_filename() != m_model_filename && m_model.init_from_file(m_model_filename)) {
m_model.set_color(m_is_dark ? DEFAULT_MODEL_COLOR_DARK : DEFAULT_MODEL_COLOR);
update_model_offset();
// BBS: remove the bed picking logic
//register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::assemble_transform(m_model_offset));
}
if (!model->get_filename().empty()) {
if (!m_model.get_filename().empty()) {
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.0f);
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
model->render();
glsafe(::glPopMatrix());
const Transform3d model_matrix = Geometry::assemble_transform(m_model_offset);
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
shader->set_uniform("projection_matrix", projection_matrix);
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
m_model.render();
shader->stop_using();
}
}
}
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom)
{
if (m_model_filename.empty()) {
render_default(bottom);
render_default(bottom, view_matrix, projection_matrix);
return;
}
if (!bottom)
render_model();
render_model(view_matrix, projection_matrix);
/*if (show_texture)
render_texture(bottom, canvas);*/
}
void Bed3D::render_default(bool bottom) const
void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
bool picking = false;
const_cast<GLTexture*>(&m_texture)->reset();
m_texture.reset();
unsigned int triangles_vcount = m_triangles.get_vertices_count();
GeometryBuffer default_triangles = update_bed_triangles();
if (triangles_vcount > 0) {
bool has_model = !m_model.get_filename().empty();
update_bed_triangles();
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", projection_matrix);
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) {
if (m_model.get_filename().empty() && !bottom) {
// draw background
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glColor4fv(picking ? PICKING_MODEL_COLOR.data() : DEFAULT_MODEL_COLOR.data()));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)default_triangles.get_vertices_data()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
m_triangles.set_color(DEFAULT_MODEL_COLOR);
m_triangles.render();
glsafe(::glDepthMask(GL_TRUE));
}
/*if (!picking) {
// draw grid
glsafe(::glLineWidth(1.5f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f));
else
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f));
glsafe(::glVertexPointer(3, GL_FLOAT, default_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
m_gridlines.set_color(picking ? DEFAULT_SOLID_GRID_COLOR : DEFAULT_TRANSPARENT_GRID_COLOR);
m_gridlines.render();
}*/
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
shader->stop_using();
}
}
void Bed3D::release_VBOs()
// BBS: remove the bed picking logic
/*
void Bed3D::register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo)
{
if (m_vbo_id > 0) {
glsafe(::glDeleteBuffers(1, &m_vbo_id));
m_vbo_id = 0;
}
}
assert(m_model.mesh_raycaster == nullptr);
indexed_triangle_set its;
its.vertices.reserve(geometry.vertices_count());
for (size_t i = 0; i < geometry.vertices_count(); ++i) {
its.vertices.emplace_back(geometry.extract_position_3(i));
}
its.indices.reserve(geometry.indices_count() / 3);
for (size_t i = 0; i < geometry.indices_count() / 3; ++i) {
const size_t tri_id = i * 3;
its.indices.emplace_back(geometry.extract_index(tri_id), geometry.extract_index(tri_id + 1), geometry.extract_index(tri_id + 2));
}
m_model.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
wxGetApp().plater()->canvas3D()->add_raycaster_for_picking(SceneRaycaster::EType::Bed, 0, *m_model.mesh_raycaster, trafo);
}
*/
} // GUI
} // Slic3r
+25 -22
View File
@@ -5,7 +5,8 @@
#include "3DScene.hpp"
#include "GLModel.hpp"
#include <libslic3r/BuildVolume.hpp>
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/ExPolygon.hpp"
#include <tuple>
#include <array>
@@ -15,6 +16,7 @@ namespace GUI {
class GLCanvas3D;
/*
class GeometryBuffer
{
struct Vertex
@@ -38,13 +40,16 @@ public:
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(); }
};
*/
bool init_model_from_poly(GLModel &model, const ExPolygon &poly, float z);
class Bed3D
{
public:
static std::array<float, 4> AXIS_X_COLOR;
static std::array<float, 4> AXIS_Y_COLOR;
static std::array<float, 4> AXIS_Z_COLOR;
static ColorRGBA AXIS_X_COLOR;
static ColorRGBA AXIS_Y_COLOR;
static ColorRGBA AXIS_Z_COLOR;
static void update_render_colors();
static void load_render_colors();
@@ -70,7 +75,7 @@ public:
m_arrow.reset();
}
float get_total_length() const { return m_stem_length + DefaultTipLength; }
void render() const;
void render();
};
public:
@@ -91,14 +96,13 @@ private:
BoundingBoxf3 m_extended_bounding_box;
// Slightly expanded print bed polygon, for collision detection.
//Polygon m_polygon;
GeometryBuffer m_triangles;
//GeometryBuffer m_gridlines;
GLModel m_triangles;
//GLModel m_gridlines;
GLTexture m_texture;
// temporary texture shown until the main texture has still no levels compressed
//GLTexture m_temp_texture;
GLModel m_model;
Vec3d m_model_offset{ Vec3d::Zero() };
unsigned int m_vbo_id{ 0 };
Axes m_axes;
float m_scale_factor{ 1.0f };
@@ -109,7 +113,7 @@ private:
public:
Bed3D() = default;
~Bed3D() { release_VBOs(); }
~Bed3D() = default;
// Update print bed model from configuration.
// Return true if the bed shape changed, so the calee will update the UI.
@@ -142,8 +146,7 @@ public:
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes);
//void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes);
void on_change_color_mode(bool is_dark);
@@ -151,21 +154,21 @@ private:
//BBS: add partplate related logic
// Calculate an extended bounding box from axes and current model for visualization purposes.
BoundingBoxf3 calc_extended_bounding_box(bool consider_model_offset = true) const;
void calc_triangles(const ExPolygon& poly);
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
void update_model_offset() const;
//BBS: with offset
GeometryBuffer update_bed_triangles() const;
void update_bed_triangles();
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_axes);
void render_axes() const;
void render_system(GLCanvas3D& canvas, bool bottom) const;
//void render_texture(bool bottom, GLCanvas3D& canvas) const;
void render_model() const;
void render_custom(GLCanvas3D& canvas, bool bottom) const;
void render_default(bool bottom) const;
void release_VBOs();
void render_axes();
void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
//void render_texture(bool bottom, GLCanvas3D& canvas);
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix);
// BBS: remove the bed picking logic
// void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo);
};
} // GUI
+590 -1133
View File
File diff suppressed because it is too large Load Diff
+90 -285
View File
@@ -1,3 +1,14 @@
///|/ Copyright (c) Prusa Research 2017 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2017 Eyal Soha @eyal0
///|/ Copyright (c) Slic3r 2015 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/GUI/3DScene.pm:
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Guillaume Seguin @iXce
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_3DScene_hpp_
#define slic3r_3DScene_hpp_
@@ -7,11 +18,13 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Color.hpp"
// BBS
#include "libslic3r/ObjectID.hpp"
#include "GLModel.hpp"
#include "GLShader.hpp"
#include "MeshUtils.hpp"
#include <functional>
#include <optional>
@@ -30,10 +43,10 @@
#define glsafe(cmd) cmd
#define glcheck()
#endif // HAS_GLSAFE
extern std::vector<std::array<float, 4>> get_extruders_colors();
extern float FullyTransparentMaterialThreshold;
extern float FullTransparentModdifiedToFixAlpha;
extern std::array<float, 4> adjust_color_for_rendering(const std::array<float, 4> &colors);
extern std::vector<Slic3r::ColorRGBA> get_extruders_colors();
extern float FullyTransparentMaterialThreshold;
extern float FullTransparentModdifiedToFixAlpha;
extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors);
namespace Slic3r {
@@ -54,224 +67,23 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
using ModelObjectPtrs = std::vector<ModelObject*>;
// Return appropriate color based on the ModelVolume.
std::array<float, 4> color_from_model_volume(const ModelVolume& model_volume);
// A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads.
class GLIndexedVertexArray {
public:
// Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized.
static_assert(sizeof(Eigen::AlignedBox<float, 3>) == 24, "Eigen::AlignedBox<float, 3> is not being vectorized, thus it does not need to be aligned");
using BoundingBox = Eigen::AlignedBox<float, 3>;
GLIndexedVertexArray() { m_bounding_box.setEmpty(); }
GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
triangle_indices(rhs.triangle_indices),
quad_indices(rhs.quad_indices),
m_bounding_box(rhs.m_bounding_box)
{ assert(!rhs.has_VBOs()); m_bounding_box.setEmpty(); }
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
triangle_indices(std::move(rhs.triangle_indices)),
quad_indices(std::move(rhs.quad_indices)),
m_bounding_box(rhs.m_bounding_box)
{ assert(! rhs.has_VBOs()); }
~GLIndexedVertexArray() { release_geometry(); }
GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs)
{
assert(vertices_and_normals_interleaved_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_indices_VBO_id == 0);
this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved;
this->triangle_indices = rhs.triangle_indices;
this->quad_indices = rhs.quad_indices;
this->m_bounding_box = rhs.m_bounding_box;
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
GLIndexedVertexArray& operator=(GLIndexedVertexArray &&rhs)
{
assert(vertices_and_normals_interleaved_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_indices_VBO_id == 0);
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
this->triangle_indices = std::move(rhs.triangle_indices);
this->quad_indices = std::move(rhs.quad_indices);
this->m_bounding_box = rhs.m_bounding_box;
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
// Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
std::vector<float> vertices_and_normals_interleaved;
std::vector<int> triangle_indices;
std::vector<int> quad_indices;
// When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length.
size_t vertices_and_normals_interleaved_size{ 0 };
size_t triangle_indices_size{ 0 };
size_t quad_indices_size{ 0 };
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
unsigned int triangle_indices_VBO_id{ 0 };
unsigned int quad_indices_VBO_id{ 0 };
#if ENABLE_SMOOTH_NORMALS
void load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals = false);
void load_mesh(const TriangleMesh& mesh, bool smooth_normals = false) { this->load_mesh_full_shading(mesh, smooth_normals); }
#else
void load_mesh_full_shading(const TriangleMesh& mesh);
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
#endif // ENABLE_SMOOTH_NORMALS
void load_its_flat_shading(const indexed_triangle_set &its);
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
inline void reserve(size_t sz) {
this->vertices_and_normals_interleaved.reserve(sz * 6);
this->triangle_indices.reserve(sz * 3);
this->quad_indices.reserve(sz * 4);
}
inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
this->vertices_and_normals_interleaved.emplace_back(nx);
this->vertices_and_normals_interleaved.emplace_back(ny);
this->vertices_and_normals_interleaved.emplace_back(nz);
this->vertices_and_normals_interleaved.emplace_back(x);
this->vertices_and_normals_interleaved.emplace_back(y);
this->vertices_and_normals_interleaved.emplace_back(z);
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
m_bounding_box.extend(Vec3f(x, y, z));
};
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
}
template<typename Derived, typename Derived2>
inline void push_geometry(const Eigen::MatrixBase<Derived>& p, const Eigen::MatrixBase<Derived2>& n) {
push_geometry(float(p(0)), float(p(1)), float(p(2)), float(n(0)), float(n(1)), float(n(2)));
}
inline void push_triangle(int idx1, int idx2, int idx3) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
this->triangle_indices.emplace_back(idx1);
this->triangle_indices.emplace_back(idx2);
this->triangle_indices.emplace_back(idx3);
this->triangle_indices_size = this->triangle_indices.size();
};
inline void push_quad(int idx1, int idx2, int idx3, int idx4) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity())
this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4));
this->quad_indices.emplace_back(idx1);
this->quad_indices.emplace_back(idx2);
this->quad_indices.emplace_back(idx3);
this->quad_indices.emplace_back(idx4);
this->quad_indices_size = this->quad_indices.size();
};
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
void finalize_geometry(bool opengl_initialized);
// Release the geometry data, release OpenGL VBOs.
void release_geometry();
void render() const;
void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const;
// Is there any geometry data stored?
bool empty() const { return vertices_and_normals_interleaved_size == 0; }
void clear() {
this->vertices_and_normals_interleaved.clear();
this->triangle_indices.clear();
this->quad_indices.clear();
vertices_and_normals_interleaved_size = 0;
triangle_indices_size = 0;
quad_indices_size = 0;
m_bounding_box.setEmpty();
}
// Shrink the internal storage to tighly fit the data stored.
void shrink_to_fit() {
this->vertices_and_normals_interleaved.shrink_to_fit();
this->triangle_indices.shrink_to_fit();
this->quad_indices.shrink_to_fit();
}
const BoundingBox& bounding_box() const { return m_bounding_box; }
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const
{
size_t memsize = 0;
if (this->vertices_and_normals_interleaved_VBO_id != 0)
memsize += this->vertices_and_normals_interleaved_size * 4;
if (this->triangle_indices_VBO_id != 0)
memsize += this->triangle_indices_size * 4;
if (this->quad_indices_VBO_id != 0)
memsize += this->quad_indices_size * 4;
return memsize;
}
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
private:
BoundingBox m_bounding_box;
};
extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume);
class GLVolume {
public:
std::string name;
static std::array<float, 4> DISABLED_COLOR;
static std::array<float, 4> SLA_SUPPORT_COLOR;
static std::array<float, 4> SLA_PAD_COLOR;
static std::array<float, 4> NEUTRAL_COLOR;
static std::array<float, 4> UNPRINTABLE_COLOR;
static std::array<std::array<float, 4>, 5> MODEL_COLOR;
static std::array<float, 4> MODEL_MIDIFIER_COL;
static std::array<float, 4> MODEL_NEGTIVE_COL;
static std::array<float, 4> SUPPORT_ENFORCER_COL;
static std::array<float, 4> SUPPORT_BLOCKER_COL;
static std::array<float, 4> MODEL_HIDDEN_COL;
static ColorRGBA DISABLED_COLOR;
static ColorRGBA SLA_SUPPORT_COLOR;
static ColorRGBA SLA_PAD_COLOR;
static ColorRGBA NEUTRAL_COLOR;
static ColorRGBA UNPRINTABLE_COLOR;
static std::array<ColorRGBA, 5> MODEL_COLOR;
static ColorRGBA MODEL_MIDIFIER_COL;
static ColorRGBA MODEL_NEGTIVE_COL;
static ColorRGBA SUPPORT_ENFORCER_COL;
static ColorRGBA SUPPORT_BLOCKER_COL;
static ColorRGBA MODEL_HIDDEN_COL;
static void update_render_colors();
static void load_render_colors();
@@ -288,7 +100,7 @@ public:
};
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const std::array<float, 4>& rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
GLVolume(const ColorRGBA& color) : GLVolume(color.r(), color.g(), color.b(), color.a()) {}
virtual ~GLVolume() = default;
// BBS
@@ -329,9 +141,9 @@ protected:
public:
// Color of the triangles / quads held by this volume.
std::array<float, 4> color;
ColorRGBA color;
// Color used to render this volume.
std::array<float, 4> render_color;
ColorRGBA render_color;
struct CompositeID {
CompositeID(int object_id, int volume_id, int instance_id) : object_id(object_id), volume_id(volume_id), instance_id(instance_id) {}
@@ -394,20 +206,22 @@ public:
bool force_neutral_color : 1;
// Whether or not to force rendering of sinking contours
bool force_sinking_contours : 1;
// Is render for picking
bool picking : 1;
};
// Is mouse or rectangle selection over this object to select/deselect it ?
EHoverState hover;
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
GUI::GLModel model;
// raycaster used for picking
std::unique_ptr<GUI::MeshRaycaster> mesh_raycaster;
// BBS
mutable std::vector<GLIndexedVertexArray> mmuseg_ivas;
mutable std::vector<GUI::GLModel> mmuseg_models;
mutable ObjectBase::Timestamp mmuseg_ts;
// Ranges of triangle and quad indices to be rendered.
std::pair<size_t, size_t> tverts_range;
std::pair<size_t, size_t> qverts_range;
// If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
// of the extrusions per layer.
@@ -417,18 +231,11 @@ public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box() const {
BoundingBoxf3 out;
if (! this->indexed_vertex_array.bounding_box().isEmpty()) {
out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
out.defined = true;
};
return out;
return this->model.get_bounding_box();
}
void set_color(const std::array<float, 4>& rgba);
void set_render_color(float r, float g, float b, float a);
void set_render_color(const std::array<float, 4>& rgba);
void set_color(const ColorRGBA& rgba) { color = rgba; }
void set_render_color(const ColorRGBA& rgba) { render_color = rgba; }
// Sets render color in dependence of current state
void set_render_color();
// set color according to model volume
@@ -517,18 +324,17 @@ public:
// convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
bool empty() const { return this->indexed_vertex_array.empty(); }
bool empty() const { return this->model.is_empty(); }
void set_range(double low, double high);
virtual void render();
//BBS: add outline related logic and add virtual specifier
virtual void render(bool with_outline = false) const;
virtual void render_with_outline(const Transform3d &view_model_matrix);
//BBS: add simple render function for thumbnail
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<std::array<float, 4>>& extruder_colors) const;
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA> extruder_colors);
void set_bounding_boxes_as_dirty() {
m_transformed_bounding_box.reset();
@@ -545,25 +351,26 @@ public:
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
//FIXME what to do wih m_convex_hull?
return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t);
return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) +
this->offsets.capacity() * sizeof(size_t);
}
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
size_t gpu_memory_used() const { return this->model.gpu_memory_used(); }
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
};
// BBS
class GLWipeTowerVolume : public GLVolume {
public:
GLWipeTowerVolume(const std::vector<std::array<float, 4>>& colors);
virtual void render(bool with_outline = false) const;
GLWipeTowerVolume(const std::vector<ColorRGBA>& colors);
void render() override;
void render_with_outline(const Transform3d &view_model_matrix) override { render(); }
std::vector<GLIndexedVertexArray> iva_per_colors;
std::vector<GUI::GLModel> model_per_colors;
bool IsTransparent();
private:
std::vector<std::array<float, 4>> m_colors;
std::vector<ColorRGBA> m_colors;
};
typedef std::vector<GLVolume*> GLVolumePtrs;
@@ -599,10 +406,16 @@ private:
PrintVolume m_render_volume;
// z range for clipping in shaders
float m_z_range[2];
std::array<float, 2> m_z_range;
// plane coeffs for clipping in shaders
float m_clipping_plane[4];
std::array<double, 4> m_clipping_plane;
// plane coeffs for render volumes with different colors in shaders
// used by cut gizmo
std::array<double, 4> m_color_clip_plane;
bool m_use_color_clip_plane{ false };
std::array<ColorRGBA, 2> m_color_clip_plane_colors{ ColorRGBA::RED(), ColorRGBA::BLUE() };
struct Slope
{
@@ -627,19 +440,15 @@ public:
~GLVolumeCollection() { clear(); }
std::vector<int> load_object(
const ModelObject *model_object,
const ModelObject* model_object,
int obj_idx,
const std::vector<int> &instance_idxs,
const std::string &color_by,
bool opengl_initialized);
const std::vector<int>& instance_idxs);
int load_object_volume(
const ModelObject *model_object,
const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx,
const std::string &color_by,
bool opengl_initialized,
bool in_assemble_view = false,
bool use_loaded_id = false);
@@ -651,31 +460,20 @@ public:
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp,
bool opengl_initialized);
size_t timestamp);
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
GLVolume* new_toolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
GLVolume* new_nontoolpath_volume(const std::array<float, 4>& rgba, size_t reserve_vbo_floats = 0);
GLVolume* new_toolpath_volume(const ColorRGBA& rgba);
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba);
int get_selection_support_threshold_angle(bool&) const;
// Render the volumes by OpenGL.
//BBS: add outline drawing logic
void render(ERenderType type,
bool disable_cullface,
const Transform3d & view_matrix,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(),
bool with_outline = true) const;
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix,
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(), bool with_outline = true) const;
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); }
// Release the geometry data assigned to the volumes.
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
// Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }
@@ -685,7 +483,18 @@ public:
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
void set_clipping_plane(const std::array<double, 4>& coeffs) { m_clipping_plane = coeffs; }
const std::array<float, 2>& get_z_range() const { return m_z_range; }
const std::array<double, 4>& get_clipping_plane() const { return m_clipping_plane; }
void set_use_color_clip_plane(bool use) { m_use_color_clip_plane = use; }
void set_color_clip_plane(const Vec3d& cp_normal, double offset) {
for (int i = 0; i < 3; ++i)
m_color_clip_plane[i] = -cp_normal[i];
m_color_clip_plane[3] = offset;
}
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_color_clip_plane_colors = colors; }
bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; }
bool is_slope_active() const { return m_slope.active; }
@@ -726,17 +535,13 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
struct _3DScene
{
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume);
static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume);
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
};
}
+4 -3
View File
@@ -2,6 +2,7 @@
#include "I18N.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
@@ -127,10 +128,10 @@ wxString CopyrightsDialog::get_html_text()
wxColour bgr_clr = wxGetApp().get_window_default_clr();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
const auto text_clr = wxGetApp().get_label_clr_default();// wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue()));
const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()));
const wxString copyright_str = _(L("Copyright")) + "&copy; ";
const wxString copyright_str = _L("Copyright") + "&copy; ";
wxString text = wxString::Format(
"<html>"
+1
View File
@@ -18,6 +18,7 @@ namespace GUI {
class ConfigOptionsGroup;
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
using ConfigOptionsGroupWkp = std::weak_ptr<ConfigOptionsGroup>;
struct BedShape
{
+3 -48
View File
@@ -673,47 +673,6 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
}
bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out)
{
if (scolor.size() == 9) {
unsigned char rgba[4];
parse_color4(scolor, rgba);
rgb_out[0] = rgba[0];
rgb_out[1] = rgba[1];
rgb_out[2] = rgba[2];
return true;
}
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
if (scolor.size() != 7 || scolor.front() != '#')
return false;
const char* c = scolor.data() + 1;
for (size_t i = 0; i < 3; ++i) {
int digit1 = hex_digit_to_int(*c++);
int digit2 = hex_digit_to_int(*c++);
if (digit1 == -1 || digit2 == -1)
return false;
rgb_out[i] = (unsigned char)(digit1 * 16 + digit2);
}
return true;
}
bool BitmapCache::parse_color4(const std::string& scolor, unsigned char* rgba_out)
{
rgba_out[0] = rgba_out[1] = rgba_out[2] = 0; rgba_out[3] = 255;
if ((scolor.size() != 7 && scolor.size() != 9) || scolor.front() != '#')
return false;
const char* c = scolor.data() + 1;
for (size_t i = 0; i < scolor.size() / 2; ++i) {
int digit1 = hex_digit_to_int(*c++);
int digit2 = hex_digit_to_int(*c++);
if (digit1 == -1 || digit2 == -1)
return false;
rgba_out[i] = (unsigned char)(digit1 * 16 + digit2);
}
return true;
}
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/)
{
@@ -781,13 +740,9 @@ wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std
if (color.empty())
bndl = new wxBitmapBundle(mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, size_t(0)));
else {
//OcraftyoneTODO: replace with ColorRGB class
// ColorRGB rgb;// [3]
// decode_color(color, rgb);
unsigned char rgb[3];
parse_color(into_u8(color), rgb);
// bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode));
bndl = new wxBitmapBundle(mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, border_width, dark_mode));
ColorRGB rgb;// [3]
decode_color(color, rgb);
bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode));
}
m_bndl_map[bitmap_key] = bndl;
}
+4 -5
View File
@@ -9,9 +9,11 @@
#include <wx/wx.h>
#endif
#include "libslic3r/Color.hpp"
struct NSVGimage;
namespace Slic3r { namespace GUI {
namespace Slic3r {
namespace GUI {
class BitmapCache
{
@@ -57,15 +59,12 @@ public:
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "", const float scale_in_center = 0.f);
wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false);
wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); }
wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); }
wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, true, 0); }
wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false);
wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false);
wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); }
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
static bool parse_color4(const std::string& scolor, unsigned char* rgba_out);
private:
std::map<std::string, wxBitmap*> m_map;
std::map<std::string, wxBitmapBundle*> m_bndl_map;
+104 -29
View File
@@ -106,6 +106,78 @@ void Camera::select_view(const std::string& direction)
}
}
double Camera::get_near_left() const
{
switch (m_type)
{
case EType::Perspective:
return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) - 1.0) / m_projection_matrix.matrix()(0, 0);
default:
case EType::Ortho:
return -1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3);
}
}
double Camera::get_near_right() const
{
switch (m_type)
{
case EType::Perspective:
return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) + 1.0) / m_projection_matrix.matrix()(0, 0);
default:
case EType::Ortho:
return 1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3);
}
}
double Camera::get_near_top() const
{
switch (m_type)
{
case EType::Perspective:
return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) + 1.0) / m_projection_matrix.matrix()(1, 1);
default:
case EType::Ortho:
return 1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3);
}
}
double Camera::get_near_bottom() const
{
switch (m_type)
{
case EType::Perspective:
return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) - 1.0) / m_projection_matrix.matrix()(1, 1);
default:
case EType::Ortho:
return -1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3);
}
}
double Camera::get_near_width() const
{
switch (m_type)
{
case EType::Perspective:
return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(0, 0);
default:
case EType::Ortho:
return 2.0 / m_projection_matrix.matrix()(0, 0);
}
}
double Camera::get_near_height() const
{
switch (m_type)
{
case EType::Perspective:
return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(1, 1);
default:
case EType::Ortho:
return 2.0 / m_projection_matrix.matrix()(1, 1);
}
}
double Camera::get_fov() const
{
switch (m_type)
@@ -118,17 +190,14 @@ double Camera::get_fov() const
};
}
void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h)
void Camera::set_viewport(int x, int y, unsigned int w, unsigned int h)
{
glsafe(::glViewport(0, 0, w, h));
glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data()));
m_viewport = { 0, 0, int(w), int(h) };
}
void Camera::apply_view_matrix()
void Camera::apply_viewport() const
{
glsafe(::glMatrixMode(GL_MODELVIEW));
glsafe(::glLoadIdentity());
glsafe(::glMultMatrixd(m_view_matrix.data()));
glsafe(::glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]));
}
void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z)
@@ -136,11 +205,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
double w = 0.0;
double h = 0.0;
const double old_distance = m_distance;
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
if (m_distance != old_distance)
// the camera has been moved re-apply view matrix
apply_view_matrix();
if (near_z > 0.0)
m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ);
@@ -174,26 +239,36 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa
}
}
glsafe(::glMatrixMode(GL_PROJECTION));
glsafe(::glLoadIdentity());
apply_projection(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second);
}
void Camera::apply_projection(double left, double right, double bottom, double top, double near_z, double far_z)
{
assert(left != right && bottom != top && near_z != far_z);
const double inv_dx = 1.0 / (right - left);
const double inv_dy = 1.0 / (top - bottom);
const double inv_dz = 1.0 / (far_z - near_z);
switch (m_type)
{
default:
case EType::Ortho:
{
glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
m_projection_matrix.matrix() << 2.0 * inv_dx, 0.0, 0.0, -(left + right) * inv_dx,
0.0, 2.0 * inv_dy, 0.0, -(bottom + top) * inv_dy,
0.0, 0.0, -2.0 * inv_dz, -(near_z + far_z) * inv_dz,
0.0, 0.0, 0.0, 1.0;
break;
}
case EType::Perspective:
{
glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
m_projection_matrix.matrix() << 2.0 * near_z * inv_dx, 0.0, (left + right) * inv_dx, 0.0,
0.0, 2.0 * near_z * inv_dy, (bottom + top) * inv_dy, 0.0,
0.0, 0.0, -(near_z + far_z) * inv_dz, -2.0 * near_z * far_z * inv_dz,
0.0, 0.0, -1.0, 0.0;
break;
}
}
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
glsafe(::glMatrixMode(GL_MODELVIEW));
}
void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor)
@@ -351,8 +426,8 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
// box in eye space
const BoundingBoxf3 eye_box = box.transformed(m_view_matrix);
near_z = -eye_box.max(2);
far_z = -eye_box.min(2);
near_z = -eye_box.max.z();
far_z = -eye_box.min.z();
// apply margin
near_z -= FrustrumZMargin;
@@ -533,19 +608,19 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up
m_distance = (position - target).norm();
const Vec3d new_position = m_target + m_distance * unit_z;
m_view_matrix(0, 0) = unit_x(0);
m_view_matrix(0, 1) = unit_x(1);
m_view_matrix(0, 2) = unit_x(2);
m_view_matrix(0, 0) = unit_x.x();
m_view_matrix(0, 1) = unit_x.y();
m_view_matrix(0, 2) = unit_x.z();
m_view_matrix(0, 3) = -unit_x.dot(new_position);
m_view_matrix(1, 0) = unit_y(0);
m_view_matrix(1, 1) = unit_y(1);
m_view_matrix(1, 2) = unit_y(2);
m_view_matrix(1, 0) = unit_y.x();
m_view_matrix(1, 1) = unit_y.y();
m_view_matrix(1, 2) = unit_y.z();
m_view_matrix(1, 3) = -unit_y.dot(new_position);
m_view_matrix(2, 0) = unit_z(0);
m_view_matrix(2, 1) = unit_z(1);
m_view_matrix(2, 2) = unit_z(2);
m_view_matrix(2, 0) = unit_z.x();
m_view_matrix(2, 1) = unit_z.y();
m_view_matrix(2, 2) = unit_z.z();
m_view_matrix(2, 3) = -unit_z.dot(new_position);
m_view_matrix(3, 0) = 0.0;
+11 -2
View File
@@ -107,14 +107,23 @@ public:
double get_far_z() const { return m_frustrum_zs.second; }
const std::pair<double, double>& get_z_range() const { return m_frustrum_zs; }
double get_near_left() const;
double get_near_right() const;
double get_near_top() const;
double get_near_bottom() const;
double get_near_width() const;
double get_near_height() const;
double get_fov() const;
void apply_viewport(int x, int y, unsigned int w, unsigned int h);
void apply_view_matrix();
void set_viewport(int x, int y, unsigned int w, unsigned int h);
void apply_viewport() const;
// Calculates and applies the projection matrix tighting the frustrum z range around the given box.
// If larger z span is needed, pass the desired values of near and far z (negative values are ignored)
void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0);
void apply_projection(double left, double right, double bottom, double top, double near_z, double far_z);
void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor);
void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor);
+72 -65
View File
@@ -511,7 +511,7 @@ void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::
void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, const bool is_global_config)
{
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
auto gcflavor = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
bool have_volumetric_extrusion_rate_slope = config->option<ConfigOptionFloat>("max_volumetric_extrusion_rate_slope")->value > 0;
@@ -520,27 +520,27 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_line("max_volumetric_extrusion_rate_slope_segment_length", have_volumetric_extrusion_rate_slope);
if(have_volumetric_extrusion_rate_slope) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false));
if(have_volumetric_extrusion_rate_slope_segment_length==0) {
DynamicPrintConfig new_conf = *config;
DynamicPrintConfig new_conf = *config;
new_conf.set_key_value("max_volumetric_extrusion_rate_slope_segment_length", new ConfigOptionInt(1));
apply(config, &new_conf);
apply(config, &new_conf);
}
bool have_perimeters = config->opt_int("wall_loops") > 0;
for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall",
"seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width",
"inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" })
"seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width",
"inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" })
toggle_field(el, have_perimeters);
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
// sparse_infill_filament uses the same logic as in Print::extruders()
for (auto el : { "sparse_infill_pattern", "infill_combination",
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"})
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"})
toggle_line(el, have_infill);
// Only allow configuration of open anchors if the anchoring is enabled.
bool has_infill_anchors = have_infill && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0;
toggle_field("infill_anchor", has_infill_anchors);
bool has_spiral_vase = config->opt_bool("spiral_mode");
bool has_top_solid_infill = config->opt_int("top_shell_layers") > 0;
bool has_bottom_solid_infill = config->opt_int("bottom_shell_layers") > 0;
@@ -548,43 +548,43 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
// solid_infill_filament uses the same logic as in Print::extruders()
for (auto el : { "top_surface_pattern", "bottom_surface_pattern", "internal_solid_infill_pattern", "solid_infill_filament"})
toggle_field(el, has_solid_infill);
for (auto el : { "infill_direction", "sparse_infill_line_width",
"sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle" })
"sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle" })
toggle_field(el, have_infill || has_solid_infill);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill);
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_solid_infill);
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
toggle_field("gap_infill_speed", have_perimeters);
for (auto el : { "top_surface_line_width", "top_surface_speed" })
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
for (auto el : {"outer_wall_acceleration", "inner_wall_acceleration", "initial_layer_acceleration",
"top_surface_acceleration", "travel_acceleration", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration"})
"top_surface_acceleration", "travel_acceleration", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration"})
toggle_field(el, have_default_acceleration);
bool have_default_jerk = config->opt_float("default_jerk") > 0;
for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk","travel_jerk", "infill_jerk"})
toggle_field(el, have_default_jerk);
bool have_skirt = config->opt_int("skirt_loops") > 0;
toggle_field("skirt_height", have_skirt && config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
for (auto el : { "skirt_distance", "draft_shield"})
toggle_field(el, have_skirt);
bool have_brim = (config->opt_enum<BrimType>("brim_type") != btNoBrim);
toggle_field("brim_object_gap", have_brim);
bool have_brim_width = (config->opt_enum<BrimType>("brim_type") != btNoBrim) && config->opt_enum<BrimType>("brim_type") != btAutoBrim;
toggle_field("brim_width", have_brim_width);
// wall_filament uses the same logic as in Print::extruders()
toggle_field("wall_filament", have_perimeters || have_brim);
bool have_brim_ear = (config->opt_enum<BrimType>("brim_type") == btEar);
const auto brim_width = config->opt_float("brim_width");
// disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0
@@ -593,32 +593,32 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
// hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected
toggle_line("brim_ears_max_angle", have_brim_ear);
toggle_line("brim_ears_detection_length", have_brim_ear);
// Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled
toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0);
bool have_raft = config->opt_int("raft_layers") > 0;
bool have_support_material = config->opt_bool("enable_support") || have_raft;
SupportType support_type = config->opt_enum<SupportType>("support_type");
bool have_support_interface = config->opt_int("support_interface_top_layers") > 0 || config->opt_int("support_interface_bottom_layers") > 0;
bool have_support_soluble = have_support_material && config->opt_float("support_top_z_distance") == 0;
auto support_style = config->opt_enum<SupportMaterialStyle>("support_style");
for (auto el : { "support_style", "support_base_pattern",
"support_base_pattern_spacing", "support_expansion", "support_angle",
"support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers",
"bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance",
//BBS: add more support params to dependent of enable_support
"support_type", "support_on_build_plate_only", "support_critical_regions_only",
"support_object_xy_distance"/*, "independent_support_layer_height"*/})
"support_base_pattern_spacing", "support_expansion", "support_angle",
"support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers",
"bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance",
//BBS: add more support params to dependent of enable_support
"support_type", "support_on_build_plate_only", "support_critical_regions_only",
"support_object_xy_distance"/*, "independent_support_layer_height"*/})
toggle_field(el, have_support_material);
toggle_field("support_threshold_angle", have_support_material && is_auto(support_type));
//toggle_field("support_closing_radius", have_support_material && support_style == smsSnug);
bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type);
bool support_is_normal_tree = support_is_tree && support_style != smsOrganic &&
// Orca: use organic as default
support_style != smsDefault;
// Orca: use organic as default
support_style != smsDefault;
bool support_is_organic = support_is_tree && !support_is_normal_tree;
// settings shared by normal and organic trees
for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" })
@@ -629,85 +629,85 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
// settings specific to organic trees
for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall"})
toggle_line(el, support_is_organic);
toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim"));
// non-organic tree support use max_bridge_length instead of bridge_no_support
toggle_line("max_bridge_length", support_is_normal_tree);
toggle_line("bridge_no_support", !support_is_normal_tree);
// This is only supported for auto normal tree
toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_normal_tree);
for (auto el : { "support_interface_spacing", "support_interface_filament",
"support_interface_loop_pattern", "support_bottom_interface_spacing" })
"support_interface_loop_pattern", "support_bottom_interface_spacing" })
toggle_field(el, have_support_material && have_support_interface);
bool have_skirt_height = have_skirt &&
(config->opt_int("skirt_height") > 1 || config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
(config->opt_int("skirt_height") > 1 || config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
toggle_line("support_speed", have_support_material || have_skirt_height);
toggle_line("support_interface_speed", have_support_material && have_support_interface);
// BBS
//toggle_field("support_material_synchronize_layers", have_support_soluble);
toggle_field("inner_wall_line_width", have_perimeters || have_skirt || have_brim);
toggle_field("support_filament", have_support_material || have_skirt);
toggle_line("raft_contact_distance", have_raft && !have_support_soluble);
// Orca: Raft, grid, snug and organic supports use these two parameters to control the size & density of the "brim"/flange
for (auto el : { "raft_first_layer_expansion", "raft_first_layer_density"})
toggle_field(el, have_support_material && !(support_is_normal_tree && !have_raft));
bool has_ironing = (config->opt_enum<IroningType>("ironing_type") != IroningType::NoIroning);
for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_speed", "ironing_angle" })
toggle_line(el, has_ironing);
// bool have_sequential_printing = (config->opt_enum<PrintSequence>("print_sequence") == PrintSequence::ByObject);
// for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" })
// toggle_field(el, have_sequential_printing);
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
toggle_field("standby_temperature_delta", have_ooze_prevention);
bool have_prime_tower = config->opt_bool("enable_prime_tower");
for (auto el : { "prime_tower_width", "prime_tower_brim_width"})
toggle_line(el, have_prime_tower);
bool purge_in_primetower = preset_bundle->printers.get_edited_preset().config.opt_bool("purge_in_prime_tower");
for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_bridging", "wipe_tower_no_sparse_layers"})
toggle_line(el, have_prime_tower && purge_in_primetower);
toggle_line("prime_volume",have_prime_tower && !purge_in_primetower);
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})
toggle_field(el, have_prime_tower);
// BBS: MusangKing - Hide "Independent support layer height" option
// BBS: MusangKing - Hide "Independent support layer height" option
toggle_line("independent_support_layer_height", have_support_material && !have_prime_tower);
bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall");
toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters);
bool has_overhang_speed = config->opt_bool("enable_overhang_speed");
for (auto el :
{"overhang_speed_classic", "overhang_1_4_speed",
"overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"})
"overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"})
toggle_line(el, has_overhang_speed);
bool has_overhang_speed_classic = config->opt_bool("overhang_speed_classic");
toggle_line("slowdown_for_curled_perimeters",!has_overhang_speed_classic && has_overhang_speed);
toggle_line("flush_into_objects", !is_global_config);
bool has_fuzzy_skin = (config->opt_enum<FuzzySkinType>("fuzzy_skin") != FuzzySkinType::None);
for (auto el : { "fuzzy_skin_thickness", "fuzzy_skin_point_distance"})
toggle_line(el, has_fuzzy_skin);
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"min_feature_size", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"})
"min_feature_size", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"})
toggle_line(el, have_arachne);
toggle_field("detect_thin_wall", !have_arachne);
@@ -719,23 +719,30 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_line(el, gcflavor == gcfKlipper);
if(gcflavor == gcfKlipper)
toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable"));
bool have_make_overhang_printable = config->opt_bool("make_overhang_printable");
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
toggle_line("exclude_object", gcflavor == gcfKlipper);
toggle_line("min_width_top_surface",config->opt_bool("only_one_wall_top"));
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" })
toggle_line(el, config->opt_bool("hole_to_polyhole"));
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase;
toggle_field("overhang_reverse", allow_overhang_reverse);
toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse);
bool has_overhang_reverse_internal_only = config->opt_bool("overhang_reverse_internal_only");
if (has_overhang_reverse_internal_only){
DynamicPrintConfig new_conf = *config;
new_conf.set_key_value("overhang_reverse_threshold", new ConfigOptionFloatOrPercent(0,true));
apply(config, &new_conf);
}
toggle_line("timelapse_type", is_BBL_Printer);
}
+4 -3
View File
@@ -36,6 +36,7 @@
#include "libslic3r/Config.hpp"
#include "libslic3r/libslic3r.h"
#include "libslic3r/Model.hpp"
#include "libslic3r/Color.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GUI_Utils.hpp"
@@ -777,9 +778,9 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
// wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
//#endif
//#endif
const auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const auto text_clr = wxGetApp().get_label_clr_default();//wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
const auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
const auto text_clr = wxGetApp().get_label_clr_default();
const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()));
const auto text_clr_str = encode_color(ColorRGB(text_clr.Red(), text_clr.Green(), text_clr.Blue()));
wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
wxString text;
if (all_printers) {
+1 -2
View File
@@ -1619,8 +1619,7 @@ boost::any& ColourPicker::get_value()
if (colour == wxTransparentColour)
m_value = std::string("");
else {
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
m_value = clr_str.ToStdString();
m_value = encode_color(ColorRGB(colour.Red(), colour.Green(), colour.Blue()));
}
return m_value;
}
File diff suppressed because it is too large Load Diff
+24 -34
View File
@@ -34,7 +34,6 @@ static const float SLIDER_BOTTOM_MARGIN = 64.0f;
class GCodeViewer
{
using IBufferType = unsigned short;
using Color = std::array<float, 4>;
using VertexBuffer = std::vector<float>;
using MultiVertexBuffer = std::vector<VertexBuffer>;
using IndexBuffer = std::vector<IBufferType>;
@@ -43,12 +42,12 @@ class GCodeViewer
using InstanceIdBuffer = std::vector<size_t>;
using InstancesOffsets = std::vector<Vec3f>;
static const std::vector<Color> Extrusion_Role_Colors;
static const std::vector<Color> Options_Colors;
static const std::vector<Color> Travel_Colors;
static const std::vector<Color> Range_Colors;
static const Color Wipe_Color;
static const Color Neutral_Color;
static const std::vector<ColorRGBA> Extrusion_Role_Colors;
static const std::vector<ColorRGBA> Options_Colors;
static const std::vector<ColorRGBA> Travel_Colors;
static const std::vector<ColorRGBA> Range_Colors;
static const ColorRGBA Wipe_Color;
static const ColorRGBA Neutral_Color;
enum class EOptionsColors : unsigned char
{
@@ -133,7 +132,7 @@ class GCodeViewer
// vbo id
unsigned int vbo{ 0 };
// Color to apply to the instances
Color color;
ColorRGBA color;
};
std::vector<Range> ranges;
@@ -256,7 +255,7 @@ class GCodeViewer
// Index of the parent tbuffer
unsigned char tbuffer_id;
// Render path property
Color color;
ColorRGBA color;
// Index of the buffer in TBuffer::indices
unsigned int ibuffer_id;
// Render path content
@@ -276,12 +275,10 @@ class GCodeViewer
bool operator() (const RenderPath &l, const RenderPath &r) const {
if (l.tbuffer_id < r.tbuffer_id)
return true;
for (int i = 0; i < 3; ++i) {
if (l.color[i] < r.color[i])
return true;
else if (l.color[i] > r.color[i])
return false;
}
if (l.color < r.color)
return true;
else if (l.color > r.color)
return false;
return l.ibuffer_id < r.ibuffer_id;
}
};
@@ -296,7 +293,6 @@ class GCodeViewer
{
enum class ERenderPrimitiveType : unsigned char
{
Point,
Line,
Triangle,
InstancedModel,
@@ -312,9 +308,9 @@ class GCodeViewer
struct Model
{
GLModel model;
Color color;
ColorRGBA color;
InstanceVBuffer instances;
GLModel::InitializationData data;
GLModel::Geometry data;
void reset();
};
@@ -337,7 +333,6 @@ class GCodeViewer
unsigned int max_vertices_per_segment() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 8; }
default: { return 0; }
@@ -349,7 +344,6 @@ class GCodeViewer
unsigned int indices_per_segment() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles
default: { return 0; }
@@ -359,7 +353,6 @@ class GCodeViewer
unsigned int max_indices_per_segment() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles
default: { return 0; }
@@ -370,14 +363,13 @@ class GCodeViewer
bool has_data() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point:
case ERenderPrimitiveType::Line:
case ERenderPrimitiveType::Triangle: {
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
}
case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
case ERenderPrimitiveType::BatchedModel: {
return model.data.vertices_count() > 0 && model.data.indices_count() &&
return !model.data.vertices.empty() && !model.data.indices.empty() &&
!vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
}
default: { return false; }
@@ -416,7 +408,7 @@ class GCodeViewer
void reset(bool log = false) { min = FLT_MAX; max = -FLT_MAX; count = 0; log_scale = log; }
float step_size() const;
Color get_color_at(float value) const;
ColorRGBA get_color_at(float value) const;
float get_value_at_step(int step) const;
};
@@ -519,7 +511,7 @@ Range layer_duration_log;
TBuffer* buffer{ nullptr };
unsigned int ibo{ 0 };
unsigned int vbo{ 0 };
Color color;
ColorRGBA color;
~SequentialRangeCap();
bool is_renderable() const { return buffer != nullptr; }
@@ -539,7 +531,6 @@ Range layer_duration_log;
int64_t refresh_time{ 0 };
int64_t refresh_paths_time{ 0 };
// opengl calls
int64_t gl_multi_points_calls_count{ 0 };
int64_t gl_multi_lines_calls_count{ 0 };
int64_t gl_multi_triangles_calls_count{ 0 };
int64_t gl_triangles_calls_count{ 0 };
@@ -582,7 +573,6 @@ Range layer_duration_log;
}
void reset_opengl() {
gl_multi_points_calls_count = 0;
gl_multi_lines_calls_count = 0;
gl_multi_triangles_calls_count = 0;
gl_triangles_calls_count = 0;
@@ -645,7 +635,7 @@ public:
bool is_visible() const { return m_visible; }
void set_visible(bool visible) { m_visible = visible; }
void render(int canvas_width, int canvas_height, const EViewType& view_type) const;
void render(int canvas_width, int canvas_height, const EViewType& view_type);
void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; }
void update_curr_move(const GCodeProcessorResult::MoveVertex move);
@@ -712,12 +702,12 @@ public:
std::vector<unsigned int> gcode_ids;
float m_scale = 1.0;
bool m_show_gcode_window = false;
void render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type) const;
void render(const bool has_render_path, float legend_height, int canvas_width, int canvas_height, int right_margin, const EViewType& view_type);
};
struct ETools
{
std::vector<Color> m_tool_colors;
std::vector<ColorRGBA> m_tool_colors;
std::vector<bool> m_tool_visibles;
};
@@ -813,7 +803,7 @@ public:
// extract rendering data from the given parameters
//BBS: add only gcode mode
void load(const GCodeProcessorResult& gcode_result, const Print& print, const BuildVolume& build_volume,
const std::vector<BoundingBoxf3>& exclude_bounding_box, bool initialized, ConfigOptionMode mode, bool only_gcode = false);
const std::vector<BoundingBoxf3>& exclude_bounding_box, ConfigOptionMode mode, bool only_gcode = false);
// recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
void refresh_render_paths();
@@ -823,7 +813,7 @@ public:
void reset();
//BBS: always load shell at preview
void reset_shell();
void load_shells(const Print& print, bool initialized, bool force_previewing = false);
void load_shells(const Print& print, bool force_previewing = false);
void set_shells_on_preview(bool is_previewing) { m_shells.previewing = is_previewing; }
//BBS: add all plates filament statistics
void render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show = true) const;
@@ -903,7 +893,7 @@ public:
private:
void load_toolpaths(const GCodeProcessorResult& gcode_result, const BuildVolume& build_volume, const std::vector<BoundingBoxf3>& exclude_bounding_box);
//BBS: always load shell at preview
//void load_shells(const Print& print, bool initialized);
//void load_shells(const Print& print);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths();
void render_shells();
@@ -920,7 +910,7 @@ private:
}
bool is_visible(const Path& path) const { return is_visible(path.role); }
void log_memory_used(const std::string& label, int64_t additional = 0) const;
Color option_color(EMoveType move_type) const;
ColorRGBA option_color(EMoveType move_type) const;
};
} // namespace GUI
File diff suppressed because it is too large Load Diff
+78 -54
View File
@@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral
///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLCanvas3D_hpp_
#define slic3r_GLCanvas3D_hpp_
@@ -16,6 +21,7 @@
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GCodeViewer.hpp"
#include "Camera.hpp"
#include "SceneRaycaster.hpp"
#include "IMToolbar.hpp"
#include "libslic3r/Slicing.hpp"
@@ -62,25 +68,24 @@ class RetinaHelper;
class Size
{
int m_width;
int m_height;
float m_scale_factor;
int m_width{ 0 };
int m_height{ 0 };
float m_scale_factor{ 1.0f };
public:
Size();
Size(int width, int height, float scale_factor = 1.0);
Size() = default;
Size(int width, int height, float scale_factor = 1.0f) : m_width(width), m_height(height), m_scale_factor(scale_factor) {}
int get_width() const;
void set_width(int width);
int get_width() const { return m_width; }
void set_width(int width) { m_width = width; }
int get_height() const;
void set_height(int height);
int get_height() const { return m_height; }
void set_height(int height) { m_height = height; }
int get_scale_factor() const;
void set_scale_factor(int height);
float get_scale_factor() const { return m_scale_factor; }
void set_scale_factor(float factor) { m_scale_factor = factor; }
};
class RenderTimerEvent : public wxEvent
{
public:
@@ -199,14 +204,6 @@ class GLCanvas3D
static const double DefaultCameraZoomToBedMarginFactor;
static const double DefaultCameraZoomToPlateMarginFactor;
static float DEFAULT_BG_LIGHT_COLOR[3];
static float ERROR_BG_LIGHT_COLOR[3];
static float DEFAULT_BG_LIGHT_COLOR_LIGHT[3];
static float ERROR_BG_LIGHT_COLOR_LIGHT[3];
static float DEFAULT_BG_LIGHT_COLOR_DARK[3];
static float ERROR_BG_LIGHT_COLOR_DARK[3];
static void update_render_colors();
static void load_render_colors();
@@ -265,6 +262,15 @@ class GLCanvas3D
int last_object_id{ -1 };
float last_z{ 0.0f };
LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE };
struct Profile
{
GLModel baseline;
GLModel profile;
GLModel background;
float old_canvas_width{ 0.0f };
std::vector<double> old_layer_height_profile;
};
Profile m_profile;
LayersEditing() = default;
~LayersEditing();
@@ -293,7 +299,6 @@ class GLCanvas3D
static float get_cursor_z_relative(const GLCanvas3D& canvas);
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; }
float object_max_z() const { return m_object_max_z; }
@@ -303,10 +308,8 @@ class GLCanvas3D
private:
bool is_initialized() const;
void generate_layer_height_texture();
void render_background_texture(const GLCanvas3D& canvas, const Rect& bar_rect);
void render_curve(const Rect& bar_rect);
void render_active_object_annotations(const GLCanvas3D& canvas);
void render_profile(const GLCanvas3D& canvas);
void update_slicing_parameters();
static float thickness_bar_width(const GLCanvas3D& canvas);
@@ -356,12 +359,12 @@ class GLCanvas3D
{
struct Triangles
{
Pointf3s object;
Pointf3s supports;
GLModel object;
GLModel supports;
};
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
typedef std::map<unsigned int, Triangles> ObjectIdToModelsMap;
double z;
ObjectIdToTrianglesMap triangles;
ObjectIdToModelsMap triangles;
SlaCap() { reset(); }
void reset() { z = DBL_MAX; triangles.clear(); }
@@ -504,6 +507,7 @@ private:
bool m_is_dark = false;
wxGLCanvas* m_canvas;
wxGLContext* m_context;
SceneRaycaster m_scene_raycaster;
Bed3D &m_bed;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
@@ -527,7 +531,7 @@ private:
std::array<ClippingPlane, 2> m_clipping_planes;
ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes;
SlaCap m_sla_caps[2];
std::array<SlaCap, 2> m_sla_caps;
std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
@@ -578,14 +582,8 @@ private:
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)
bool m_render_sla_auxiliaries;
std::string m_color_by;
bool m_reload_delayed;
#if ENABLE_RENDER_PICKING_PASS
bool m_show_picking_texture;
#endif // ENABLE_RENDER_PICKING_PASS
RenderStats m_render_stats;
int m_imgui_undo_redo_hovered_pos{ -1 };
@@ -706,6 +704,16 @@ public:
}
m_gizmo_highlighter;
#if ENABLE_SHOW_CAMERA_TARGET
struct CameraTarget
{
std::array<GLModel, 3> axis;
Vec3d target{ Vec3d::Zero() };
};
CameraTarget m_camera_target;
#endif // ENABLE_SHOW_CAMERA_TARGET
GLModel m_background;
public:
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
~GLCanvas3D();
@@ -722,6 +730,25 @@ public:
bool init();
void post_event(wxEvent &&event);
std::shared_ptr<SceneRaycasterItem> add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster,
const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) {
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces);
}
void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) {
m_scene_raycaster.remove_raycasters(type, id);
}
void remove_raycasters_for_picking(SceneRaycaster::EType type) {
m_scene_raycaster.remove_raycasters(type);
}
std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters_for_picking(SceneRaycaster::EType type) {
return m_scene_raycaster.get_raycasters(type);
}
void set_raycaster_gizmos_on_top(bool value) {
m_scene_raycaster.set_gizmos_on_top(value);
}
void reset_explosion_ratio() { m_explosion_ratio = 1.0; }
void on_change_color_mode(bool is_dark, bool reinit = true);
const bool get_dark_mode_status() { return m_is_dark; }
@@ -780,7 +807,9 @@ public:
bool get_use_clipping_planes() const { return m_use_clipping_planes; }
const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
void set_color_by(const std::string& value);
void set_use_color_clip_plane(bool use) { m_volumes.set_use_color_clip_plane(use); }
void set_color_clip_plane(const Vec3d& cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); }
void set_color_clip_plane_colors(const std::array<ColorRGBA, 2>& colors) { m_volumes.set_color_clip_plane_colors(colors); }
void refresh_camera_scene_box();
@@ -857,15 +886,15 @@ public:
void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
const GLVolumeCollection& volumes, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false);
static void render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects,
const GLVolumeCollection& volumes, std::vector<std::array<float, 4>>& extruder_colors,
const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors,
GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false);
// render thumbnail using an off-screen framebuffer
static void render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<std::array<float, 4>>& extruder_colors,
PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors,
GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false);
// render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported
static void render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params,
PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<std::array<float, 4>>& extruder_colors,
PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors,
GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false);
//BBS use gcoder viewer render calibration thumbnails
@@ -937,7 +966,6 @@ public:
void do_move(const std::string& snapshot_type);
void do_rotate(const std::string& snapshot_type);
void do_scale(const std::string& snapshot_type);
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
void do_center();
void do_mirror(const std::string& snapshot_type);
@@ -1111,27 +1139,25 @@ private:
void _picking_pass();
void _rectangular_selection_picking_pass();
void _render_background() const;
void _render_bed(bool bottom, bool show_axes);
void _render_bed_for_picking(bool bottom);
void _render_background();
void _render_bed(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_axes);
//BBS: add part plate related logic
void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false) const;
void _render_plates_for_picking() const;
void _render_platelist(const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false);
//BBS: add outline drawing logic
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
//BBS: GUI refactor: add canvas size as parameters
void _render_gcode(int canvas_width, int canvas_height);
//BBS: render a plane for assemble
void _render_plane() const;
void _render_selection() const;
void _render_selection();
void _render_sequential_clearance();
#if ENABLE_RENDER_SELECTION_CENTER
void _render_selection_center() const;
void _render_selection_center();
#endif // ENABLE_RENDER_SELECTION_CENTER
void _check_and_update_toolbar_icon_scale();
void _render_overlays();
void _render_style_editor();
void _render_volumes_for_picking() const;
void _render_volumes_for_picking(const Camera& camera) const;
void _render_current_gizmo() const;
void _render_gizmos_overlay();
void _render_main_toolbar();
@@ -1147,15 +1173,15 @@ private:
void _render_assemble_control() const;
void _render_assemble_info() const;
#if ENABLE_SHOW_CAMERA_TARGET
void _render_camera_target() const;
void _render_camera_target();
#endif // ENABLE_SHOW_CAMERA_TARGET
void _render_sla_slices();
void _render_selection_sidebar_hints() const;
void _render_selection_sidebar_hints();
//BBS: GUI refactor: adjust main toolbar position
bool _render_orient_menu(float left, float right, float bottom, float top);
bool _render_arrange_menu(float left, float right, float bottom, float top);
// render thumbnail using the default framebuffer
void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<std::array<float, 4>>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type);
void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type);
void _update_volumes_hover_state();
@@ -1201,8 +1227,6 @@ private:
// BBS FIXME
float get_overlay_window_width() { return 0; /*LayersEditing::get_overlay_window_width();*/ }
static std::vector<std::array<float, 4>> _parse_colors(const std::vector<std::string>& colors);
};
} // namespace GUI
+1143 -471
View File
File diff suppressed because it is too large Load Diff
+181 -42
View File
@@ -1,8 +1,13 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLModel_hpp_
#define slic3r_GLModel_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Color.hpp"
#include <vector>
#include <string>
@@ -13,53 +18,139 @@ namespace Slic3r {
class TriangleMesh;
class Polygon;
using Polygons = std::vector<Polygon>;
class BuildVolume;
namespace GUI {
class GLModel
{
public:
enum class PrimitiveType : unsigned char
struct Geometry
{
Triangles,
Lines,
LineStrip,
LineLoop
enum class EPrimitiveType : unsigned char
{
Points,
Triangles,
TriangleStrip,
TriangleFan,
Lines,
LineStrip,
LineLoop
};
enum class EVertexLayout : unsigned char
{
P2, // position 2 floats
P2T2, // position 2 floats + texture coords 2 floats
P3, // position 3 floats
P3T2, // position 3 floats + texture coords 2 floats
P3N3, // position 3 floats + normal 3 floats
P3N3T2, // position 3 floats + normal 3 floats + texture coords 2 floats
P4, // position 4 floats
};
enum class EIndexType : unsigned char
{
UINT, // unsigned int
USHORT, // unsigned short
UBYTE // unsigned byte
};
struct Format
{
EPrimitiveType type{ EPrimitiveType::Triangles };
EVertexLayout vertex_layout{ EVertexLayout::P3N3 };
};
Format format;
std::vector<float> vertices;
std::vector<unsigned int> indices;
EIndexType index_type{ EIndexType::UINT };
ColorRGBA color{ ColorRGBA::BLACK() };
void reserve_vertices(size_t vertices_count) { vertices.reserve(vertices_count * vertex_stride_floats(format)); }
void reserve_indices(size_t indices_count) { indices.reserve(indices_count); }
void add_vertex(const Vec2f& position); // EVertexLayout::P2
void add_vertex(const Vec2f& position, const Vec2f& tex_coord); // EVertexLayout::P2T2
void add_vertex(const Vec3f& position); // EVertexLayout::P3
void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2
void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
void add_vertex(const Vec3f& position, const Vec3f& normal, const Vec2f& tex_coord); // EVertexLayout::P3N3T2
void add_vertex(const Vec4f& position); // EVertexLayout::P4
void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
void set_index(size_t id, unsigned int index);
void add_index(unsigned int id);
void add_line(unsigned int id1, unsigned int id2);
void add_triangle(unsigned int id1, unsigned int id2, unsigned int id3);
Vec2f extract_position_2(size_t id) const;
Vec3f extract_position_3(size_t id) const;
Vec3f extract_normal_3(size_t id) const;
Vec2f extract_tex_coord_2(size_t id) const;
unsigned int extract_index(size_t id) const;
void remove_vertex(size_t id);
bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; }
size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); }
size_t indices_count() const { return indices.size(); }
size_t vertices_size_floats() const { return vertices.size(); }
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); }
indexed_triangle_set get_as_indexed_triangle_set() const;
static size_t vertex_stride_floats(const Format& format);
static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); }
static size_t position_stride_floats(const Format& format);
static size_t position_stride_bytes(const Format& format) { return position_stride_floats(format) * sizeof(float); }
static size_t position_offset_floats(const Format& format);
static size_t position_offset_bytes(const Format& format) { return position_offset_floats(format) * sizeof(float); }
static size_t normal_stride_floats(const Format& format);
static size_t normal_stride_bytes(const Format& format) { return normal_stride_floats(format) * sizeof(float); }
static size_t normal_offset_floats(const Format& format);
static size_t normal_offset_bytes(const Format& format) { return normal_offset_floats(format) * sizeof(float); }
static size_t tex_coord_stride_floats(const Format& format);
static size_t tex_coord_stride_bytes(const Format& format) { return tex_coord_stride_floats(format) * sizeof(float); }
static size_t tex_coord_offset_floats(const Format& format);
static size_t tex_coord_offset_bytes(const Format& format) { return tex_coord_offset_floats(format) * sizeof(float); }
static size_t index_stride_bytes(const Geometry& data);
static bool has_position(const Format& format);
static bool has_normal(const Format& format);
static bool has_tex_coord(const Format& format);
};
struct RenderData
{
PrimitiveType type;
Geometry geometry;
unsigned int vbo_id{ 0 };
unsigned int ibo_id{ 0 };
size_t vertices_count{ 0 };
size_t indices_count{ 0 };
std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f };
};
struct InitializationData
{
struct Entity
{
PrimitiveType type;
std::vector<Vec3f> positions;
std::vector<Vec3f> normals;
std::vector<unsigned int> indices;
std::array<float, 4> color{ 1.0f, 1.0f, 1.0f, 1.0f };
};
std::vector<Entity> entities;
size_t vertices_count() const;
size_t vertices_size_floats() const { return vertices_count() * 6; }
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
size_t indices_count() const;
size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); }
};
private:
std::vector<RenderData> m_render_data;
RenderData m_render_data;
// By default the vertex and index buffers data are sent to gpu at the first call to render() method.
// If you need to initialize a model from outside the main thread, so that a call to render() may happen
// before the initialization is complete, use the methods:
// disable_render()
// ... do your initialization ...
// enable_render()
// to keep the data on cpu side until needed.
bool m_render_disabled{ false };
BoundingBoxf3 m_bounding_box;
std::string m_filename;
@@ -67,50 +158,98 @@ namespace GUI {
GLModel() = default;
virtual ~GLModel() { reset(); }
void init_from(const InitializationData& data);
void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
size_t vertices_count() const { return m_render_data.vertices_count > 0 ?
m_render_data.vertices_count : m_render_data.geometry.vertices_count(); }
size_t indices_count() const { return m_render_data.indices_count > 0 ?
m_render_data.indices_count : m_render_data.geometry.indices_count(); }
size_t vertices_size_floats() const { return vertices_count() * Geometry::vertex_stride_floats(m_render_data.geometry.format); }
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry); }
const Geometry& get_geometry() const { return m_render_data.geometry; }
void init_from(Geometry&& data);
void init_from(const TriangleMesh& mesh);
void init_from(const indexed_triangle_set& its);
void init_from(const Polygons& polygons, float z);
bool init_from_file(const std::string& filename);
// if entity_id == -1 set the color of all entities
void set_color(int entity_id, const std::array<float, 4>& color);
void set_color(const ColorRGBA& color) { m_render_data.geometry.color = color; }
const ColorRGBA& get_color() const { return m_render_data.geometry.color; }
void reset();
void render() const;
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
void render();
void render(const std::pair<size_t, size_t>& range);
void render_instanced(unsigned int instances_vbo, unsigned int instances_count);
bool is_initialized() const { return !m_render_data.empty(); }
bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; }
bool is_empty() const { return m_render_data.geometry.is_empty(); }
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
const std::string& get_filename() const { return m_filename; }
bool is_render_disabled() const { return m_render_disabled; }
void enable_render() { m_render_disabled = false; }
void disable_render() { m_render_disabled = true; }
size_t cpu_memory_used() const {
size_t ret = 0;
if (!m_render_data.geometry.vertices.empty())
ret += vertices_size_bytes();
if (!m_render_data.geometry.indices.empty())
ret += indices_size_bytes();
return ret;
}
size_t gpu_memory_used() const {
size_t ret = 0;
if (m_render_data.geometry.vertices.empty())
ret += vertices_size_bytes();
if (m_render_data.geometry.indices.empty())
ret += indices_size_bytes();
return ret;
}
private:
void send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices);
bool send_to_gpu();
};
bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true);
// create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
// the origin of the arrow is in the center of the stem cap
// the arrow has its axis of symmetry along the Z axis and is pointing upward
// used to render bed axes and sequential marker
GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height);
GLModel::Geometry stilized_arrow(unsigned int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height);
// create an arrow whose stem is a quarter of circle, with the given dimensions and resolution
// the origin of the arrow is in the center of the circle
// the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise
// used to render sidebar hints for rotations
GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness);
GLModel::Geometry circular_arrow(unsigned int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness);
// create an arrow with the given dimensions
// the origin of the arrow is in the center of the stem cap
// the arrow is contained in XY plane and has its main axis along the Y axis
// used to render sidebar hints for position and scale
GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
GLModel::Geometry straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
// create a diamond with the given resolution
// the origin of the diamond is in its center
// the diamond is contained into a box with size [1, 1, 1]
GLModel::InitializationData diamond(int resolution);
GLModel::Geometry diamond(unsigned int resolution);
// create a sphere with smooth normals
// the origin of the sphere is in its center
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius);
// create a cylinder with smooth normals
// the axis of the cylinder is the Z axis
// the origin of the cylinder is the center of its bottom cap face
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height);
// create a torus with smooth normals
// the axis of the torus is the Z axis
// the origin of the torus is in its center
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
} // namespace GUI
} // namespace Slic3r
+60 -60
View File
@@ -1,5 +1,10 @@
///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLSelectionRectangle.hpp"
#include "Camera.hpp"
#include "CameraUtils.hpp"
#include "3DScene.hpp"
#include "GLCanvas3D.hpp"
#include "GUI_App.hpp"
@@ -29,35 +34,19 @@ namespace GUI {
m_end_corner = mouse_position;
}
std::vector<unsigned int> GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points)
std::vector<unsigned int> GLSelectionRectangle::contains(const std::vector<Vec3d>& points) const
{
std::vector<unsigned int> out;
if (!is_dragging())
return out;
m_state = Off;
const Camera& camera = wxGetApp().plater()->get_camera();
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection= camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
// Convert our std::vector to Eigen dynamic matrix.
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> pts(points.size(), 3);
for (size_t i=0; i<points.size(); ++i)
pts.block<1, 3>(i, 0) = points[i];
// Get the projections.
Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::DontAlign> projections;
igl::project(pts, modelview, projection, viewport, projections);
// bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
const BoundingBox rectangle(Points{ Point(m_start_corner.cast<coord_t>()), Point(m_end_corner.cast<coord_t>()) });
// Iterate over all points and determine whether they're in the rectangle.
for (int i = 0; i<projections.rows(); ++i)
if (rectangle.contains(Point(projections(i, 0), canvas.get_canvas_size().get_height() - projections(i, 1))))
const Camera &camera = wxGetApp().plater()->get_camera();
Points points_2d = CameraUtils::project(camera, points);
unsigned int size = static_cast<unsigned int>(points.size());
for (unsigned int i = 0; i< size; ++i)
if (rectangle.contains(points_2d[i]))
out.push_back(i);
return out;
@@ -69,59 +58,70 @@ namespace GUI {
m_state = Off;
}
void GLSelectionRectangle::render(const GLCanvas3D& canvas) const
void GLSelectionRectangle::render(const GLCanvas3D& canvas)
{
if (!is_dragging())
return;
const Camera& camera = wxGetApp().plater()->get_camera();
float inv_zoom = (float)camera.get_inv_zoom();
Size cnv_size = canvas.get_canvas_size();
float cnv_half_width = 0.5f * (float)cnv_size.get_width();
float cnv_half_height = 0.5f * (float)cnv_size.get_height();
if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f))
const Size cnv_size = canvas.get_canvas_size();
const float cnv_width = (float)cnv_size.get_width();
const float cnv_height = (float)cnv_size.get_height();
if (cnv_width == 0.0f || cnv_height == 0.0f)
return;
Vec2d start(m_start_corner(0) - cnv_half_width, cnv_half_height - m_start_corner(1));
Vec2d end(m_end_corner(0) - cnv_half_width, cnv_half_height - m_end_corner(1));
float left = (float)std::min(start(0), end(0)) * inv_zoom;
float top = (float)std::max(start(1), end(1)) * inv_zoom;
float right = (float)std::max(start(0), end(0)) * inv_zoom;
float bottom = (float)std::min(start(1), end(1)) * inv_zoom;
const float cnv_inv_width = 1.0f / cnv_width;
const float cnv_inv_height = 1.0f / cnv_height;
const float left = 2.0f * (get_left() * cnv_inv_width - 0.5f);
const float right = 2.0f * (get_right() * cnv_inv_width - 0.5f);
const float top = -2.0f * (get_top() * cnv_inv_height - 0.5f);
const float bottom = -2.0f * (get_bottom() * cnv_inv_height - 0.5f);
glsafe(::glLineWidth(1.5f));
float color[3];
color[0] = 0.00f;
color[1] = 1.00f;
color[2] = 0.38f;
glsafe(::glColor3fv(color));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
// ensure that the rectangle is renderered inside the frustrum
glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5)));
// ensure that the overlay fits the frustrum near z plane
double gui_scale = camera.get_gui_scale();
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
glsafe(::glPushAttrib(GL_ENABLE_BIT));
glsafe(::glLineStipple(4, 0xAAAA));
glsafe(::glEnable(GL_LINE_STIPPLE));
::glBegin(GL_LINE_LOOP);
::glVertex2f((GLfloat)left, (GLfloat)bottom);
::glVertex2f((GLfloat)right, (GLfloat)bottom);
::glVertex2f((GLfloat)right, (GLfloat)top);
::glVertex2f((GLfloat)left, (GLfloat)top);
glsafe(::glEnd());
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
if (!m_rectangle.is_initialized() || !m_old_start_corner.isApprox(m_start_corner) || !m_old_end_corner.isApprox(m_end_corner)) {
m_old_start_corner = m_start_corner;
m_old_end_corner = m_end_corner;
m_rectangle.reset();
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P2 };
init_data.reserve_vertices(4);
init_data.reserve_indices(4);
// vertices
init_data.add_vertex(Vec2f(left, bottom));
init_data.add_vertex(Vec2f(right, bottom));
init_data.add_vertex(Vec2f(right, top));
init_data.add_vertex(Vec2f(left, top));
// indices
init_data.add_index(0);
init_data.add_index(1);
init_data.add_index(2);
init_data.add_index(3);
m_rectangle.init_from(std::move(init_data));
}
shader->set_uniform("view_model_matrix", Transform3d::Identity());
shader->set_uniform("projection_matrix", Transform3d::Identity());
m_rectangle.set_color({0.0f, 1.0f, 0.38f, 1.0f});
m_rectangle.render();
shader->stop_using();
}
glsafe(::glPopAttrib());
glsafe(::glPopMatrix());
}
} // namespace GUI
+20 -12
View File
@@ -1,7 +1,12 @@
///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLSelectionRectangle_hpp_
#define slic3r_GLSelectionRectangle_hpp_
#include "libslic3r/Point.hpp"
#include "GLModel.hpp"
namespace Slic3r {
namespace GUI {
@@ -24,28 +29,31 @@ public:
void dragging(const Vec2d& mouse_position);
// Given a vector of points in world coordinates, the function returns indices of those
// that are in the rectangle. It then disables the rectangle.
std::vector<unsigned int> stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points);
// that are in the rectangle.
std::vector<unsigned int> contains(const std::vector<Vec3d>& points) const;
// Disables the rectangle.
void stop_dragging();
void render(const GLCanvas3D& canvas) const;
void render(const GLCanvas3D& canvas);
bool is_dragging() const { return m_state != Off; }
EState get_state() const { return m_state; }
float get_width() const { return std::abs(m_start_corner(0) - m_end_corner(0)); }
float get_height() const { return std::abs(m_start_corner(1) - m_end_corner(1)); }
float get_left() const { return std::min(m_start_corner(0), m_end_corner(0)); }
float get_right() const { return std::max(m_start_corner(0), m_end_corner(0)); }
float get_top() const { return std::max(m_start_corner(1), m_end_corner(1)); }
float get_bottom() const { return std::min(m_start_corner(1), m_end_corner(1)); }
float get_width() const { return std::abs(m_start_corner.x() - m_end_corner.x()); }
float get_height() const { return std::abs(m_start_corner.y() - m_end_corner.y()); }
float get_left() const { return std::min(m_start_corner.x(), m_end_corner.x()); }
float get_right() const { return std::max(m_start_corner.x(), m_end_corner.x()); }
float get_top() const { return std::max(m_start_corner.y(), m_end_corner.y()); }
float get_bottom() const { return std::min(m_start_corner.y(), m_end_corner.y()); }
private:
EState m_state = Off;
Vec2d m_start_corner;
Vec2d m_end_corner;
EState m_state{ Off };
Vec2d m_start_corner{ Vec2d::Zero() };
Vec2d m_end_corner{ Vec2d::Zero() };
GLModel m_rectangle;
Vec2d m_old_start_corner{ Vec2d::Zero() };
Vec2d m_old_end_corner{ Vec2d::Zero() };
};
+87 -94
View File
@@ -4,6 +4,7 @@
#include "3DScene.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/format.hpp"
#include "libslic3r/Color.hpp"
#include <boost/nowide/fstream.hpp>
#include <GL/glew.h>
@@ -121,8 +122,7 @@ bool GLShaderProgram::init_from_texts(const std::string& name, const ShaderSourc
for (size_t i = 0; i < static_cast<size_t>(EShaderType::Count); ++i) {
const std::string& source = sources[i];
if (!source.empty())
{
if (!source.empty()) {
EShaderType type = static_cast<EShaderType>(i);
auto [result, id] = create_shader(type);
if (result)
@@ -206,154 +206,147 @@ void GLShaderProgram::stop_using() const
glsafe(::glUseProgram(0));
}
bool GLShaderProgram::set_uniform(const char* name, int value) const
void GLShaderProgram::set_uniform(int id, int value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform1i(id, static_cast<GLint>(value)));
return true;
}
return false;
if (id >= 0)
glsafe(::glUniform1i(id, value));
}
bool GLShaderProgram::set_uniform(const char* name, bool value) const
void GLShaderProgram::set_uniform(int id, bool value) const
{
return set_uniform(name, value ? 1 : 0);
set_uniform(id, value ? 1 : 0);
}
bool GLShaderProgram::set_uniform(const char* name, float value) const
void GLShaderProgram::set_uniform(int id, float value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
glsafe(::glUniform1f(id, static_cast<GLfloat>(value)));
return true;
}
return false;
if (id >= 0)
glsafe(::glUniform1f(id, value));
}
bool GLShaderProgram::set_uniform(const char* name, double value) const
void GLShaderProgram::set_uniform(int id, double value) const
{
return set_uniform(name, static_cast<float>(value));
set_uniform(id, static_cast<float>(value));
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 2>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<int, 2>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform2iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 3>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<int, 3>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform3iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 4>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<int, 4>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform4iv(id, 1, static_cast<const GLint*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<float, 2>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 3>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<float, 3>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 4>& value) const
void GLShaderProgram::set_uniform(int id, const std::array<float, 4>& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const
void GLShaderProgram::set_uniform(int id, const std::array<double, 4>& value) const
{
if (size == 1)
return set_uniform(name, value[0]);
else if (size < 5) {
int id = get_uniform_location(name);
if (id >= 0) {
if (size == 2)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
else if (size == 3)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
else
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
return true;
}
}
return false;
const std::array<float, 4> f_value = { float(value[0]), float(value[1]), float(value[2]), float(value[3]) };
set_uniform(id, f_value);
}
bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const
void GLShaderProgram::set_uniform(int id, const float* value, size_t size) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (size == 1)
set_uniform(id, value[0]);
else if (size == 2)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
else if (size == 3)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
else if (size == 4)
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
}
}
void GLShaderProgram::set_uniform(int id, const Transform3f& value) const
{
if (id >= 0)
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.matrix().data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const
void GLShaderProgram::set_uniform(int id, const Transform3d& value) const
{
return set_uniform(name, value.cast<float>());
set_uniform(id, value.cast<float>());
}
bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const
void GLShaderProgram::set_uniform(int id, const Matrix3f& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
if (id >= 0)
glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const
void GLShaderProgram::set_uniform(int id, const Matrix3d& value) const
{
int id = get_uniform_location(name);
if (id >= 0) {
set_uniform(id, (Matrix3f)value.cast<float>());
}
void GLShaderProgram::set_uniform(int id, const Matrix4f& value) const
{
if (id >= 0)
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
}
void GLShaderProgram::set_uniform(int id, const Matrix4d& value) const
{
set_uniform(id, (Matrix4f)value.cast<float>());
}
void GLShaderProgram::set_uniform(int id, const Vec2f& value) const
{
if (id >= 0)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
}
void GLShaderProgram::set_uniform(int id, const Vec2d& value) const
{
set_uniform(id, static_cast<Vec2f>(value.cast<float>()));
}
void GLShaderProgram::set_uniform(int id, const Vec3f& value) const
{
if (id >= 0)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
return true;
}
return false;
}
bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const
void GLShaderProgram::set_uniform(int id, const Vec3d& value) const
{
return set_uniform(name, static_cast<Vec3f>(value.cast<float>()));
set_uniform(id, static_cast<Vec3f>(value.cast<float>()));
}
void GLShaderProgram::set_uniform(int id, const ColorRGB& value) const
{
set_uniform(id, value.data(), 3);
}
void GLShaderProgram::set_uniform(int id, const ColorRGBA& value) const
{
set_uniform(id, value.data(), 4);
}
int GLShaderProgram::get_attrib_location(const char* name) const
+52 -16
View File
@@ -9,6 +9,9 @@
namespace Slic3r {
class ColorRGB;
class ColorRGBA;
class GLShaderProgram
{
public:
@@ -44,22 +47,55 @@ public:
void start_using() const;
void stop_using() const;
bool set_uniform(const char* name, int value) const;
bool set_uniform(const char* name, bool value) const;
bool set_uniform(const char* name, float value) const;
bool set_uniform(const char* name, double value) const;
bool set_uniform(const char* name, const std::array<int, 2>& value) const;
bool set_uniform(const char* name, const std::array<int, 3>& value) const;
bool set_uniform(const char* name, const std::array<int, 4>& value) const;
bool set_uniform(const char* name, const std::array<float, 2>& value) const;
bool set_uniform(const char* name, const std::array<float, 3>& value) const;
bool set_uniform(const char* name, const std::array<float, 4>& value) const;
bool set_uniform(const char* name, const float* value, size_t size) const;
bool set_uniform(const char* name, const Transform3f& value) const;
bool set_uniform(const char* name, const Transform3d& value) const;
bool set_uniform(const char* name, const Matrix3f& value) const;
bool set_uniform(const char* name, const Vec3f& value) const;
bool set_uniform(const char* name, const Vec3d& value) const;
void set_uniform(const char* name, int value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, bool value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, float value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, double value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<int, 2>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<int, 3>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<int, 4>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<float, 2>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<float, 3>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<float, 4>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const std::array<double, 4>& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const float* value, size_t size) const { set_uniform(get_uniform_location(name), value, size); }
void set_uniform(const char* name, const Transform3f& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Transform3d& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Matrix3f& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Matrix3d& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Matrix4f& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Matrix4d& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Vec2f& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Vec2d& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Vec3f& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const Vec3d& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const ColorRGB& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(const char* name, const ColorRGBA& value) const { set_uniform(get_uniform_location(name), value); }
void set_uniform(int id, int value) const;
void set_uniform(int id, bool value) const;
void set_uniform(int id, float value) const;
void set_uniform(int id, double value) const;
void set_uniform(int id, const std::array<int, 2>& value) const;
void set_uniform(int id, const std::array<int, 3>& value) const;
void set_uniform(int id, const std::array<int, 4>& value) const;
void set_uniform(int id, const std::array<float, 2>& value) const;
void set_uniform(int id, const std::array<float, 3>& value) const;
void set_uniform(int id, const std::array<float, 4>& value) const;
void set_uniform(int id, const std::array<double, 4>& value) const;
void set_uniform(int id, const float* value, size_t size) const;
void set_uniform(int id, const Transform3f& value) const;
void set_uniform(int id, const Transform3d& value) const;
void set_uniform(int id, const Matrix3f& value) const;
void set_uniform(int id, const Matrix3d& value) const;
void set_uniform(int id, const Matrix4f& value) const;
void set_uniform(int id, const Matrix4d& value) const;
void set_uniform(int id, const Vec2f& value) const;
void set_uniform(int id, const Vec2d& value) const;
void set_uniform(int id, const Vec3f& value) const;
void set_uniform(int id, const Vec3d& value) const;
void set_uniform(int id, const ColorRGB& value) const;
void set_uniform(int id, const ColorRGBA& value) const;
// returns -1 if not found
int get_attrib_location(const char* name) const;
+24 -37
View File
@@ -33,61 +33,48 @@ std::pair<bool, std::string> GLShadersManager::init()
bool valid = true;
const std::string prefix = GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 1) ? "140/" : "110/";
// imgui shader
valid &= append_shader("imgui", { prefix + "imgui.vs", prefix + "imgui.fs" });
// basic shader, used to render all what was previously rendered using the immediate mode
valid &= append_shader("flat", { prefix + "flat.vs", prefix + "flat.fs" });
// basic shader with plane clipping, used to render volumes in picking pass
valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" });
// basic shader for textures, used to render textures
valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" });
// used to render 3D scene background
valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" });
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" });
//used to render thumbnail
valid &= append_shader("thumbnail", { "thumbnail.vs", "thumbnail.fs" });
// used to render first layer for calibration
valid &= append_shader("cali", { "cali.vs", "cali.fs"});
valid &= append_shader("thumbnail", { prefix + "thumbnail.vs", prefix + "thumbnail.fs"});
// used to render printbed
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
valid &= append_shader("printbed", { prefix + "printbed.vs", prefix + "printbed.fs" });
// used to render options in gcode preview
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
// used to render extrusion and travel paths as lines in gcode preview
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
valid &= append_shader("gouraud_light_instanced", { prefix + "gouraud_light_instanced.vs", prefix + "gouraud_light_instanced.fs" });
}
// used to render objects in 3d editor
//if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0)) {
if (0) {
valid &= append_shader("gouraud", { "gouraud_130.vs", "gouraud_130.fs" }
#if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv }
#endif // ENABLE_ENVIRONMENT_MAP
);
}
else {
valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
valid &= append_shader("gouraud", { prefix + "gouraud.vs", prefix + "gouraud.fs" }
#if ENABLE_ENVIRONMENT_MAP
, { "ENABLE_ENVIRONMENT_MAP"sv }
#endif // ENABLE_ENVIRONMENT_MAP
);
}
// used to render variable layers heights in 3d editor
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
valid &= append_shader("variable_layer_height", { prefix + "variable_layer_height.vs", prefix + "variable_layer_height.fs" });
// used to render highlight contour around selected triangles inside the multi-material gizmo
valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" });
valid &= append_shader("mm_contour", { prefix + "mm_contour.vs", prefix + "mm_contour.fs" });
// Used to render painted triangles inside the multi-material gizmo. Triangle normals are computed inside fragment shader.
// For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
// Because of this, objects had darker colors inside the multi-material gizmo.
// Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
// Since macOS 12 (Monterey), this issue with the opposite direction on Apple's Arm CPU seems to be fixed, and computed
// triangle normals inside fragment shader have the right direction.
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12) {
//if (GUI::wxGetApp().plater() && GUI::wxGetApp().plater()->is_wireframe_enabled())
// valid &= append_shader("mm_gouraud", {"mm_gouraud_wireframe.vs", "mm_gouraud_wireframe.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
//else
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
}
else {
//if (GUI::wxGetApp().plater() && GUI::wxGetApp().plater()->is_wireframe_enabled())
// valid &= append_shader("mm_gouraud", {"mm_gouraud_wireframe.vs", "mm_gouraud_wireframe.fs"});
//else
valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
}
//BBS: add shader for outline
valid &= append_shader("outline", { "outline.vs", "outline.fs" });
if (platform_flavor() == PlatformFlavor::OSXOnArm && wxPlatformInfo::Get().GetOSMajorVersion() < 12)
valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv });
else
valid &= append_shader("mm_gouraud", { prefix + "mm_gouraud.vs", prefix + "mm_gouraud.fs" });
return { valid, error };
}
+81 -28
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
//BBS:add i18n
#include "I18N.hpp"
//BBS: add fstream for debug output
@@ -8,6 +12,8 @@
#include "3DScene.hpp"
#include "OpenGLManager.hpp"
#include "GUI_App.hpp"
#include "GLModel.hpp"
#include <GL/glew.h>
@@ -125,11 +131,7 @@ void GLTexture::Compressor::compress()
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
GLTexture::GLTexture()
: m_id(0)
, m_width(0)
, m_height(0)
, m_source("")
, m_compressor(*this)
: m_compressor(*this)
{
}
@@ -418,13 +420,13 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
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)
if (compress && OpenGLManager::are_compressed_textures_supported())
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
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
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_NEAREST));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
@@ -536,7 +538,7 @@ bool GLTexture::generate_from_text(const std::string &text_str, wxFont &font, wx
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
if (GLEW_EXT_texture_compression_s3tc)
if (OpenGLManager::are_compressed_textures_supported())
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
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
@@ -637,7 +639,7 @@ bool GLTexture::generate_texture_from_text(const std::string& text_str, wxFont&
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
if (GLEW_EXT_texture_compression_s3tc)
if (OpenGLManager::are_compressed_textures_supported())
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
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
@@ -664,12 +666,32 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id));
::glBegin(GL_QUADS);
::glTexCoord2f(uvs.left_bottom.u, uvs.left_bottom.v); ::glVertex2f(left, bottom);
::glTexCoord2f(uvs.right_bottom.u, uvs.right_bottom.v); ::glVertex2f(right, bottom);
::glTexCoord2f(uvs.right_top.u, uvs.right_top.v); ::glVertex2f(right, top);
::glTexCoord2f(uvs.left_top.u, uvs.left_top.v); ::glVertex2f(left, top);
glsafe(::glEnd());
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2 };
init_data.reserve_vertices(4);
init_data.reserve_indices(6);
// vertices
init_data.add_vertex(Vec2f(left, bottom), Vec2f(uvs.left_bottom.u, uvs.left_bottom.v));
init_data.add_vertex(Vec2f(right, bottom), Vec2f(uvs.right_bottom.u, uvs.right_bottom.v));
init_data.add_vertex(Vec2f(right, top), Vec2f(uvs.right_top.u, uvs.right_top.v));
init_data.add_vertex(Vec2f(left, top), Vec2f(uvs.left_top.u, uvs.left_top.v));
// indices
init_data.add_triangle(0, 1, 2);
init_data.add_triangle(2, 3, 0);
GLModel model;
model.init_from(std::move(init_data));
GLShaderProgram* shader = wxGetApp().get_shader("flat_texture");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("view_model_matrix", Transform3d::Identity());
shader->set_uniform("projection_matrix", Transform3d::Identity());
model.render();
shader->stop_using();
}
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
@@ -677,9 +699,29 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glDisable(GL_BLEND));
}
static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h)
{
auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; };
auto upper_power_of_two = [](int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; };
int new_w = std::max(w, h);
if (!is_power_of_two(new_w))
new_w = upper_power_of_two(new_w);
while (new_w > max_size_px) {
new_w /= 2;
}
const int new_h = new_w;
const bool ret = (new_w != w || new_h != h);
w = new_w;
h = new_h;
return ret;
}
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
const bool compression_enabled = (compression_type != None) && OpenGLManager::are_compressed_textures_supported();
// Load a PNG with an alpha channel.
wxImage image;
@@ -693,6 +735,11 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
bool requires_rescale = false;
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures()) {
if (to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), OpenGLManager::get_gl_info().get_max_tex_size(), m_width, m_height))
requires_rescale = true;
}
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;
@@ -819,7 +866,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
m_source = filename;
if (compression_enabled && compression_type == MultiThreaded)
if (compression_type == MultiThreaded)
// start asynchronous compression
m_compressor.start_compressing();
@@ -828,7 +875,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
const bool compression_enabled = compress && OpenGLManager::are_compressed_textures_supported();
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr) {
@@ -836,11 +883,17 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
return false;
}
float scale = (float)max_size_px / std::max(image->width, image->height);
const float scale = (float)max_size_px / std::max(image->width, image->height);
m_width = (int)(scale * image->width);
m_height = (int)(scale * image->height);
if (use_mipmaps && compression_enabled && OpenGLManager::force_power_of_two_textures())
to_squared_power_of_two(boost::filesystem::path(filename).filename().string(), max_size_px, m_width, m_height);
float scale_w = (float)m_width / image->width;
float scale_h = (float)m_height / image->height;
if (compression_enabled) {
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
@@ -853,7 +906,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
m_height += (4 - height_rem);
}
int n_pixels = m_width * m_height;
const int n_pixels = m_width * m_height;
if (n_pixels <= 0) {
reset();
@@ -870,7 +923,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
std::vector<unsigned char> data(n_pixels * 4, 0);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4);
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), m_width, m_height, m_width * 4);
// sends data to gpu
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
@@ -892,7 +945,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
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 && OpenGLManager::use_manually_generated_mipmaps()) {
if (use_mipmaps) {
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
int lod_w = m_width;
int lod_h = m_height;
@@ -902,11 +955,12 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
lod_w = std::max(lod_w / 2, 1);
lod_h = std::max(lod_h / 2, 1);
scale /= 2.0f;
scale_w /= 2.0f;
scale_h /= 2.0f;
data.resize(lod_w * lod_h * 4);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
nsvgRasterizeXY(rast, image, 0, 0, scale_w, scale_h, data.data(), lod_w, lod_h, lod_w * 4);
if (compression_enabled) {
// 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));
@@ -921,9 +975,8 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
} else if (use_mipmaps && !OpenGLManager::use_manually_generated_mipmaps()) {
glGenerateMipmap(GL_TEXTURE_2D);
} else {
}
else {
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
}
+5 -5
View File
@@ -64,8 +64,8 @@ namespace GUI {
struct UV
{
float u;
float v;
float u{ 0.0f };
float v{ 0.0f };
};
struct Quad_UVs
@@ -79,9 +79,9 @@ namespace GUI {
static Quad_UVs FullTextureUVs;
protected:
unsigned int m_id;
int m_width;
int m_height;
unsigned int m_id{ 0 };
int m_width{ 0 };
int m_height{ 0 };
std::string m_source;
Compressor m_compressor;
+249 -264
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/Point.hpp"
#include "libslic3r/libslic3r.h"
@@ -281,21 +285,13 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
return res;
}
bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
bool GLToolbar::init_arrow(const std::string& filename)
{
if (m_arrow_texture.texture.get_id() != 0)
if (m_arrow_texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/images/";
bool res = false;
if (!arrow_texture.filename.empty()) {
res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
}
if (res)
m_arrow_texture.metadata = arrow_texture;
return res;
const std::string path = resources_dir() + "/images/";
return (!filename.empty()) ? m_arrow_texture.load_from_svg_file(path + filename, false, false, false, 1000) : false;
}
GLToolbar::Layout::EType GLToolbar::get_layout_type() const
@@ -551,18 +547,16 @@ void GLToolbar::render(const GLCanvas3D& parent,GLToolbarItem::EType type)
{
default:
case Layout::Horizontal: { render_horizontal(parent,type); break; }
case Layout::Vertical: { render_vertical(parent); break; }
case Layout::Vertical: { render_vertical(parent); break; }
}
}
bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
{
if (!m_enabled)
return false;
Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
const Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
bool processed = false;
// mouse anywhere
@@ -610,7 +604,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
return false;
}
int item_id = contains_mouse(mouse_pos, parent);
const int item_id = contains_mouse(mouse_pos, parent);
if (item_id != -1) {
// mouse inside toolbar
if (evt.LeftDown() || evt.LeftDClick()) {
@@ -757,16 +751,12 @@ int GLToolbar::get_visible_items_cnt() const
void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover)
{
if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id))
{
if ((0 <= item_id) && (item_id < (int)m_items.size()))
{
if (m_pressed_toggable_id == -1 || m_pressed_toggable_id == item_id) {
if (0 <= item_id && item_id < (int)m_items.size()) {
GLToolbarItem* item = m_items[item_id];
if ((item != nullptr) && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered()))
{
if (((type == GLToolbarItem::Right) && item->is_right_toggable()) ||
((type == GLToolbarItem::Left) && item->is_left_toggable()))
{
if (item != nullptr && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered())) {
if ((type == GLToolbarItem::Right && item->is_right_toggable()) ||
(type == GLToolbarItem::Left && item->is_left_toggable())) {
GLToolbarItem::EState state = item->get_state();
if (state == GLToolbarItem::Hover)
item->set_state(GLToolbarItem::HoverPressed);
@@ -784,12 +774,11 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas
switch (type)
{
default:
case GLToolbarItem::Left: { item->do_left_action(); break; }
case GLToolbarItem::Left: { item->do_left_action(); break; }
case GLToolbarItem::Right: { item->do_right_action(); break; }
}
}
else
{
else {
if (m_type == Radio)
select_item(item->get_name());
else
@@ -804,8 +793,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas
case GLToolbarItem::Right: { item->do_right_action(); break; }
}
if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
{
if (m_type == Normal && item->get_state() != GLToolbarItem::Disabled) {
// the item may get disabled during the action, if not, set it back to normal state
item->set_state(GLToolbarItem::Normal);
parent.render();
@@ -825,55 +813,51 @@ void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
default:
case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; }
case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; }
case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; }
}
}
void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
// NB: mouse_pos is already scaled appropriately
const Size cnv_size = parent.get_canvas_size();
const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y()));
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
const float icons_size = m_layout.icons_size * m_layout.scale;
const float separator_size = m_layout.separator_size * m_layout.scale;
const float gap_size = m_layout.gap_size * m_layout.scale;
const float border = m_layout.border * m_layout.scale;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
const float separator_stride = separator_size + gap_size;
const float icon_stride = icons_size + gap_size;
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float left = m_layout.left + border;
float top = m_layout.top - border;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left + scaled_border;
float top = m_layout.top - scaled_border;
for (GLToolbarItem* item : m_items)
{
for (GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
if (item->is_separator())
left += separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
else {
float right = left + icons_size;
const float bottom = top - icons_size;
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
right += scaled_icons_size * item->get_extra_size_ratio();
GLToolbarItem::EState state = item->get_state();
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
right += icons_size * item->get_extra_size_ratio();
const GLToolbarItem::EState state = item->get_state();
bool inside = (left <= (float)scaled_mouse_pos.x()) &&
((float)scaled_mouse_pos.x() <= right) &&
(bottom <= (float)scaled_mouse_pos.y()) &&
((float)scaled_mouse_pos.y() <= top);
switch (state)
{
case GLToolbarItem::Normal:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::Hover);
parent.set_as_dirty();
}
@@ -882,8 +866,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
}
case GLToolbarItem::Hover:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Normal);
parent.set_as_dirty();
}
@@ -892,8 +875,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
}
case GLToolbarItem::Pressed:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::HoverPressed);
parent.set_as_dirty();
}
@@ -902,8 +884,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
}
case GLToolbarItem::HoverPressed:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Pressed);
parent.set_as_dirty();
}
@@ -912,8 +893,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
}
case GLToolbarItem::Disabled:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::HoverDisabled);
parent.set_as_dirty();
}
@@ -922,8 +902,7 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
}
case GLToolbarItem::HoverDisabled:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Disabled);
parent.set_as_dirty();
}
@@ -939,59 +918,55 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D
left += icon_stride;
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
left += scaled_icons_size * item->get_extra_size_ratio();
left += icons_size * item->get_extra_size_ratio();
}
}
}
void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
// NB: mouse_pos is already scaled appropriately
const Size cnv_size = parent.get_canvas_size();
const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y()));
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
const float icons_size = m_layout.icons_size * m_layout.scale;
const float separator_size = m_layout.separator_size * m_layout.scale;
const float gap_size = m_layout.gap_size * m_layout.scale;
const float border = m_layout.border * m_layout.scale;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
const float separator_stride = separator_size + gap_size;
const float icon_stride = icons_size + gap_size;
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left + border;
float top = m_layout.top - border;
float left = m_layout.left + scaled_border;
float top = m_layout.top - scaled_border;
for (GLToolbarItem* item : m_items)
{
for (GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
if (item->is_separator())
top -= separator_stride;
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
else {
float right = left + icons_size;
const float bottom = top - icons_size;
if (item->is_action_with_text_image())
right += m_layout.text_size * factor;
right += m_layout.text_size * m_layout.scale;
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
right += scaled_icons_size * item->get_extra_size_ratio();
right += icons_size * item->get_extra_size_ratio();
GLToolbarItem::EState state = item->get_state();
bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top);
const bool inside = (left <= (float)scaled_mouse_pos.x()) &&
((float)scaled_mouse_pos.x() <= right) &&
(bottom <= (float)scaled_mouse_pos.y()) &&
((float)scaled_mouse_pos.y() <= top);
switch (state)
{
case GLToolbarItem::Normal:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::Hover);
parent.set_as_dirty();
}
@@ -1000,8 +975,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
case GLToolbarItem::Hover:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Normal);
parent.set_as_dirty();
}
@@ -1010,8 +984,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
case GLToolbarItem::Pressed:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::HoverPressed);
parent.set_as_dirty();
}
@@ -1020,8 +993,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
case GLToolbarItem::HoverPressed:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Pressed);
parent.set_as_dirty();
}
@@ -1030,8 +1002,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
case GLToolbarItem::Disabled:
{
if (inside)
{
if (inside) {
item->set_state(GLToolbarItem::HoverDisabled);
parent.set_as_dirty();
}
@@ -1040,8 +1011,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
case GLToolbarItem::HoverDisabled:
{
if (!inside)
{
if (!inside) {
item->set_state(GLToolbarItem::Disabled);
parent.set_as_dirty();
}
@@ -1083,77 +1053,78 @@ int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent)
{
default:
case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); }
case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); }
case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); }
}
}
int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
// NB: mouse_pos is already scaled appropriately
const Size cnv_size = parent.get_canvas_size();
const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y()));
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
const float icons_size = m_layout.icons_size * m_layout.scale;
const float separator_size = m_layout.separator_size * m_layout.scale;
const float gap_size = m_layout.gap_size * m_layout.scale;
const float border = m_layout.border * m_layout.scale;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float left = m_layout.left + border;
const float top = m_layout.top - border;
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float left = m_layout.left + scaled_border;
float top = m_layout.top - scaled_border;
for (size_t id=0; id<m_items.size(); ++id)
{
for (size_t id = 0; id < m_items.size(); ++id) {
GLToolbarItem* item = m_items[id];
if (!item->is_visible())
continue;
if (item->is_separator())
{
float right = left + scaled_separator_size;
float bottom = top - scaled_icons_size;
if (item->is_separator()) {
float right = left + separator_size;
const float bottom = top - icons_size;
// mouse inside the separator
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return id;
left = right;
right += scaled_gap_size;
right += gap_size;
if (id < m_items.size() - 1)
{
if (id < m_items.size() - 1) {
// mouse inside the gap
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return -2;
}
left = right;
}
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
else {
float right = left + icons_size;
const float bottom = top - icons_size;
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
right += scaled_icons_size * item->get_extra_size_ratio();
right += icons_size * item->get_extra_size_ratio();
// mouse inside the icon
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return id;
left = right;
right += scaled_gap_size;
right += gap_size;
if (id < m_items.size() - 1)
{
if (id < m_items.size() - 1) {
// mouse inside the gap
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return -2;
}
@@ -1166,73 +1137,75 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
// NB: mouse_pos is already scaled appropriately
const Size cnv_size = parent.get_canvas_size();
const Vec2d scaled_mouse_pos((mouse_pos.x() - 0.5 * (double)cnv_size.get_width()), (0.5 * (double)cnv_size.get_height() - mouse_pos.y()));
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
const float icons_size = m_layout.icons_size * m_layout.scale;
const float separator_size = m_layout.separator_size * m_layout.scale;
const float gap_size = m_layout.gap_size * m_layout.scale;
const float border = m_layout.border * m_layout.scale;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
const float left = m_layout.left + border;
float top = m_layout.top - border;
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float left = m_layout.left + scaled_border;
float top = m_layout.top - scaled_border;
for (size_t id=0; id<m_items.size(); ++id)
{
for (size_t id = 0; id < m_items.size(); ++id) {
GLToolbarItem* item = m_items[id];
if (!item->is_visible())
continue;
if (item->is_separator())
{
float right = left + scaled_icons_size;
float bottom = top - scaled_separator_size;
if (item->is_separator()) {
const float right = left + icons_size;
float bottom = top - separator_size;
// mouse inside the separator
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return id;
top = bottom;
bottom -= scaled_gap_size;
bottom -= gap_size;
if (id < m_items.size() - 1)
{
if (id < m_items.size() - 1) {
// mouse inside the gap
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return -2;
}
top = bottom;
}
else
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
else {
float right = left + icons_size;
float bottom = top - icons_size;
if (item->is_action_with_text_image())
right += m_layout.text_size * factor;
right += m_layout.text_size * m_layout.scale;
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
right += scaled_icons_size * item->get_extra_size_ratio();
right += icons_size * item->get_extra_size_ratio();
// mouse inside the icon
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return id;
top = bottom;
bottom -= scaled_gap_size;
bottom -= gap_size;
if (id < m_items.size() - 1)
{
if (id < m_items.size() - 1) {
// mouse inside the gap
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
if (left <= (float)scaled_mouse_pos.x() &&
(float)scaled_mouse_pos.x() <= right &&
bottom <= (float)scaled_mouse_pos.y() &&
(float)scaled_mouse_pos.y() <= top)
return -2;
}
@@ -1243,33 +1216,32 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
return -1;
}
void GLToolbar::render_background(float left, float top, float right, float bottom, float border) const
void GLToolbar::render_background(float left, float top, float right, float bottom, float border_w, float border_h) const
{
unsigned int tex_id = m_background_texture.texture.get_id();
float tex_width = (float)m_background_texture.texture.get_width();
float tex_height = (float)m_background_texture.texture.get_height();
if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0))
{
float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
const unsigned int tex_id = m_background_texture.texture.get_id();
const float tex_width = (float)m_background_texture.texture.get_width();
const float tex_height = (float)m_background_texture.texture.get_height();
if (tex_id != 0 && tex_width > 0.0f && tex_height > 0.0f) {
const float inv_tex_width = 1.0f / tex_width;
const float inv_tex_height = 1.0f / tex_height;
float internal_left = left + border;
float internal_right = right - border;
float internal_top = top - border;
float internal_bottom = bottom + border;
const float internal_left = left + border_w;
const float internal_right = right - border_w;
const float internal_top = top - border_h;
const float internal_bottom = bottom + border_w;
float left_uv = 0.0f;
float right_uv = 1.0f;
float top_uv = 1.0f;
float bottom_uv = 0.0f;
const float left_uv = 0.0f;
const float right_uv = 1.0f;
const float top_uv = 1.0f;
const float bottom_uv = 0.0f;
float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height;
const float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width;
const float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width;
const float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height;
const float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height;
// top-left corner
if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Top))
if (m_layout.horizontal_orientation == Layout::HO_Left || m_layout.vertical_orientation == Layout::VO_Top)
GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
else
GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } });
@@ -1281,7 +1253,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } });
// top-right corner
if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Top))
if (m_layout.horizontal_orientation == Layout::HO_Right || m_layout.vertical_orientation == Layout::VO_Top)
GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
else
GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } });
@@ -1302,7 +1274,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott
GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } });
// bottom-left corner
if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Bottom))
if (m_layout.horizontal_orientation == Layout::HO_Left || m_layout.vertical_orientation == Layout::VO_Bottom)
GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
else
GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } });
@@ -1314,7 +1286,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } });
// bottom-right corner
if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Bottom))
if (m_layout.horizontal_orientation == Layout::HO_Right || m_layout.vertical_orientation == Layout::VO_Bottom)
GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
else
GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } });
@@ -1324,7 +1296,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott
void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
{
// arrow texture not initialized
if (m_arrow_texture.texture.get_id() == 0)
if (m_arrow_texture.get_id() == 0)
return;
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
@@ -1363,67 +1335,71 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
top -= separator_stride;
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
const unsigned int tex_id = m_arrow_texture.get_id();
// arrow width and height
float arr_tex_width = (float)m_arrow_texture.texture.get_width();
float arr_tex_height = (float)m_arrow_texture.texture.get_height();
if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) {
float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f;
float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f;
const float arr_tex_width = (float)m_arrow_texture.get_width();
const float arr_tex_height = (float)m_arrow_texture.get_height();
if (tex_id != 0 && arr_tex_width > 0.0f && arr_tex_height > 0.0f) {
float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow
float internal_right = right - border + scaled_icons_size * 1.5f;
float internal_top = top - border;
// bottom is not moving and should be calculated from arrow texture sides ratio
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
float arrow_sides_ratio = (float)m_arrow_texture.get_height() / (float)m_arrow_texture.get_width();
float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
const float left_uv = 0.0f;
const float right_uv = 1.0f;
const float top_uv = 1.0f;
const float bottom_uv = 0.0f;
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } });
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { left_uv, top_uv }, { right_uv, top_uv }, { right_uv, bottom_uv }, { left_uv, bottom_uv } });
}
}
void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type)
{
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
const Size cnv_size = parent.get_canvas_size();
const float cnv_w = (float)cnv_size.get_width();
const float cnv_h = (float)cnv_size.get_height();
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float scaled_width = get_width() * inv_zoom;
float scaled_height = get_height() * inv_zoom;
if (cnv_w == 0 || cnv_h == 0)
return;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
const float inv_cnv_w = 1.0f / cnv_w;
const float inv_cnv_h = 1.0f / cnv_h;
float left = m_layout.left;
float top = m_layout.top;
float right = left + scaled_width;
const float icons_size_x = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_w;
const float icons_size_y = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_h;
const float separator_size = 2.0f * m_layout.separator_size * m_layout.scale * inv_cnv_w;
const float gap_size = 2.0f * m_layout.gap_size * m_layout.scale * inv_cnv_w;
const float border_w = 2.0f * m_layout.border * m_layout.scale * inv_cnv_w;
const float border_h = 2.0f * m_layout.border * m_layout.scale * inv_cnv_h;
const float width = 2.0f * get_width() * inv_cnv_w;
const float height = 2.0f * get_height() * inv_cnv_h;
const float separator_stride = separator_size + gap_size;
const float icon_stride = icons_size_x + gap_size;
float left = 2.0f * m_layout.left * inv_cnv_w;
float top = 2.0f * m_layout.top * inv_cnv_h;
float right = left + width;
if (type == GLToolbarItem::SeparatorLine)
right = left + scaled_width * 0.5;
float bottom = top - scaled_height;
right = left + width * 0.5;
const float bottom = top - height;
render_background(left, top, right, bottom, scaled_border);
render_background(left, top, right, bottom, border_w, border_h);
left += scaled_border;
top -= scaled_border;
left += border_w;
top -= border_h;
// renders icons
for (const GLToolbarItem* item : m_items)
{
for (const GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
if (item->is_separator())
left += separator_stride;
else
{
else {
//BBS GUI refactor
item->render_left_pos = left;
if (!item->is_action_with_text_image()) {
@@ -1432,13 +1408,13 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
int tex_height = m_icons_texture.get_height();
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
item->render(tex_id, left, left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
}
//BBS: GUI refactor: GLToolbar
if (item->is_action_with_text())
{
float scaled_text_size = item->get_extra_size_ratio() * scaled_icons_size;
item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_size, top - scaled_icons_size, top);
float scaled_text_size = item->get_extra_size_ratio() * icons_size_x;
item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_size, top - icons_size_y, top);
left += scaled_text_size;
}
left += icon_stride;
@@ -1448,28 +1424,37 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType
void GLToolbar::render_vertical(const GLCanvas3D& parent)
{
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
const Size cnv_size = parent.get_canvas_size();
const float cnv_w = (float)cnv_size.get_width();
const float cnv_h = (float)cnv_size.get_height();
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float scaled_width = get_width() * inv_zoom;
float scaled_height = get_height() * inv_zoom;
if (cnv_w == 0 || cnv_h == 0)
return;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
const float inv_cnv_w = 1.0f / cnv_w;
const float inv_cnv_h = 1.0f / cnv_h;
float left = m_layout.left;
float top = m_layout.top;
float right = left + scaled_width;
float bottom = top - scaled_height;
const float icons_size_x = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_w;
const float icons_size_y = 2.0f * m_layout.icons_size * m_layout.scale * inv_cnv_h;
const float separator_size = 2.0f * m_layout.separator_size * m_layout.scale * inv_cnv_h;
const float gap_size = 2.0f * m_layout.gap_size * m_layout.scale * inv_cnv_h;
const float border_w = 2.0f * m_layout.border * m_layout.scale * inv_cnv_w;
const float border_h = 2.0f * m_layout.border * m_layout.scale * inv_cnv_h;
const float width = 2.0f * get_width() * inv_cnv_w;
const float height = 2.0f * get_height() * inv_cnv_h;
render_background(left, top, right, bottom, scaled_border);
const float separator_stride = separator_size + gap_size;
const float icon_stride = icons_size_y + gap_size;
left += scaled_border;
top -= scaled_border;
float left = 2.0f * m_layout.left * inv_cnv_w;
float top = 2.0f * m_layout.top * inv_cnv_h;
const float right = left + width;
const float bottom = top - height;
render_background(left, top, right, bottom, border_w, border_h);
left += border_w;
top -= border_h;
// renders icons
for (const GLToolbarItem* item : m_items) {
@@ -1482,10 +1467,10 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent)
unsigned int tex_id;
int tex_width, tex_height;
if (item->is_action_with_text_image()) {
float scaled_text_size = m_layout.text_size * factor;
float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size;
float scaled_text_border = 2.5 * factor;
float scaled_text_height = scaled_icons_size / 2.0f;
float scaled_text_size = m_layout.text_size * m_layout.scale * inv_cnv_w;
float scaled_text_width = item->get_extra_size_ratio() * icons_size_x;
float scaled_text_border = 2.5 * m_layout.scale * inv_cnv_h;
float scaled_text_height = icons_size_y / 2.0f;
item->render_text(left, left + scaled_text_size, top - scaled_text_border - scaled_text_height, top - scaled_text_border);
float image_left = left + scaled_text_size;
@@ -1494,7 +1479,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent)
tex_height = item->m_data.image_texture.get_height();
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
item->render_image(tex_id, image_left, image_left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
item->render_image(tex_id, image_left, image_left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
}
else {
tex_id = m_icons_texture.get_id();
@@ -1502,14 +1487,14 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent)
tex_height = m_icons_texture.get_height();
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
item->render(tex_id, left, left + icons_size_x, top - icons_size_y, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
//BBS: GUI refactor: GLToolbar
}
if (item->is_action_with_text())
{
float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size;
float scaled_text_height = scaled_icons_size;
item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_width, top - scaled_text_height, top);
float scaled_text_width = item->get_extra_size_ratio() * icons_size_x;
float scaled_text_height = icons_size_y;
item->render_text(left + icons_size_x, left + icons_size_x + scaled_text_width, top - scaled_text_height, top);
}
top -= icon_stride;
}
+8 -4
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Oleksandra Iushchenko @YuSanka, Vojtěch Král @vojtechkral, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLToolbar_hpp_
#define slic3r_GLToolbar_hpp_
@@ -327,7 +331,7 @@ private:
mutable GLTexture m_images_texture;
mutable bool m_images_texture_dirty;
BackgroundTexture m_background_texture;
BackgroundTexture m_arrow_texture;
GLTexture m_arrow_texture;
Layout m_layout;
ItemsList m_items;
@@ -354,7 +358,7 @@ public:
bool init(const BackgroundTexture::Metadata& background_texture);
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
bool init_arrow(const std::string& filename);
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
@@ -436,8 +440,8 @@ private:
int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
void render_background(float left, float top, float right, float bottom, float border) const;
void render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type);
void render_background(float left, float top, float right, float bottom, float border_w, float border_h) const;
void render_horizontal(const GLCanvas3D &parent, GLToolbarItem::EType type);
void render_vertical(const GLCanvas3D& parent);
bool generate_icons_texture();
+9 -4
View File
@@ -56,6 +56,7 @@
#include "libslic3r/Thread.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp"
#include "GUI.hpp"
#include "GUI_Utils.hpp"
@@ -3233,8 +3234,7 @@ void GUI_App::set_label_clr_modified(const wxColour& clr)
if (m_color_label_modified == clr)
return;
m_color_label_modified = clr;
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
std::string str = clr_str.ToStdString();
const std::string str = encode_color(ColorRGB(clr.Red(), clr.Green(), clr.Blue()));
app_config->save();
*/
}
@@ -3247,8 +3247,7 @@ void GUI_App::set_label_clr_sys(const wxColour& clr)
if (m_color_label_sys == clr)
return;
m_color_label_sys = clr;
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
std::string str = clr_str.ToStdString();
const std::string str = encode_color(ColorRGB(clr.Red(), clr.Green(), clr.Blue()));
app_config->save();
*/
}
@@ -5865,6 +5864,12 @@ Sidebar& GUI_App::sidebar()
return plater_->sidebar();
}
GizmoObjectManipulation *GUI_App::obj_manipul()
{
// If this method is called before plater_ has been initialized, return nullptr (to avoid a crash)
return (plater_ != nullptr) ? &plater_->get_view3D_canvas3D()->get_gizmos_manager().get_object_manipulation() : nullptr;
}
ObjectSettings* GUI_App::obj_settings()
{
return sidebar().obj_settings();
+2
View File
@@ -129,6 +129,7 @@ enum CameraMenuIDs {
class Tab;
class ConfigWizard;
class GizmoObjectManipulation;
static wxString dots("...", wxConvUTF8);
@@ -529,6 +530,7 @@ private:
#endif /* __APPLE */
Sidebar& sidebar();
GizmoObjectManipulation* obj_manipul();
ObjectSettings* obj_settings();
ObjectList* obj_list();
ObjectLayers* obj_layers();
+1 -7
View File
@@ -2,6 +2,7 @@
#define slic3r_GUI_Colors_hpp_
#include "imgui/imgui.h"
#include "libslic3r/Color.hpp"
enum RenderCol_ {
RenderCol_3D_Background = 0,
@@ -38,13 +39,6 @@ public:
static ImVec4 colors[RenderCol_Count];
};
const char* GetRenderColName(RenderCol idx);
inline std::array<float, 4> GLColor(ImVec4 color) {
return {color.x, color.y, color.z, color.w };
}
inline ImVec4 IMColor(std::array<float, 4> color) {
return ImVec4(color[0], color[1], color[2], color[3]);
}
}
+13
View File
@@ -0,0 +1,13 @@
///|/ Copyright (c) Prusa Research 2021 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "GUI_Geometry.hpp"
namespace Slic3r {
namespace GUI {
} // namespace Slic3r
} // namespace GUI
+82
View File
@@ -0,0 +1,82 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_Geometry_hpp_
#define slic3r_GUI_Geometry_hpp_
namespace Slic3r {
namespace GUI {
enum class ECoordinatesType : unsigned char
{
World,
Instance,
Local
};
class TransformationType
{
public:
enum Enum {
// Transforming in a world coordinate system
World = 0,
// Transforming in a instance coordinate system
Instance = 1,
// Transforming in a local coordinate system
Local = 2,
// Absolute transformations, allowed in local coordinate system only.
Absolute = 0,
// Relative transformations, allowed in both local and world coordinate system.
Relative = 4,
// For group selection, the transformation is performed as if the group made a single solid body.
Joint = 0,
// For group selection, the transformation is performed on each object independently.
Independent = 8,
World_Relative_Joint = World | Relative | Joint,
World_Relative_Independent = World | Relative | Independent,
Instance_Absolute_Joint = Instance | Absolute | Joint,
Instance_Absolute_Independent = Instance | Absolute | Independent,
Instance_Relative_Joint = Instance | Relative | Joint,
Instance_Relative_Independent = Instance | Relative | Independent,
Local_Absolute_Joint = Local | Absolute | Joint,
Local_Absolute_Independent = Local | Absolute | Independent,
Local_Relative_Joint = Local | Relative | Joint,
Local_Relative_Independent = Local | Relative | Independent,
};
TransformationType() : m_value(World) {}
TransformationType(Enum value) : m_value(value) {}
TransformationType& operator=(Enum value) { m_value = value; return *this; }
Enum operator()() const { return m_value; }
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
void set_world() { this->remove(Instance); this->remove(Local); }
void set_instance() { this->remove(Local); this->add(Instance); }
void set_local() { this->remove(Instance); this->add(Local); }
void set_absolute() { this->remove(Relative); }
void set_relative() { this->add(Relative); }
void set_joint() { this->remove(Independent); }
void set_independent() { this->add(Independent); }
bool world() const { return !this->has(Instance) && !this->has(Local); }
bool instance() const { return this->has(Instance); }
bool local() const { return this->has(Local); }
bool absolute() const { return !this->has(Relative); }
bool relative() const { return this->has(Relative); }
bool joint() const { return !this->has(Independent); }
bool independent() const { return this->has(Independent); }
private:
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
Enum m_value;
};
} // namespace Slic3r
} // namespace GUI
#endif // slic3r_GUI_Geometry_hpp_
+11 -11
View File
@@ -22,8 +22,10 @@ namespace GUI
ObjectLayers::ObjectLayers(wxWindow* parent) :
OG_Settings(parent, true)
{
m_grid_sizer = new wxFlexGridSizer(3, 0, wxGetApp().em_unit()); // "Min Z", "Max Z", "Layer height" & buttons sizer
m_grid_sizer = new wxFlexGridSizer(5, 0, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, unit & buttons sizer
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
m_grid_sizer->AddGrowableCol(1);
m_grid_sizer->AddGrowableCol(3);
m_og->activate();
m_og->sizer->Clear(true);
@@ -75,7 +77,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
auto head_text = new wxStaticText(m_parent, wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
head_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
head_text->SetFont(wxGetApp().normal_font());
m_grid_sizer->Add(head_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit());
m_grid_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL);
// Add control for the "Min Z"
@@ -101,14 +103,12 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
select_editor(editor, is_last_edited_range);
auto sizer1 = new wxBoxSizer(wxHORIZONTAL);
sizer1->Add(editor);
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto middle_text = new wxStaticText(m_parent, wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
middle_text->SetFont(wxGetApp().normal_font());
sizer1->Add(middle_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit());
m_grid_sizer->Add(sizer1);
m_grid_sizer->Add(middle_text, 0, wxALIGN_CENTER_VERTICAL);
// Add control for the "Max Z"
@@ -132,13 +132,13 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
});
//select_editor(editor, is_last_edited_range);
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto sizer2 = new wxBoxSizer(wxHORIZONTAL);
sizer2->Add(editor);
auto unit_text = new wxStaticText(m_parent, wxID_ANY, _L("mm"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
unit_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
unit_text->SetFont(wxGetApp().normal_font());
sizer2->Add(unit_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit());
sizer2->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL);
m_grid_sizer->Add(sizer2);
@@ -335,7 +335,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
m_type(type),
m_set_focus_data(set_focus_data_fn),
wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
@@ -444,7 +444,7 @@ coordf_t LayerRangeEditor::get_value()
void LayerRangeEditor::msw_rescale()
{
SetMinSize(wxSize(8 * wxGetApp().em_unit(), wxDefaultCoord));
SetMinSize(wxSize(wxGetApp().em_unit(), wxDefaultCoord));
}
} //namespace GUI
+102 -101
View File
@@ -1,3 +1,9 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, David Kocík @kocikdav, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2021 Mathias Rasmussen
///|/ Copyright (c) 2020 rongith
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "libslic3r/PresetBundle.hpp"
#include "GUI_ObjectList.hpp"
@@ -304,6 +310,7 @@ ObjectList::ObjectList(wxWindow* parent) :
ObjectList::~ObjectList()
{
delete m_objects_model;
}
void ObjectList::set_min_height()
@@ -1949,7 +1956,7 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo
const BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx);
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
const GLVolume* v = selection.get_first_volume();
const Geometry::Transformation inst_transform = v->get_instance_transformation();
const Transform3d inv_inst_transform = inst_transform.get_matrix(true).inverse();
const Vec3d instance_offset = v->get_instance_offset();
@@ -2085,7 +2092,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
ModelVolume *new_volume = model_object.add_volume(std::move(mesh), type);
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
const GLVolume* v = selection.get_first_volume();
// Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
@@ -2810,7 +2817,7 @@ void ObjectList::merge(bool to_multipart_object)
}
}
void ObjectList::merge_volumes()
/*void ObjectList::merge_volumes()
{
std::vector<int> obj_idxs, vol_idxs;
get_selection_indexes(obj_idxs, vol_idxs);
@@ -2838,11 +2845,11 @@ void ObjectList::merge_volumes()
else {
for (int vol_idx : vol_idxs)
selection.add_volume(last_obj_idx, vol_idx, 0, false);
}*/
}#1#
#else
wxGetApp().plater()->merge(obj_idxs[0], vol_idxs);
#endif
}
}*/
void ObjectList::layers_editing()
{
@@ -3029,6 +3036,93 @@ bool ObjectList::can_split_instances()
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
}
bool ObjectList::has_selected_cut_object() const
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
// ys_FIXME: The obj_idx<size condition is a workaround for https://github.com/prusa3d/PrusaSlicer/issues/11186,
// but not the correct fix. The deleted item probably should not be in sels in the first place.
if (obj_idx >= 0 && obj_idx < int(m_objects->size()) && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
invalidate_cut_info_for_object(size_t(obj_idx));
}
}
void ObjectList::invalidate_cut_info_for_object(int obj_idx)
{
ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut())
return;
take_snapshot(_u8L("Invalidate cut info"));
const CutObjectBase cut_id = init_obj->cut_id;
// invalidate cut for related objects (which have the same cut_id)
for (size_t idx = 0; idx < m_objects->size(); idx++)
if (ModelObject* obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) {
obj->invalidate_cut();
update_info_items(idx);
add_volumes_to_object_in_list(idx);
}
update_lock_icons_for_model();
}
void ObjectList::delete_all_connectors_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
delete_all_connectors_for_object(size_t(obj_idx));
}
}
void ObjectList::delete_all_connectors_for_object(int obj_idx)
{
ModelObject* init_obj = object(obj_idx);
if (!init_obj->is_cut())
return;
take_snapshot(_u8L("Delete all connectors"));
const CutObjectBase cut_id = init_obj->cut_id;
// Delete all connectors for related objects (which have the same cut_id)
Model& model = wxGetApp().plater()->model();
for (int idx = int(m_objects->size())-1; idx >= 0; idx--)
if (ModelObject* obj = object(idx); obj->cut_id.is_equal(cut_id)) {
obj->delete_connectors();
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
model.delete_object(idx);
m_objects_model->Delete(m_objects_model->GetItemById(idx));
continue;
}
update_info_items(idx);
add_volumes_to_object_in_list(idx);
changed_object(int(idx));
}
update_lock_icons_for_model();
}
bool ObjectList::can_merge_to_multipart_object() const
{
if (has_selected_cut_object())
@@ -3043,10 +3137,9 @@ bool ObjectList::can_merge_to_multipart_object() const
return false;
// should be selected just objects
for (wxDataViewItem item : sels) {
for (wxDataViewItem item : sels)
if (!(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return false;
}
return true;
}
@@ -3071,97 +3164,6 @@ bool ObjectList::can_mesh_boolean() const
return (*m_objects)[obj_idx]->volumes.size() > 1 || ((*m_objects)[obj_idx]->volumes.size() == 1 && (*m_objects)[obj_idx]->volumes[0]->is_splittable());
}
bool ObjectList::has_selected_cut_object() const
{
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())
return false;
for (wxDataViewItem item : sels) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0 && object(obj_idx)->is_cut())
return true;
}
return false;
}
void ObjectList::invalidate_cut_info_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
invalidate_cut_info_for_object(size_t(obj_idx));
}
}
void ObjectList::invalidate_cut_info_for_object(int obj_idx)
{
ModelObject *init_obj = object(obj_idx);
if (!init_obj->is_cut()) return;
take_snapshot("Invalidate cut info");
const CutObjectBase cut_id = init_obj->cut_id;
// invalidate cut for related objects (which have the same cut_id)
for (size_t idx = 0; idx < m_objects->size(); idx++)
if (ModelObject *obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) {
obj->invalidate_cut();
update_info_items(idx);
add_volumes_to_object_in_list(idx);
}
update_lock_icons_for_model();
}
void ObjectList::delete_all_connectors_for_selection()
{
const wxDataViewItem item = GetSelection();
if (item) {
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx >= 0)
delete_all_connectors_for_object(size_t(obj_idx));
}
}
void ObjectList::delete_all_connectors_for_object(int obj_idx)
{
ModelObject *init_obj = object(obj_idx);
if (!init_obj->is_cut())
return;
take_snapshot("Delete all connectors");
auto has_solid_mesh = [](ModelObject* obj) {
for (const ModelVolume *volume : obj->volumes)
if (volume->is_model_part()) return true;
return false;
};
const CutObjectBase cut_id = init_obj->cut_id;
// Delete all connectors for related objects (which have the same cut_id)
Model &model = wxGetApp().plater()->model();
for (int idx = int(m_objects->size()) - 1; idx >= 0; idx--)
if (ModelObject *obj = object(idx); obj->cut_id.is_equal(cut_id)) {
obj->delete_connectors();
if (obj->volumes.empty() || !has_solid_mesh(obj)) {
model.delete_object(idx);
m_objects_model->Delete(m_objects_model->GetItemById(idx));
continue;
}
update_info_items(idx);
add_volumes_to_object_in_list(idx);
changed_object(int(idx));
}
update_lock_icons_for_model();
}
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
void ObjectList::changed_object(const int obj_idx/* = -1*/) const
{
@@ -4349,7 +4351,7 @@ void ObjectList::update_selections()
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
}
else if (selection.is_single_volume() || selection.is_any_modifier()) {
const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin());
const auto gl_vol = selection.get_first_volume();
if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx())
return;
}
@@ -4425,8 +4427,7 @@ void ObjectList::update_selections()
{
if (m_selection_mode & smSettings)
{
const auto idx = *selection.get_volume_idxs().begin();
const auto gl_vol = selection.get_volume(idx);
const auto gl_vol = selection.get_first_volume();
if (gl_vol->volume_idx() >= 0) {
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
// are not associated with ModelVolumes, but they are temporarily generated by the backend
+2 -2
View File
@@ -205,7 +205,7 @@ private:
public:
ObjectList(wxWindow* parent);
~ObjectList();
~ObjectList() override;
void set_min_height();
void update_min_height();
@@ -297,7 +297,7 @@ public:
void del_info_item(const int obj_idx, InfoItemType type);
void split();
void merge(bool to_multipart_object);
void merge_volumes(); // BBS: merge parts to single part
// void merge_volumes(); // BBS: merge parts to single part
void layers_editing();
void boolean(); // BBS: Boolean Operation of parts
+3 -3
View File
@@ -2813,13 +2813,13 @@ int ObjectTablePanel::init_filaments_and_colors()
}
unsigned int i = 0;
unsigned char rgb[3];
ColorRGB rgb;
while (i < m_filaments_count) {
const std::string& txt_color = global_config->opt_string("filament_colour", i);
if (i < color_count) {
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
if (decode_color(txt_color, rgb))
{
m_filaments_colors[i] = wxColour(rgb[0], rgb[1], rgb[2]);
m_filaments_colors[i] = wxColour(rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar());
}
else
{
-1
View File
@@ -2,7 +2,6 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/Layer.hpp"
#include "IMSlider.hpp"
#include "IMSlider_Utils.hpp"
#include "GUI_Preview.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
+18 -29
View File
@@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_Utils_hpp_
#define slic3r_GUI_Utils_hpp_
@@ -23,6 +27,7 @@
#include "Event.hpp"
#include "../libslic3r/libslic3r_version.h"
#include "../libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp"
class wxCheckBox;
@@ -39,28 +44,10 @@ inline int hex_to_int(const char c)
return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
static std::array<float, 4> decode_color_to_float_array(const std::string color)
static ColorRGBA decode_color_to_float_array(const std::string color)
{
// set alpha to 1.0f by default
std::array<float, 4> ret = {0, 0, 0, 1.0f};
const char * c = color.data() + 1;
if (color.size() == 7 && color.front() == '#') {
for (size_t j = 0; j < 3; ++j) {
int digit1 = hex_to_int(*c++);
int digit2 = hex_to_int(*c++);
if (digit1 == -1 || digit2 == -1) break;
ret[j] = float(digit1 * 16 + digit2) / 255.0f;
}
}
else if (color.size() == 9 && color.front() == '#') {
for (size_t j = 0; j < 4; ++j) {
int digit1 = hex_to_int(*c++);
int digit2 = hex_to_int(*c++);
if (digit1 == -1 || digit2 == -1) break;
ret[j] = float(digit1 * 16 + digit2) / 255.0f;
}
}
ColorRGBA ret = ColorRGBA::BLACK();
decode_color(color, ret);
return ret;
}
@@ -470,14 +457,6 @@ public:
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics);
inline int hex_digit_to_int(const char c)
{
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
class TaskTimer
{
std::chrono::milliseconds start_timer;
@@ -488,6 +467,16 @@ public:
~TaskTimer();
};
class KeyAutoRepeatFilter
{
size_t m_count{ 0 };
public:
void increase_count() { ++m_count; }
void reset_count() { m_count = 0; }
bool is_first() const { return m_count == 0; }
};
/* Image Generator */
#define _3MF_COVER_SIZE wxSize(240, 240)
+193 -129
View File
@@ -17,6 +17,8 @@
#include <imgui/imgui_internal.h>
#include "slic3r/GUI/CameraUtils.hpp"
namespace Slic3r {
namespace GUI {
@@ -27,32 +29,12 @@ const int c_connectors_group_id = 4;
const float UndefFloat = -999.f;
// connector colors
using ColorRGBA = std::array<float, 4>;
static const ColorRGBA BLACK() { return {0.0f, 0.0f, 0.0f, 1.0f}; }
static const ColorRGBA BLUE() { return {0.0f, 0.0f, 1.0f, 1.0f}; }
static const ColorRGBA BLUEISH() { return {0.5f, 0.5f, 1.0f, 1.0f}; }
static const ColorRGBA CYAN() { return {0.0f, 1.0f, 1.0f, 1.0f}; }
static const ColorRGBA DARK_GRAY() { return {0.25f, 0.25f, 0.25f, 1.0f}; }
static const ColorRGBA DARK_YELLOW() { return {0.5f, 0.5f, 0.0f, 1.0f}; }
static const ColorRGBA GRAY() { return {0.5f, 0.5f, 0.5f, 1.0f}; }
static const ColorRGBA GREEN() { return {0.0f, 1.0f, 0.0f, 1.0f}; }
static const ColorRGBA GREENISH() { return {0.5f, 1.0f, 0.5f, 1.0f}; }
static const ColorRGBA LIGHT_GRAY() { return {0.75f, 0.75f, 0.75f, 1.0f}; }
static const ColorRGBA MAGENTA() { return {1.0f, 0.0f, 1.0f, 1.0f}; }
static const ColorRGBA ORANGE() { return {0.923f, 0.504f, 0.264f, 1.0f}; }
static const ColorRGBA RED() { return {1.0f, 0.0f, 0.0f, 1.0f}; }
static const ColorRGBA REDISH() { return {1.0f, 0.5f, 0.5f, 1.0f}; }
static const ColorRGBA YELLOW() { return {1.0f, 1.0f, 0.0f, 1.0f}; }
static const ColorRGBA WHITE() { return {1.0f, 1.0f, 1.0f, 1.0f}; }
static const ColorRGBA PLAG_COLOR = YELLOW();
static const ColorRGBA DOWEL_COLOR = DARK_YELLOW();
static const ColorRGBA HOVERED_PLAG_COLOR = CYAN();
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW();
static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN();
static const ColorRGBA HOVERED_DOWEL_COLOR = {0.0f, 0.5f, 0.5f, 1.0f};
static const ColorRGBA SELECTED_PLAG_COLOR = GRAY();
static const ColorRGBA SELECTED_DOWEL_COLOR = GRAY(); // DARK_GRAY();
static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY();
static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::GRAY(); // DARK_GRAY();
static const ColorRGBA CONNECTOR_DEF_COLOR = {1.0f, 1.0f, 1.0f, 0.5f};
static const ColorRGBA CONNECTOR_ERR_COLOR = {1.0f, 0.3f, 0.3f, 0.5f};
static const ColorRGBA HOVERED_ERR_COLOR = {1.0f, 0.3f, 0.3f, 1.0f};
@@ -103,8 +85,8 @@ static void rotate_z_3d(std::array<Vec3d, 4>& verts, float radian_angle)
const double GLGizmoAdvancedCut::Offset = 10.0;
const double GLGizmoAdvancedCut::Margin = 20.0;
const std::array<float, 4> GLGizmoAdvancedCut::GrabberColor = { 1.0, 1.0, 0.0, 1.0 };
const std::array<float, 4> GLGizmoAdvancedCut::GrabberHoverColor = { 0.7, 0.7, 0.0, 1.0};
const ColorRGBA GLGizmoAdvancedCut::GrabberColor = { 1.0f, 1.0f, 0.0f, 1.0f };
const ColorRGBA GLGizmoAdvancedCut::GrabberHoverColor = { 0.7f, 0.7f, 0.0f, 1.0f };
GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoRotate3D(parent, icon_filename, sprite_id, nullptr)
@@ -124,13 +106,19 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic
for (int i = 0; i < 4; i++)
m_cut_plane_points[i] = { 0., 0., 0. };
set_group_id(m_gizmos.size());
m_group_id = (m_gizmos.size());
m_rotation.setZero();
//m_current_base_rotation.setZero();
m_rotate_cmds.clear();
m_buffered_rotation.setZero();
}
void GLGizmoAdvancedCut::data_changed(bool is_serializing)
{
GLGizmoRotate3D::data_changed(is_serializing);
finish_rotation();
}
bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down)
{
CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors;
@@ -146,7 +134,7 @@ bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mous
return false;
if (m_hover_id != -1) {
start_dragging();
//start_dragging();
return true;
}
@@ -241,7 +229,7 @@ bool GLGizmoAdvancedCut::unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &p
Vec3d point;
Vec3d direction;
Vec3d hit;
MeshRaycaster::line_from_mouse_pos_static(mouse_pos, Transform3d::Identity(), camera, point, direction);
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
Vec3d normal = -cp->get_normal().cast<double>();
double den = normal.dot(direction);
if (den != 0.) {
@@ -431,38 +419,32 @@ CommonGizmosDataID GLGizmoAdvancedCut::on_get_requirements() const
void GLGizmoAdvancedCut::on_start_dragging()
{
for (auto gizmo : m_gizmos) {
if (m_hover_id == gizmo.get_group_id()) {
gizmo.start_dragging();
return;
}
if (m_hover_id == X || m_hover_id == Y || m_hover_id == Z) {
m_gizmos[m_hover_id].start_dragging();
} else if (m_hover_id == c_connectors_group_id - 1) {
const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
m_start_movement = m_movement;
m_start_height = m_height;
m_drag_pos = m_move_grabber.center;
}
if (m_hover_id != get_group_id())
return;
const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
m_start_movement = m_movement;
m_start_height = m_height;
m_drag_pos = m_move_grabber.center;
if (m_hover_id >= c_connectors_group_id)
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move connector");
}
void GLGizmoAdvancedCut::on_stop_dragging()
{
if (m_hover_id == X || m_hover_id == Y || m_hover_id == Z) {
m_gizmos[m_hover_id].stop_dragging();
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Rotate cut plane");
} else if (m_hover_id == c_connectors_group_id - 1) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move cut plane");
} else if (m_hover_id >= c_connectors_group_id) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move connector");
}
}
void GLGizmoAdvancedCut::on_update(const UpdateData& data)
void GLGizmoAdvancedCut::on_dragging(const UpdateData &data)
{
GLGizmoRotate3D::on_update(data);
GLGizmoRotate3D::on_dragging(data);
Vec3d rotation;
for (int i = 0; i < 3; i++)
@@ -475,7 +457,7 @@ void GLGizmoAdvancedCut::on_update(const UpdateData& data)
m_rotation = rotation;
//m_move_grabber.angles = m_current_base_rotation + m_rotation;
if (m_hover_id == get_group_id()) {
if (m_hover_id == m_group_id) {
double move = calc_projection(data.mouse_ray);
set_movement(m_start_movement + move);
Vec3d plane_normal = get_plane_normal();
@@ -511,7 +493,7 @@ void GLGizmoAdvancedCut::on_render()
render_cut_line();
}
/*
void GLGizmoAdvancedCut::on_render_for_picking()
{
GLGizmoRotate3D::on_render_for_picking();
@@ -525,12 +507,17 @@ void GLGizmoAdvancedCut::on_render_for_picking()
float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0);
#endif
std::array<float, 4> color = picking_color_component(0);
m_move_grabber.color[0] = color[0];
m_move_grabber.color[1] = color[1];
m_move_grabber.color[2] = color[2];
m_move_grabber.color[3] = color[3];
m_move_grabber.render_for_picking(mean_size);
m_move_grabber.color = picking_color_component(0);
GLShaderProgram *shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
const Camera &camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
m_move_grabber.render_for_picking(mean_size);
shader->stop_using();
}
glsafe(::glEnable(GL_DEPTH_TEST));
auto inst_id = m_c->selection_info()->get_active_instance();
@@ -548,7 +535,6 @@ void GLGizmoAdvancedCut::on_render_for_picking()
Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ();
float height = connector.height;
const Camera &camera = wxGetApp().plater()->get_camera();
if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prizm) {
pos -= height * m_cut_plane_normal;
height *= 2;
@@ -561,14 +547,13 @@ void GLGizmoAdvancedCut::on_render_for_picking()
Transform3d scale_tf = Transform3d::Identity();
scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast<double>());
const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf;
const Transform3d model_matrix = translate_tf * m_rotate_matrix * scale_tf;
std::array<float, 4> color = picking_color_component(i+1);
render_connector_model(m_shapes[connectors[i].attribs], color, view_model_matrix, true);
ColorRGBA color = picking_color_component(i+1);
render_connector_model(m_shapes[connectors[i].attribs], color, model_matrix, true);
}
}
*/
void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_limit)
{
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
@@ -638,7 +623,7 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoAdvancedCut: Invalid object selection");
// m_cut_z is the distance from the bed. Subtract possible SLA elevation.
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
const GLVolume* first_glvolume = selection.get_first_volume();
// perform cut
{
@@ -882,67 +867,117 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers()
point += object_offset;
}
// draw plane
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
GLShaderProgram *shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
::glBegin(GL_QUADS);
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
for (const Vec3d& point : plane_points_rot) {
::glVertex3f(point(0), point(1), point(2));
}
glsafe(::glEnd());
// draw plane
{
m_plane.reset();
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f };
init_data.reserve_vertices(4);
init_data.reserve_vertices(6);
// Draw the grabber and the connecting line
Vec3d plane_center_rot = calc_plane_center(plane_points_rot);
m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset;
// m_move_grabber.angles = m_current_base_rotation + m_rotation;
// vertices
for (const Vec3d &point : plane_points_rot) {
init_data.add_vertex((Vec3f)point.cast<float>());
}
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
glsafe(::glColor3f(1.0, 1.0, 0.0));
glLineStipple(1, 0x0FFF);
glEnable(GL_LINE_STIPPLE);
::glBegin(GL_LINES);
::glVertex3dv(plane_center_rot.data());
::glVertex3dv(m_move_grabber.center.data());
glsafe(::glEnd());
glDisable(GL_LINE_STIPPLE);
// indices
init_data.add_triangle(0, 1, 2);
init_data.add_triangle(2, 3, 0);
// std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color);
// m_move_grabber.color = GrabberColor;
// m_move_grabber.hover_color = GrabberHoverColor;
// m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
bool hover = (m_hover_id == get_group_id());
std::array<float, 4> render_color;
if (hover) {
render_color = GrabberHoverColor;
}
else
render_color = GrabberColor;
m_plane.init_from(std::move(init_data));
}
const Camera &camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
m_plane.render();
const GLModel &cube = m_move_grabber.get_cube();
// BBS set to fixed size grabber
// float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size));
float fullsize = 8.0f;
if (GLGizmoBase::INV_ZOOM > 0) {
fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM;
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
// Draw the grabber and the connecting line
Vec3d plane_center_rot = calc_plane_center(plane_points_rot);
m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset;
// m_move_grabber.angles = m_current_base_rotation + m_rotation;
{
m_grabber_connection.reset();
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
init_data.color = ColorRGBA::YELLOW();
init_data.reserve_vertices(2);
init_data.reserve_vertices(2);
// vertices
init_data.add_vertex((Vec3f)plane_center_rot.cast<float>());
init_data.add_vertex((Vec3f)m_move_grabber.center.cast<float>());
// indices
init_data.add_line(0, 1);
m_grabber_connection.init_from(std::move(init_data));
}
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
glLineStipple(1, 0x0FFF);
glEnable(GL_LINE_STIPPLE);
m_grabber_connection.render();
glDisable(GL_LINE_STIPPLE);
shader->stop_using();
}
const_cast<GLModel*>(&cube)->set_color(-1, render_color);
{
GLShaderProgram *shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
// std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color);
// m_move_grabber.color = GrabberColor;
// m_move_grabber.hover_color = GrabberHoverColor;
// m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
bool hover = (m_hover_id == m_group_id);
ColorRGBA render_color;
if (hover) {
render_color = GrabberHoverColor;
}
else
render_color = GrabberColor;
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_move_grabber.center.x(), m_move_grabber.center.y(), m_move_grabber.center.z()));
glsafe(::glMultMatrixd(m_rotate_matrix.data()));
PickingModel &cube = m_move_grabber.get_cube();
// BBS set to fixed size grabber
// float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size));
float fullsize = 8.0f;
if (GLGizmoBase::INV_ZOOM > 0) {
fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM;
}
glsafe(::glScaled(fullsize, fullsize, fullsize));
cube.render();
glsafe(::glPopMatrix());
cube.model.set_color(render_color);
const Transform3d trafo_matrix = Geometry::assemble_transform(m_move_grabber.center) * m_rotate_matrix *
Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), fullsize * Vec3d::Ones());
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
cube.model.render();
shader->stop_using();
}
// Should be placed at last, because GLGizmoRotate3D clears depth buffer
set_center(m_cut_plane_center);
@@ -1007,9 +1042,9 @@ void GLGizmoAdvancedCut::render_connectors()
Transform3d scale_tf = Transform3d::Identity();
scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast<double>());
const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf;
const Transform3d model_matrix = translate_tf * m_rotate_matrix * scale_tf;
render_connector_model(m_shapes[connector.attribs], render_color, view_model_matrix);
render_connector_model(m_shapes[connector.attribs], render_color, model_matrix);
}
}
@@ -1025,36 +1060,65 @@ void GLGizmoAdvancedCut::render_cut_line()
if (!cut_line_processing() || m_cut_line_end == Vec3d::Zero())
return;
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glColor3f(0.0, 1.0, 0.0));
glEnable(GL_LINE_STIPPLE);
::glBegin(GL_LINES);
::glVertex3dv(m_cut_line_begin.data());
::glVertex3dv(m_cut_line_end.data());
glsafe(::glEnd());
glDisable(GL_LINE_STIPPLE);
glsafe(::glDisable(GL_DEPTH_TEST));
GLShaderProgram *shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
{
m_cut_line.reset();
GLModel::Geometry init_data;
init_data.format = {GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3};
init_data.color = ColorRGBA::GREEN();
init_data.reserve_vertices(2);
init_data.reserve_vertices(2);
// vertices
init_data.add_vertex((Vec3f) m_cut_line_begin.cast<float>());
init_data.add_vertex((Vec3f) m_cut_line_end.cast<float>());
// indices
init_data.add_line(0, 1);
m_cut_line.init_from(std::move(init_data));
}
const Camera &camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
glEnable(GL_LINE_STIPPLE);
glLineStipple(1, 0x0FFF);
m_cut_line.render();
glDisable(GL_LINE_STIPPLE);
shader->stop_using();
}
}
void GLGizmoAdvancedCut::render_connector_model(GLModel &model, const std::array<float, 4> &color, Transform3d view_model_matrix, bool for_picking)
void GLGizmoAdvancedCut::render_connector_model(GLModel &model, const ColorRGBA &color, Transform3d model_matrix, bool for_picking)
{
glPushMatrix();
GLShaderProgram *shader = nullptr;
if (for_picking)
shader = wxGetApp().get_shader("cali");
shader = wxGetApp().get_shader("flat");
else
shader = wxGetApp().get_shader("gouraud_light");
if (shader) {
shader->start_using();
glsafe(::glMultMatrixd(view_model_matrix.data()));
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
model.set_color(-1, color);
model.set_color(color);
model.render();
shader->stop_using();
}
glPopMatrix();
}
void GLGizmoAdvancedCut::clear_selection()
@@ -1814,7 +1878,7 @@ bool GLGizmoAdvancedCut::process_cut_line(SLAGizmoEventType action, const Vec2d
Vec3d pt;
Vec3d dir;
MeshRaycaster::line_from_mouse_pos_static(mouse_position, Transform3d::Identity(), camera, pt, dir);
CameraUtils::ray_from_screen_pos(camera, mouse_position, pt, dir);
dir.normalize();
pt += dir; // Move the pt along dir so it is not clipped.
+19 -37
View File
@@ -27,8 +27,8 @@ struct Rotate_data {
private:
static const double Offset;
static const double Margin;
static const std::array<float, 4> GrabberColor;
static const std::array<float, 4> GrabberHoverColor;
static const ColorRGBA GrabberColor;
static const ColorRGBA GrabberHoverColor;
mutable double m_movement;
mutable double m_height; // height of cut plane to heatbed
@@ -53,6 +53,9 @@ private:
bool m_place_on_cut_lower{false};
bool m_rotate_upper{false};
bool m_rotate_lower{false};
GLModel m_plane;
GLModel m_grabber_connection;
GLModel m_cut_line;
bool m_do_segment;
double m_segment_smoothing_alpha;
@@ -135,45 +138,24 @@ public:
virtual bool apply_clipping_plane() { return m_connectors_editing; }
void data_changed(bool is_serializing) override;
protected:
virtual bool on_init();
virtual void on_load(cereal::BinaryInputArchive &ar) override;
virtual void on_save(cereal::BinaryOutputArchive &ar) const override;
virtual std::string on_get_name() const;
virtual void on_set_state();
virtual bool on_is_activable() const;
virtual CommonGizmosDataID on_get_requirements() const override;
virtual void on_start_dragging() override;
virtual void on_stop_dragging() override;
virtual void on_update(const UpdateData& data);
virtual void on_render();
virtual void on_render_for_picking();
bool on_init() override;
void on_load(cereal::BinaryInputArchive &ar) override;
void on_save(cereal::BinaryOutputArchive &ar) const override;
std::string on_get_name() const override;
void on_set_state() override;
bool on_is_activable() const override;
CommonGizmosDataID on_get_requirements() const override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData& data) override;
void on_render() override;
virtual void on_render_input_window(float x, float y, float bottom_limit);
void show_tooltip_information(float x, float y);
virtual void on_enable_grabber(unsigned int id)
{
if (id < 3)
m_gizmos[id].enable_grabber(0);
else if (id == 3)
this->enable_grabber(0);
}
virtual void on_disable_grabber(unsigned int id)
{
if (id < 3)
m_gizmos[id].disable_grabber(0);
else if (id == 3)
this->disable_grabber(0);
}
virtual void on_set_hover_id()
{
for (int i = 0; i < 3; ++i)
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
private:
void perform_cut(const Selection& selection);
bool can_perform_cut() const;
@@ -201,7 +183,7 @@ private:
void render_connectors();
void render_clipper_cut();
void render_cut_line();
void render_connector_model(GLModel &model, const std::array<float, 4>& color, Transform3d view_model_matrix, bool for_picking = false);
void render_connector_model(GLModel &model, const ColorRGBA& color, Transform3d model_matrix, bool for_picking = false);
void clear_selection();
void init_connector_shapes();
+234 -186
View File
@@ -1,9 +1,14 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_Colors.hpp"
// TODO: Display tooltips quicker on Linux
@@ -21,73 +26,63 @@ const float GLGizmoBase::Grabber::FixedGrabberSize = 16.0f;
const float GLGizmoBase::Grabber::FixedRadiusSize = 80.0f;
std::array<float, 4> GLGizmoBase::DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f };
std::array<float, 4> GLGizmoBase::DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 4> GLGizmoBase::DEFAULT_HIGHLIGHT_COLOR = { 1.0f, 0.38f, 0.0f, 1.0f };
std::array<std::array<float, 4>, 3> GLGizmoBase::AXES_HOVER_COLOR = {{
ColorRGBA GLGizmoBase::DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f };
ColorRGBA GLGizmoBase::DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
ColorRGBA GLGizmoBase::DEFAULT_HIGHLIGHT_COLOR = {1.0f, 0.38f, 0.0f, 1.0f};
std::array<ColorRGBA, 3> GLGizmoBase::AXES_HOVER_COLOR = {{
{ 0.7f, 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.7f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.7f, 1.0f }
}};
std::array<std::array<float, 4>, 3> GLGizmoBase::AXES_COLOR = { {
std::array<ColorRGBA, 3> GLGizmoBase::AXES_COLOR = {{
{ 1.0, 0.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 1.0f, 1.0f }
}};
std::array<float, 4> GLGizmoBase::CONSTRAINED_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f };
std::array<float, 4> GLGizmoBase::FLATTEN_COLOR = { 0.96f, 0.93f, 0.93f, 0.5f };
std::array<float, 4> GLGizmoBase::FLATTEN_HOVER_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
ColorRGBA GLGizmoBase::CONSTRAINED_COLOR = {0.5f, 0.5f, 0.5f, 1.0f};
ColorRGBA GLGizmoBase::FLATTEN_COLOR = {0.96f, 0.93f, 0.93f, 0.5f};
ColorRGBA GLGizmoBase::FLATTEN_HOVER_COLOR = {1.0f, 1.0f, 1.0f, 0.75f};
// new style color
std::array<float, 4> GLGizmoBase::GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f};
std::array<float, 4> GLGizmoBase::GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f};
std::array<float, 4> GLGizmoBase::GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f};
std::array<float, 4> GLGizmoBase::GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f};
ColorRGBA GLGizmoBase::GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f};
ColorRGBA GLGizmoBase::GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f};
ColorRGBA GLGizmoBase::GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f};
ColorRGBA GLGizmoBase::GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f};
void GLGizmoBase::update_render_colors()
{
GLGizmoBase::AXES_COLOR = { {
GLColor(RenderColor::colors[RenderCol_Grabber_X]),
GLColor(RenderColor::colors[RenderCol_Grabber_Y]),
GLColor(RenderColor::colors[RenderCol_Grabber_Z])
ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_X]),
ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_Y]),
ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Grabber_Z])
} };
GLGizmoBase::FLATTEN_COLOR = GLColor(RenderColor::colors[RenderCol_Flatten_Plane]);
GLGizmoBase::FLATTEN_HOVER_COLOR = GLColor(RenderColor::colors[RenderCol_Flatten_Plane_Hover]);
GLGizmoBase::FLATTEN_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Flatten_Plane]);
GLGizmoBase::FLATTEN_HOVER_COLOR = ImGuiWrapper::from_ImVec4(RenderColor::colors[RenderCol_Flatten_Plane_Hover]);
}
void GLGizmoBase::load_render_colors()
{
RenderColor::colors[RenderCol_Grabber_X] = IMColor(GLGizmoBase::AXES_COLOR[0]);
RenderColor::colors[RenderCol_Grabber_Y] = IMColor(GLGizmoBase::AXES_COLOR[1]);
RenderColor::colors[RenderCol_Grabber_Z] = IMColor(GLGizmoBase::AXES_COLOR[2]);
RenderColor::colors[RenderCol_Flatten_Plane] = IMColor(GLGizmoBase::FLATTEN_COLOR);
RenderColor::colors[RenderCol_Flatten_Plane_Hover] = IMColor(GLGizmoBase::FLATTEN_HOVER_COLOR);
RenderColor::colors[RenderCol_Grabber_X] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[0]);
RenderColor::colors[RenderCol_Grabber_Y] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[1]);
RenderColor::colors[RenderCol_Grabber_Z] = ImGuiWrapper::to_ImVec4(GLGizmoBase::AXES_COLOR[2]);
RenderColor::colors[RenderCol_Flatten_Plane] = ImGuiWrapper::to_ImVec4(GLGizmoBase::FLATTEN_COLOR);
RenderColor::colors[RenderCol_Flatten_Plane_Hover] = ImGuiWrapper::to_ImVec4(GLGizmoBase::FLATTEN_HOVER_COLOR);
}
GLGizmoBase::Grabber::Grabber()
: center(Vec3d::Zero())
, angles(Vec3d::Zero())
, dragging(false)
, enabled(true)
{
color = GRABBER_NORMAL_COL;
hover_color = GRABBER_HOVER_COL;
}
PickingModel GLGizmoBase::Grabber::s_cube;
PickingModel GLGizmoBase::Grabber::s_cone;
void GLGizmoBase::Grabber::render(bool hover, float size) const
GLGizmoBase::Grabber::~Grabber()
{
std::array<float, 4> render_color;
if (hover) {
render_color = hover_color;
}
else
render_color = color;
if (s_cube.model.is_initialized())
s_cube.model.reset();
render(size, render_color, false);
if (s_cone.model.is_initialized())
s_cone.model.reset();
}
float GLGizmoBase::Grabber::get_half_size(float size) const
@@ -100,70 +95,124 @@ float GLGizmoBase::Grabber::get_dragging_half_size(float size) const
return get_half_size(size) * DraggingScaleFactor;
}
const GLModel& GLGizmoBase::Grabber::get_cube() const
PickingModel &GLGizmoBase::Grabber::get_cube()
{
if (! cube_initialized) {
if (!s_cube.model.is_initialized()) {
// This cannot be done in constructor, OpenGL is not yet
// initialized at that point (on Linux at least).
indexed_triangle_set mesh = its_make_cube(1., 1., 1.);
its_translate(mesh, Vec3f(-0.5, -0.5, -0.5));
const_cast<GLModel&>(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } });
const_cast<bool&>(cube_initialized) = true;
indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0);
its_translate(its, -0.5f * Vec3f::Ones());
s_cube.model.init_from(its);
s_cube.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
return cube;
return s_cube;
}
void GLGizmoBase::Grabber::render(float size, const std::array<float, 4>& render_color, bool picking) const
void GLGizmoBase::Grabber::register_raycasters_for_picking(int id)
{
if (! cube_initialized) {
picking_id = id;
// registration will happen on next call to render()
}
void GLGizmoBase::Grabber::unregister_raycasters_for_picking()
{
wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, picking_id);
picking_id = -1;
raycasters = { nullptr };
}
void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color)
{
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader == nullptr)
return;
if (!s_cube.model.is_initialized()) {
// This cannot be done in constructor, OpenGL is not yet
// initialized at that point (on Linux at least).
indexed_triangle_set mesh = its_make_cube(1., 1., 1.);
its_translate(mesh, Vec3f(-0.5, -0.5, -0.5));
const_cast<GLModel&>(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } });
const_cast<bool&>(cube_initialized) = true;
indexed_triangle_set its = its_make_cube(1.0, 1.0, 1.0);
its_translate(its, -0.5f * Vec3f::Ones());
s_cube.model.init_from(its);
s_cube.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
if (!s_cone.model.is_initialized()) {
indexed_triangle_set its = its_make_cone(1.0, 1.0, double(PI) / 18.0);
s_cone.model.init_from(its);
s_cone.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
//BBS set to fixed size grabber
//float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size));
float fullsize = 8.0f;
if (GLGizmoBase::INV_ZOOM > 0) {
fullsize = FixedGrabberSize * GLGizmoBase::INV_ZOOM;
const float grabber_size = FixedGrabberSize * INV_ZOOM;
const double extension_size = 0.75 * FixedGrabberSize * INV_ZOOM;
s_cube.model.set_color(render_color);
s_cone.model.set_color(render_color);
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Transform3d& view_matrix = camera.get_view_matrix();
const Matrix3d view_matrix_no_offset = view_matrix.matrix().block(0, 0, 3, 3);
auto render_extension = [&view_matrix, &view_matrix_no_offset, shader, this](int idx, PickingModel &model, const Transform3d &model_matrix) {
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
const Matrix3d view_normal_matrix = view_matrix_no_offset * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
model.model.render();
if (raycasters[idx] == nullptr) {
GLCanvas3D &canvas = *wxGetApp().plater()->canvas3D();
raycasters[idx] = canvas.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, picking_id, *model.mesh_raycaster, model_matrix);
} else {
raycasters[idx]->set_transform(model_matrix);
}
};
if (extensions == EGrabberExtension::PosZ) {
const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, Vec3d(0.75 * extension_size, 0.75 * extension_size, 2.0 * extension_size));
render_extension(0, s_cone, model_matrix);
} else {
const Transform3d model_matrix = matrix * Geometry::assemble_transform(center, angles, grabber_size * Vec3d::Ones());
render_extension(0, s_cube, model_matrix);
const Transform3d extension_model_matrix_base = matrix * Geometry::assemble_transform(center, angles);
const Vec3d extension_scale(0.75 * extension_size, 0.75 * extension_size, 3.0 * extension_size);
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosX)) != 0) {
render_extension(1, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitX(), Vec3d(0.0, 0.5 * double(PI), 0.0), extension_scale));
}
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegX)) != 0) {
render_extension(2, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitX(), Vec3d(0.0, -0.5 * double(PI), 0.0), extension_scale));
}
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosY)) != 0) {
render_extension(3, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitY(), Vec3d(-0.5 * double(PI), 0.0, 0.0), extension_scale));
}
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegY)) != 0) {
render_extension(4, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitY(), Vec3d(0.5 * double(PI), 0.0, 0.0), extension_scale));
}
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::PosZ)) != 0) {
render_extension(5, s_cone, extension_model_matrix_base * Geometry::assemble_transform(2.0 * extension_size * Vec3d::UnitZ(), Vec3d::Zero(), extension_scale));
}
if ((int(extensions) & int(GLGizmoBase::EGrabberExtension::NegZ)) != 0) {
render_extension(6, s_cone, extension_model_matrix_base * Geometry::assemble_transform(-2.0 * extension_size * Vec3d::UnitZ(), Vec3d(double(PI), 0.0, 0.0), extension_scale));
}
}
const_cast<GLModel*>(&cube)->set_color(-1, render_color);
glsafe(::glPushMatrix());
glsafe(::glTranslated(center.x(), center.y(), center.z()));
glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0));
glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0));
glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0));
glsafe(::glScaled(fullsize, fullsize, fullsize));
cube.render();
glsafe(::glPopMatrix());
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
, m_shortcut_key(NO_SHORTCUT_KEY_VALUE)
, m_icon_filename(icon_filename)
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
, m_first_input_window_render(true)
, m_dirty(false)
{
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
m_highlight_color = DEFAULT_HIGHLIGHT_COLOR;
m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24));
m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.));
m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.));
}
std::string GLGizmoBase::get_action_snapshot_name() const
{
return "Gizmo action";
}
void GLGizmoBase::set_icon_filename(const std::string &filename) {
@@ -172,64 +221,15 @@ void GLGizmoBase::set_icon_filename(const std::string &filename) {
void GLGizmoBase::set_hover_id(int id)
{
if (m_grabbers.empty() || (id < (int)m_grabbers.size()))
{
m_hover_id = id;
on_set_hover_id();
}
}
// do not change hover id during dragging
assert(!m_dragging);
void GLGizmoBase::set_highlight_color(const std::array<float, 4>& color)
{
m_highlight_color = color;
}
void GLGizmoBase::enable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = true;
on_enable_grabber(id);
}
void GLGizmoBase::disable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = false;
on_disable_grabber(id);
}
void GLGizmoBase::start_dragging()
{
m_dragging = true;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging();
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("this %1%, m_hover_id=%2%\n")%this %m_hover_id;
}
void GLGizmoBase::stop_dragging()
{
m_dragging = false;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = false;
}
on_stop_dragging();
//BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("this %1%, m_hover_id=%2%\n")%this %m_hover_id;
}
void GLGizmoBase::update(const UpdateData& data)
{
if (m_hover_id != -1)
on_update(data);
// allow empty grabbers when not using grabbers but use hover_id - flatten, rotate
// if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
// return;
m_hover_id = id;
on_set_hover_id();
}
bool GLGizmoBase::update_items_state()
@@ -237,7 +237,7 @@ bool GLGizmoBase::update_items_state()
bool res = m_dirty;
m_dirty = false;
return res;
};
}
bool GLGizmoBase::GizmoImguiBegin(const std::string &name, int flags)
{
@@ -265,22 +265,18 @@ void GLGizmoBase::GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float
m_imgui->set_next_window_pos(x, y, flag, pivot_x, pivot_y);
}
std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const
void GLGizmoBase::register_grabbers_for_picking()
{
static const float INV_255 = 1.0f / 255.0f;
for (size_t i = 0; i < m_grabbers.size(); ++i) {
m_grabbers[i].register_raycasters_for_picking((m_group_id >= 0) ? m_group_id : i);
}
}
id = BASE_ID - id;
if (m_group_id > -1)
id -= m_group_id;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
return std::array<float, 4> {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
void GLGizmoBase::unregister_grabbers_for_picking()
{
for (size_t i = 0; i < m_grabbers.size(); ++i) {
m_grabbers[i].unregister_raycasters_for_picking();
}
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
@@ -293,34 +289,101 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
}
void GLGizmoBase::render_grabbers(float size) const
{
render_grabbers(0, m_grabbers.size() - 1, size, false);
}
void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const
{
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
glsafe(::glDisable(GL_CULL_FACE));
for (size_t i = first; i <= last; ++i) {
if (m_grabbers[i].enabled)
m_grabbers[i].render(m_hover_id == i, size);
m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size);
}
glsafe(::glEnable(GL_CULL_FACE));
shader->stop_using();
}
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
#if ENABLE_FIXED_GRABBER
float mean_size = (float)(GLGizmoBase::Grabber::FixedGrabberSize);
#else
float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0);
#endif
// help function to process grabbers
// call start_dragging, stop_dragging, on_dragging
bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
bool is_dragging_finished = false;
if (mouse_event.Moving()) {
// it should not happen but for sure
assert(!m_dragging);
if (m_dragging) is_dragging_finished = true;
else return false;
}
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) {
if (m_grabbers[i].enabled) {
std::array<float, 4> color = picking_color_component(i);
m_grabbers[i].color = color;
m_grabbers[i].render_for_picking(mean_size);
if (mouse_event.LeftDown()) {
Selection &selection = m_parent.get_selection();
if (!selection.is_empty() && m_hover_id != -1 /* &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))*/) {
selection.setup_cache();
m_dragging = true;
for (auto &grabber : m_grabbers) grabber.dragging = false;
// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
// m_grabbers[m_hover_id].dragging = true;
on_start_dragging();
// Let the plater know that the dragging started
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED));
m_parent.set_as_dirty();
return true;
}
} else if (m_dragging) {
// when mouse cursor leave window than finish actual dragging operation
bool is_leaving = mouse_event.Leaving();
if (mouse_event.Dragging()) {
Point mouse_coord(mouse_event.GetX(), mouse_event.GetY());
auto ray = m_parent.mouse_ray(mouse_coord);
UpdateData data(ray, mouse_coord);
on_dragging(data);
wxGetApp().obj_manipul()->set_dirty();
m_parent.set_as_dirty();
return true;
}
else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) {
do_stop_dragging(is_leaving);
return true;
}
}
return false;
}
void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup)
{
for (auto& grabber : m_grabbers) grabber.dragging = false;
m_dragging = false;
// NOTE: This should be part of GLCanvas3D
// Reset hover_id when leave window
if (perform_mouse_cleanup) m_parent.mouse_up_cleanup();
on_stop_dragging();
// There is prediction that after draggign, data are changed
// Data are updated twice also by canvas3D::reload_scene.
// Should be fixed.
m_parent.get_gizmos_manager().update_data();
wxGetApp().obj_manipul()->set_dirty();
// Let the plater know that the dragging finished, so a delayed
// refresh of the scene with the background processing data should
// be performed.
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
m_parent.refresh_camera_scene_box();
}
std::string GLGizmoBase::format(float value, unsigned int decimals) const
@@ -336,9 +399,12 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
{
on_render_input_window(x, y, bottom_limit);
if (m_first_input_window_render) {
// for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame
// so, we forces another frame rendering the first time the imgui window is shown
// imgui windows that don't have an initial size needs to be processed once to get one
// and are not rendered in the first frame
// so, we forces to render another frame the first time the imgui window is shown
// https://github.com/ocornut/imgui/issues/2949
m_parent.set_as_dirty();
m_parent.request_extra_frame();
m_first_input_window_render = false;
}
}
@@ -354,23 +420,5 @@ std::string GLGizmoBase::get_name(bool include_shortcut) const
return out;
}
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace GUI
} // namespace Slic3r
+111 -68
View File
@@ -1,10 +1,17 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoBase_hpp_
#define slic3r_GLGizmoBase_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Color.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/SceneRaycaster.hpp"
#include <cereal/archives/binary.hpp>
@@ -26,7 +33,6 @@ class ImGuiWrapper;
class GLCanvas3D;
enum class CommonGizmosDataID;
class CommonGizmosDataPool;
class Selection;
class GLGizmoBase
{
@@ -34,26 +40,41 @@ public:
// Starting value for ids to avoid clashing with ids used by GLVolumes
// (254 is choosen to leave some space for forward compatibility)
static const unsigned int BASE_ID = 255 * 255 * 254;
static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7;
static float INV_ZOOM;
//BBS colors
static std::array<float, 4> DEFAULT_BASE_COLOR;
static std::array<float, 4> DEFAULT_DRAG_COLOR;
static std::array<float, 4> DEFAULT_HIGHLIGHT_COLOR;
static std::array<std::array<float, 4>, 3> AXES_COLOR;
static std::array<std::array<float, 4>, 3> AXES_HOVER_COLOR;
static std::array<float, 4> CONSTRAINED_COLOR;
static std::array<float, 4> FLATTEN_COLOR;
static std::array<float, 4> FLATTEN_HOVER_COLOR;
static std::array<float, 4> GRABBER_NORMAL_COL;
static std::array<float, 4> GRABBER_HOVER_COL;
static std::array<float, 4> GRABBER_UNIFORM_COL;
static std::array<float, 4> GRABBER_UNIFORM_HOVER_COL;
static ColorRGBA DEFAULT_BASE_COLOR;
static ColorRGBA DEFAULT_DRAG_COLOR;
static ColorRGBA DEFAULT_HIGHLIGHT_COLOR;
static std::array<ColorRGBA, 3> AXES_COLOR;
static std::array<ColorRGBA, 3> AXES_HOVER_COLOR;
static ColorRGBA CONSTRAINED_COLOR;
static ColorRGBA FLATTEN_COLOR;
static ColorRGBA FLATTEN_HOVER_COLOR;
static ColorRGBA GRABBER_NORMAL_COL;
static ColorRGBA GRABBER_HOVER_COL;
static ColorRGBA GRABBER_UNIFORM_COL;
static ColorRGBA GRABBER_UNIFORM_HOVER_COL;
static void update_render_colors();
static void load_render_colors();
enum class EGrabberExtension
{
None = 0,
PosX = 1 << 0,
NegX = 1 << 1,
PosY = 1 << 2,
NegY = 1 << 3,
PosZ = 1 << 4,
NegZ = 1 << 5,
};
// Represents NO key(button on keyboard) value
static const int NO_SHORTCUT_KEY_VALUE = 0;
protected:
struct Grabber
{
@@ -63,27 +84,35 @@ protected:
static const float FixedGrabberSize;
static const float FixedRadiusSize;
Vec3d center;
Vec3d angles;
std::array<float, 4> color;
std::array<float, 4> hover_color;
bool enabled;
bool dragging;
bool enabled{ true };
bool dragging{ false };
Vec3d center{ Vec3d::Zero() };
Vec3d angles{ Vec3d::Zero() };
Transform3d matrix{ Transform3d::Identity() };
ColorRGBA color{GRABBER_NORMAL_COL};
ColorRGBA hover_color{GRABBER_HOVER_COL};
EGrabberExtension extensions{ EGrabberExtension::None };
// the picking id shared by all the elements
int picking_id{ -1 };
std::array<std::shared_ptr<SceneRaycasterItem>, GRABBER_ELEMENTS_MAX_COUNT> raycasters = { nullptr };
Grabber();
Grabber() = default;
~Grabber();
void render(bool hover, float size) const;
void render_for_picking(float size) const { render(size, color, true); }
void render(bool hover, float size) { render(size, hover ? hover_color : color); }
float get_half_size(float size) const;
float get_dragging_half_size(float size) const;
const GLModel& get_cube() const;
PickingModel &get_cube();
void register_raycasters_for_picking(int id);
void unregister_raycasters_for_picking();
private:
void render(float size, const std::array<float, 4>& render_color, bool picking) const;
void render(float size, const ColorRGBA& render_color);
GLModel cube;
bool cube_initialized = false;
static PickingModel s_cube;
static PickingModel s_cone;
};
public:
@@ -107,24 +136,17 @@ public:
protected:
GLCanvas3D& m_parent;
int m_group_id;
int m_group_id; // TODO: remove only for rotate
EState m_state;
int m_shortcut_key;
std::string m_icon_filename;
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
std::array<float, 4> m_base_color;
std::array<float, 4> m_drag_color;
std::array<float, 4> m_highlight_color;
int m_hover_id{ -1 };
bool m_dragging{ false };
mutable std::vector<Grabber> m_grabbers;
ImGuiWrapper* m_imgui;
bool m_first_input_window_render;
mutable std::string m_tooltip;
CommonGizmosDataPool* m_c;
GLModel m_cone;
GLModel m_cylinder;
GLModel m_sphere;
bool m_first_input_window_render{ true };
CommonGizmosDataPool* m_c{ nullptr };
bool m_is_dark_mode = false;
@@ -132,7 +154,7 @@ public:
GLGizmoBase(GLCanvas3D& parent,
const std::string& icon_filename,
unsigned int sprite_id);
virtual ~GLGizmoBase() {}
virtual ~GLGizmoBase() = default;
bool init() { return on_init(); }
@@ -141,9 +163,6 @@ public:
std::string get_name(bool include_shortcut = true) const;
int get_group_id() const { return m_group_id; }
void set_group_id(int id) { m_group_id = id; }
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; on_set_state(); }
@@ -159,7 +178,7 @@ public:
virtual bool wants_enter_leave_snapshots() const { return false; }
virtual std::string get_gizmo_entering_text() const { assert(false); return ""; }
virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; }
virtual std::string get_action_snapshot_name() { return "Gizmo action"; }
virtual std::string get_action_snapshot_name() const;
void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
virtual bool apply_clipping_plane() { return true; }
@@ -168,32 +187,44 @@ public:
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
void set_highlight_color(const std::array<float, 4>& color);
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
void start_dragging();
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const UpdateData& data);
// returns True when Gizmo changed its state
bool update_items_state();
void render() { m_tooltip.clear(); on_render(); }
void render_for_picking() { on_render_for_picking(); }
void render() { on_render(); }
void render_input_window(float x, float y, float bottom_limit);
virtual void on_change_color_mode(bool is_dark) { m_is_dark_mode = is_dark; }
/// <summary>
/// Mouse tooltip text
/// </summary>
/// <returns>Text to be visible in mouse tooltip</returns>
virtual std::string get_tooltip() const { return ""; }
int get_count() { return ++count; }
std::string get_gizmo_name() { return on_get_name(); }
/// <summary>
/// Is called when data (Selection) is changed
/// </summary>
virtual void data_changed(bool is_serializing){};
/// <summary>
/// Implement when want to process mouse events in gizmo
/// Click, Right click, move, drag, ...
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
virtual bool is_in_editing_mode() const { return false; }
virtual bool is_selection_rectangle_dragging() const { return false; }
protected:
float last_input_window_width = 0;
virtual bool on_init() = 0;
@@ -207,39 +238,51 @@ protected:
virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
// called inside use_grabbers
virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
virtual void on_update(const UpdateData& data) {}
virtual void on_dragging(const UpdateData& data) {}
virtual void on_render() = 0;
virtual void on_render_for_picking() = 0;
virtual void on_render_input_window(float x, float y, float bottom_limit) {}
bool GizmoImguiBegin(const std::string& name, int flags);
void GizmoImguiEnd();
void GizmoImguiSetNextWIndowPos(float &x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
std::array<float, 4> picking_color_component(unsigned int id) const;
void register_grabbers_for_picking();
void unregister_grabbers_for_picking();
virtual void on_register_raycasters_for_picking() {}
virtual void on_unregister_raycasters_for_picking() {}
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
void render_grabbers(size_t first, size_t last, float size, bool force_hover) const;
std::string format(float value, unsigned int decimals) const;
// Mark gizmo as dirty to Re-Render when idle()
void set_dirty();
/// <summary>
/// function which
/// Set up m_dragging and call functions
/// on_start_dragging / on_dragging / on_stop_dragging
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>same as on_mouse</returns>
bool use_grabbers(const wxMouseEvent &mouse_event);
void do_stop_dragging(bool perform_mouse_cleanup);
private:
// Flag for dirty visible state of Gizmo
// When True then need new rendering
bool m_dirty;
bool m_dirty{ false };
int count = 0;
};
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace GUI
} // namespace Slic3r
File diff suppressed because it is too large Load Diff
+366 -49
View File
@@ -1,73 +1,390 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoCut_hpp_
#define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLSelectionRectangle.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/ObjectID.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/CutUtils.hpp"
#include "imgui/imgui.h"
namespace Slic3r {
enum class CutConnectorType : int;
class ModelVolume;
class GLShaderProgram;
struct CutConnectorAttributes;
namespace GUI {
class Selection;
class GLGizmoCut : public GLGizmoBase
enum class SLAGizmoEventType : unsigned char;
namespace CommonGizmosDataObjects { class ObjectClipper; }
class GLGizmoCut3D : public GLGizmoBase
{
static const double Offset;
static const double Margin;
static const std::array<float, 4> GrabberColor;
double m_cut_z{ 0.0 };
double m_max_z{ 0.0 };
double m_start_z{ 0.0 };
Vec3d m_drag_pos;
Vec3d m_drag_center;
bool m_keep_upper{ true };
bool m_keep_lower{ true };
bool m_rotate_lower{ false };
// BBS: m_do_segment
bool m_cut_to_parts {false};
bool m_do_segment{ false };
double m_segment_smoothing_alpha{ 0.5 };
int m_segment_number{ 5 };
struct CutContours
{
TriangleMesh mesh;
GLModel contours;
double cut_z{ 0.0 };
Vec3d position{ Vec3d::Zero() };
Vec3d shift{ Vec3d::Zero() };
ObjectID object_id;
int instance_idx{ -1 };
enum GrabberID {
X = 0,
Y,
Z,
CutPlane,
CutPlaneZRotation,
CutPlaneXMove,
CutPlaneYMove,
Count,
};
CutContours m_cut_contours;
Transform3d m_rotation_m{ Transform3d::Identity() };
double m_snap_step{ 1.0 };
int m_connectors_group_id;
// archived values
Vec3d m_ar_plane_center { Vec3d::Zero() };
Transform3d m_start_dragging_m{ Transform3d::Identity() };
Vec3d m_plane_center{ Vec3d::Zero() };
// data to check position of the cut palne center on gizmo activation
Vec3d m_min_pos{ Vec3d::Zero() };
Vec3d m_max_pos{ Vec3d::Zero() };
Vec3d m_bb_center{ Vec3d::Zero() };
Vec3d m_center_offset{ Vec3d::Zero() };
BoundingBoxf3 m_bounding_box;
BoundingBoxf3 m_transformed_bounding_box;
// values from RotationGizmo
double m_radius{ 0.0 };
double m_grabber_radius{ 0.0 };
double m_grabber_connection_len{ 0.0 };
Vec3d m_cut_plane_start_move_pos {Vec3d::Zero()};
double m_snap_coarse_in_radius{ 0.0 };
double m_snap_coarse_out_radius{ 0.0 };
double m_snap_fine_in_radius{ 0.0 };
double m_snap_fine_out_radius{ 0.0 };
// dragging angel in hovered axes
double m_angle{ 0.0 };
TriangleMesh m_connector_mesh;
// workaround for using of the clipping plane normal
Vec3d m_clp_normal{ Vec3d::Ones() };
Vec3d m_line_beg{ Vec3d::Zero() };
Vec3d m_line_end{ Vec3d::Zero() };
Vec2d m_ldown_mouse_position{ Vec2d::Zero() };
GLModel m_grabber_connection;
GLModel m_cut_line;
PickingModel m_plane;
PickingModel m_sphere;
PickingModel m_cone;
PickingModel m_cube;
std::map<CutConnectorAttributes, PickingModel> m_shapes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_raycasters;
GLModel m_circle;
GLModel m_scale;
GLModel m_snap_radii;
GLModel m_reference_radius;
GLModel m_angle_arc;
Vec3d m_old_center;
Vec3d m_cut_normal;
struct InvalidConnectorsStatistics
{
unsigned int outside_cut_contour;
unsigned int outside_bb;
bool is_overlap;
void invalidate() {
outside_cut_contour = 0;
outside_bb = 0;
is_overlap = false;
}
} m_info_stats;
bool m_keep_upper{ true };
bool m_keep_lower{ true };
bool m_keep_as_parts{ false };
bool m_place_on_cut_upper{ true };
bool m_place_on_cut_lower{ false };
bool m_rotate_upper{ false };
bool m_rotate_lower{ false };
// Input params for cut with tongue and groove
Cut::Groove m_groove;
bool m_groove_editing { false };
bool m_is_slider_editing_done { false };
// Input params for cut with snaps
float m_snap_bulge_proportion{ 0.15f };
float m_snap_space_proportion{ 0.3f };
bool m_hide_cut_plane{ false };
bool m_connectors_editing{ false };
bool m_cut_plane_as_circle{ false };
float m_connector_depth_ratio{ 3.f };
float m_connector_size{ 2.5f };
float m_connector_angle{ 0.f };
float m_connector_depth_ratio_tolerance{ 0.1f };
float m_connector_size_tolerance{ 0.f };
float m_label_width{ 0.f };
float m_control_width{ 200.f };
double m_editing_window_width;
bool m_imperial_units{ false };
float m_contour_width{ 0.4f };
float m_cut_plane_radius_koef{ 1.5f };
mutable std::vector<bool> m_selected; // which pins are currently selected
int m_selected_count{ 0 };
GLSelectionRectangle m_selection_rectangle;
std::vector<size_t> m_invalid_connectors_idxs;
bool m_was_cut_plane_dragged { false };
bool m_was_contour_selected { false };
// Vertices of the groove used to detection if groove is valid
std::vector<Vec3d> m_groove_vertices;
class PartSelection {
public:
PartSelection() = default;
PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc);
PartSelection(const ModelObject* mo, int instance_idx_in);
~PartSelection() { m_model.clear_objects(); }
struct Part {
GLModel glmodel;
MeshRaycaster raycaster;
bool selected;
bool is_modifier;
};
void render(const Vec3d* normal, GLModel& sphere_model);
void toggle_selection(const Vec2d& mouse_pos);
void turn_over_selection();
ModelObject* model_object() { return m_model.objects.front(); }
bool valid() const { return m_valid; }
bool is_one_object() const;
const std::vector<Part>& parts() const { return m_parts; }
const std::vector<size_t>* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); }
std::vector<Cut::Part> get_cut_parts();
private:
Model m_model;
int m_instance_idx;
std::vector<Part> m_parts;
bool m_valid = false;
std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below
std::vector<size_t> m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object)
std::vector<Vec3d> m_contour_points; // Debugging
std::vector<std::vector<Vec3d>> m_debug_pts; // Debugging
void add_object(const ModelObject* object);
};
PartSelection m_part_selection;
std::vector<std::pair<wxString, wxString>> m_shortcuts_cut;
std::vector<std::pair<wxString, wxString>> m_shortcuts_connector;
enum class CutMode {
cutPlanar
, cutTongueAndGroove
//, cutGrig
//,cutRadial
//,cutModular
};
enum class CutConnectorMode {
Auto
, Manual
};
std::vector<std::string> m_modes;
size_t m_mode{ size_t(CutMode::cutPlanar) };
std::vector<std::string> m_connector_modes;
CutConnectorMode m_connector_mode{ CutConnectorMode::Manual };
std::vector<std::string> m_connector_types;
CutConnectorType m_connector_type;
std::vector<std::string> m_connector_styles;
int m_connector_style;
std::vector<std::string> m_connector_shapes;
int m_connector_shape_id;
std::vector<std::string> m_axis_names;
std::map<std::string, wxString> m_part_orientation_names;
std::map<std::string, std::string> m_labels_map;
public:
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
double get_cut_z() const { return m_cut_z; }
void set_cut_z(double cut_z);
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
std::string get_tooltip() const override;
bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_contours = true);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool is_in_editing_mode() const override { return m_connectors_editing; }
bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); }
bool is_looking_forward() const;
/// <summary>
/// Drag of plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void shift_cut(double delta);
void rotate_vec3d_around_plane_center(Vec3d&vec);
void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset);
void update_clipper();
void invalidate_cut_plane();
BoundingBoxf3 bounding_box() const;
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, const Transform3d& rotation_m = Transform3d::Identity()) const;
protected:
virtual bool on_init() override;
virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual std::string on_get_name() const override;
virtual void on_set_state() override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
bool on_init() override;
void on_load(cereal::BinaryInputArchive&ar) override;
void on_save(cereal::BinaryOutputArchive&ar) const override;
std::string on_get_name() const override;
void on_set_state() override;
CommonGizmosDataID on_get_requirements() const override;
void on_set_hover_id() override;
bool on_is_activable() const override;
bool on_is_selectable() const override;
Vec3d mouse_position_in_local_plane(GrabberID axis, const Linef3&mouse_ray) const;
void dragging_grabber_move(const GLGizmoBase::UpdateData &data);
void dragging_grabber_rotation(const GLGizmoBase::UpdateData &data);
void dragging_connector(const GLGizmoBase::UpdateData &data);
void on_dragging(const UpdateData&data) override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_render() override;
void render_debug_input_window(float x);
void unselect_all_connectors();
void select_all_connectors();
void apply_selected_connectors(std::function<void(size_t idx)> apply_fn);
void render_connectors_input_window(CutConnectors &connectors, float x, float y, float bottom_limit);
void render_build_size();
void reset_cut_plane();
void set_connectors_editing(bool connectors_editing);
void flip_cut_plane();
void process_contours();
void reset_cut_by_contours();
void render_flip_plane_button(bool disable_pred = false);
void add_vertical_scaled_interval(float interval);
void add_horizontal_scaled_interval(float interval);
void add_horizontal_shift(float shift);
void render_color_marker(float size, const ImU32& color);
void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance);
void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val);
bool render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val);
void render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val);
void render_cut_plane_input_window(CutConnectors &connectors, float x, float y, float bottom_limit);
void init_input_window_data(CutConnectors &connectors);
void render_input_window_warning() const;
bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position);
bool delete_selected_connectors(CutConnectors&connectors);
void select_connector(int idx, bool select);
bool is_selection_changed(bool alt_down, bool shift_down);
void process_selection_rectangle(CutConnectors &connectors);
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
void update_raycasters_for_picking();
void set_volumes_picking_state(bool state);
void update_raycasters_for_picking_transform();
void update_plane_model();
void on_render_input_window(float x, float y, float bottom_limit) override;
void show_tooltip_information(float x, float y);
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); }
std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); }
void data_changed(bool is_serializing) override;
Transform3d get_cut_matrix(const Selection& selection);
private:
void perform_cut(const Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
BoundingBoxf3 bounding_box() const;
void update_contours();
void set_center(const Vec3d&center, bool update_tbb = false);
void switch_to_mode(size_t new_mode);
bool render_cut_mode_combo();
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
void render_move_center_input(int axis);
void render_connect_mode_radio_button(CutConnectorMode mode);
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
bool render_connect_type_radio_button(CutConnectorType type);
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
void render_connectors();
bool can_perform_cut() const;
bool has_valid_groove() const;
bool has_valid_contour() const;
void apply_connectors_in_model(ModelObject* mo, int &dowels_count);
bool cut_line_processing() const;
void discard_cut_line_processing();
void apply_color_clip_plane_colors();
void render_cut_plane();
static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix);
void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width);
void render_rotation_snapping(GrabberID axis, const ColorRGBA& color);
void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix, double line_len_koef = 1.0);
void render_cut_plane_grabbers();
void render_cut_line();
void perform_cut(const Selection&selection);
void set_center_pos(const Vec3d&center_pos, bool update_tbb = false);
void update_bb();
void init_picking_models();
void init_rendering_items();
void render_clipper_cut();
void clear_selection();
void reset_connectors();
void init_connector_shapes();
void update_connector_shape();
void validate_connector_settings();
bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position);
void check_and_update_connectors_state();
void toggle_model_objects_visibility();
indexed_triangle_set its_make_groove_plane();
indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes);
void apply_cut_connectors(ModelObject* mo, const std::string& connector_name);
};
} // namespace GUI
+11 -12
View File
@@ -32,9 +32,9 @@ std::string GLGizmoFaceDetector::on_get_name() const
void GLGizmoFaceDetector::on_render()
{
if (m_iva.has_VBOs()) {
::glColor4f(0.f, 0.f, 1.f, 0.4f);
m_iva.render();
if (model.is_initialized()) {
model.set_color({0.f, 0.f, 1.f, 0.4f});
model.render();
}
}
@@ -72,7 +72,7 @@ void GLGizmoFaceDetector::on_render_input_window(float x, float y, float bottom_
void GLGizmoFaceDetector::on_set_state()
{
if (get_state() == On) {
m_iva.release_geometry();
model.reset();
display_exterior_face();
}
}
@@ -94,7 +94,10 @@ void GLGizmoFaceDetector::perform_recognition(const Selection& selection)
void GLGizmoFaceDetector::display_exterior_face()
{
int cnt = 0;
m_iva.release_geometry();
model.reset();
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT };
const ModelObjectPtrs& objects = wxGetApp().model().objects;
for (ModelObject* mo : objects) {
@@ -110,19 +113,15 @@ void GLGizmoFaceDetector::display_exterior_face()
continue;
for (int i = 0; i < 3; ++i) {
m_iva.push_geometry(double(mv_its.vertices[facet_vert_idxs[i]](0)),
double(mv_its.vertices[facet_vert_idxs[i]](1)),
double(mv_its.vertices[facet_vert_idxs[i]](2)),
0., 0., 1.);
init_data.add_vertex((Vec3f) mv_its.vertices[facet_vert_idxs[i]].cast<float>(), Vec3f{0.0f, 0.0f, 1.0f});
}
m_iva.push_triangle(cnt, cnt + 1, cnt + 2);
init_data.add_uint_triangle(cnt, cnt + 1, cnt + 2);
cnt += 3;
}
}
}
m_iva.finalize_geometry(true);
model.init_from(std::move(init_data));
}
CommonGizmosDataID GLGizmoFaceDetector::on_get_requirements() const
@@ -28,7 +28,7 @@ private:
void perform_recognition(const Selection& selection);
void display_exterior_face();
GLIndexedVertexArray m_iva;
GUI::GLModel model;
double m_sample_interval = {0.5};
};
+17 -12
View File
@@ -105,7 +105,7 @@ bool GLGizmoFdmSupports::on_init()
return true;
}
void GLGizmoFdmSupports::render_painter_gizmo() const
void GLGizmoFdmSupports::render_painter_gizmo()
{
const Selection& selection = m_parent.get_selection();
@@ -116,8 +116,8 @@ void GLGizmoFdmSupports::render_painter_gizmo() const
//BBS: draw support volumes
if (m_volume_ready && m_support_volume && (m_edit_state != state_generating))
{
//m_support_volume->set_render_color();
::glColor4f(0.f, 0.7f, 0.f, 0.7f);
// TODO: FIXME
m_support_volume->set_render_color({0.f, 0.7f, 0.f, 0.7f});
m_support_volume->render();
}
@@ -177,8 +177,12 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
if (is_left_handed)
glsafe(::glFrontFace(GL_CW));
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(trafo_matrix.data()));
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
float normal_z = -::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg));
Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
@@ -188,9 +192,8 @@ void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
shader->set_uniform("slope.actived", m_parent.is_using_slope());
shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix);
shader->set_uniform("slope.normal_z", normal_z);
m_triangle_selectors[mesh_id]->render(m_imgui);
m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix);
glsafe(::glPopMatrix());
if (is_left_handed)
glsafe(::glFrontFace(GL_CCW));
}
@@ -427,7 +430,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@@ -441,7 +444,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position(clp_dist, true);
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
}
ImGui::Separator();
@@ -640,7 +643,7 @@ void GLGizmoFdmSupports::update_from_model_object(bool first_update)
m_volume_timestamps.clear();
int volume_id = -1;
std::vector<std::array<float, 4>> ebt_colors;
std::vector<ColorRGBA> ebt_colors;
ebt_colors.push_back(GLVolume::NEUTRAL_COLOR);
ebt_colors.push_back(TriangleSelectorGUI::enforcers_color);
ebt_colors.push_back(TriangleSelectorGUI::blockers_color);
@@ -894,13 +897,16 @@ void GLGizmoFdmSupports::run_thread()
print->set_status(100, L("Support Generated"));
goto _finished;
}
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
for (const SupportLayer *support_layer : m_print_instance.print_object->support_layers())
{
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
{
_3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, *m_support_volume);
_3DScene::extrusionentity_to_verts(extrusion_entity, float(support_layer->print_z), m_print_instance.shift, init_data);
}
}
m_support_volume->model.init_from(std::move(init_data));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished extrusionentity_to_verts, update status to 100%";
print->set_status(100, L("Support Generated"));
@@ -926,7 +932,6 @@ _finished:
void GLGizmoFdmSupports::generate_support_volume()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",before finalize_geometry";
m_support_volume->indexed_vertex_array.finalize_geometry(m_parent.is_initialized());
std::unique_lock<std::mutex> lck(m_mutex);
m_volume_ready = true;
+3 -2
View File
@@ -5,6 +5,7 @@
//BBS
#include "libslic3r/Print.hpp"
#include "libslic3r/ObjectID.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include <boost/thread.hpp>
@@ -15,7 +16,7 @@ class GLGizmoFdmSupports : public GLGizmoPainterBase
public:
GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
void render_painter_gizmo() const override;
void render_painter_gizmo() override;
//BBS: add edit state
enum EditState {
@@ -39,7 +40,7 @@ protected:
std::string get_gizmo_entering_text() const override { return "Entering Paint-on supports"; }
std::string get_gizmo_leaving_text() const override { return "Leaving Paint-on supports"; }
std::string get_action_snapshot_name() override { return "Paint-on supports editing"; }
std::string get_action_snapshot_name() const override { return "Paint-on supports editing"; }
// BBS
wchar_t m_current_tool = 0;
+103 -72
View File
@@ -1,6 +1,11 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoFlatten.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
@@ -16,14 +21,43 @@ namespace GUI {
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{}
bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.LeftDown()) {
if (m_hover_id != -1) {
Selection &selection = m_parent.get_selection();
if (selection.is_single_full_instance()) {
// Rotate the object so the normal points downward:
selection.flattening_rotate(m_planes[m_hover_id].normal);
m_parent.do_rotate(L("Gizmo-Place on Face"));
wxGetApp().obj_manipul()->set_dirty();
}
return true;
}
}
else if (mouse_event.LeftUp())
return m_hover_id != -1;
return false;
}
void GLGizmoFlatten::data_changed(bool is_serializing)
{
const Selection & selection = m_parent.get_selection();
const ModelObject *model_object = nullptr;
int instance_id = -1;
if (selection.is_single_full_instance() ||
selection.is_from_single_object() ) {
model_object = selection.get_model()->objects[selection.get_object_idx()];
instance_id = selection.get_instance_idx();
}
set_flattening_data(model_object, instance_id);
}
bool GLGizmoFlatten::on_init()
{
// BBS
m_shortcut_key = WXK_CONTROL_F;
return true;
}
@@ -49,77 +83,71 @@ bool GLGizmoFlatten::on_is_activable() const
return m_parent.get_selection().is_single_full_instance();
}
void GLGizmoFlatten::on_start_dragging()
{
if (m_hover_id != -1) {
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = m_parent.get_selection().get_bounding_box().center();
}
}
void GLGizmoFlatten::on_render()
{
const Selection& selection = m_parent.get_selection();
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
shader->start_using();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
if (selection.is_single_full_instance()) {
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
const Transform3d& inst_matrix = selection.get_first_volume()->get_instance_transformation().get_matrix();
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d model_matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst_matrix;
const Transform3d view_model_matrix = camera.get_view_matrix() * model_matrix;
shader->set_uniform("view_model_matrix", view_model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
if (this->is_plane_update_necessary())
update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i) {
if (i == m_hover_id)
glsafe(::glColor4fv(GLGizmoBase::FLATTEN_HOVER_COLOR.data()));
else
glsafe(::glColor4fv(GLGizmoBase::FLATTEN_COLOR.data()));
if (m_planes[i].vbo.has_VBOs())
m_planes[i].vbo.render();
m_planes[i].vbo.model.set_color(i == m_hover_id ? GLGizmoBase::FLATTEN_HOVER_COLOR : GLGizmoBase::FLATTEN_COLOR);
m_planes[i].vbo.model.render();
}
glsafe(::glPopMatrix());
}
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
shader->stop_using();
}
void GLGizmoFlatten::on_render_for_picking()
void GLGizmoFlatten::on_register_raycasters_for_picking()
{
const Selection& selection = m_parent.get_selection();
// the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account
m_parent.set_raycaster_gizmos_on_top(true);
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
assert(m_planes_casters.empty());
if (!m_planes.empty()) {
const Selection& selection = m_parent.get_selection();
const Transform3d matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) *
selection.get_first_volume()->get_instance_transformation().get_matrix();
if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) {
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i) {
glsafe(::glColor4fv(picking_color_component(i).data()));
m_planes[i].vbo.render();
m_planes_casters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_planes[i].vbo.mesh_raycaster, matrix));
}
glsafe(::glPopMatrix());
}
glsafe(::glEnable(GL_CULL_FACE));
}
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
void GLGizmoFlatten::on_unregister_raycasters_for_picking()
{
m_starting_center = Vec3d::Zero();
if (model_object != m_old_model_object) {
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo);
m_parent.set_raycaster_gizmos_on_top(false);
m_planes_casters.clear();
}
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object, int instance_id)
{
if (model_object != m_old_model_object || instance_id != m_old_instance_id) {
m_planes.clear();
m_planes_valid = false;
on_unregister_raycasters_for_picking();
}
}
@@ -136,6 +164,7 @@ void GLGizmoFlatten::update_planes()
}
ch = ch.convex_hull_3d();
m_planes.clear();
on_unregister_raycasters_for_picking();
const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true);
// Following constants are used for discarding too small polygons.
@@ -196,9 +225,7 @@ void GLGizmoFlatten::update_planes()
}
// Let's prepare transformation of the normal vector from mesh to instance coordinates.
Geometry::Transformation t(inst_matrix);
Vec3d scaling = t.get_scaling_factor();
t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2)));
const Matrix3d normal_matrix = inst_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
// Now we'll go through all the polygons, transform the points into xy plane to process them:
for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
@@ -206,7 +233,7 @@ void GLGizmoFlatten::update_planes()
const Vec3d& normal = m_planes[polygon_id].normal;
// transform the normal according to the instance matrix:
Vec3d normal_transformed = t.get_matrix() * normal;
const Vec3d normal_transformed = normal_matrix * normal;
// We are going to rotate about z and y to flatten the plane
Eigen::Quaterniond q;
@@ -219,7 +246,7 @@ void GLGizmoFlatten::update_planes()
// And yes, it is a nasty thing to do. Whoever has time is free to refactor.
Vec3d bb_size = BoundingBoxf3(polygon).size();
float sf = std::min(1./bb_size(0), 1./bb_size(1));
Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f));
Transform3d tr = Geometry::scale_transform({ sf, sf, 1.f });
polygon = transform(polygon, tr);
polygon = Slic3r::Geometry::convex_hull(polygon);
polygon = transform(polygon, tr.inverse());
@@ -324,34 +351,46 @@ void GLGizmoFlatten::update_planes()
m_first_instance_scale = mo->instances.front()->get_scaling_factor();
m_first_instance_mirror = mo->instances.front()->get_mirror();
m_old_model_object = mo;
m_old_instance_id = m_c->selection_info()->get_active_instance();
// And finally create respective VBOs. The polygon is convex with
// the vertices in order, so triangulation is trivial.
for (auto& plane : m_planes) {
plane.vbo.reserve(plane.vertices.size());
for (const auto& vert : plane.vertices)
plane.vbo.push_geometry(vert, plane.normal);
for (size_t i=1; i<plane.vertices.size()-1; ++i)
plane.vbo.push_triangle(0, i, i+1); // triangle fan
plane.vbo.finalize_geometry(true);
// FIXME: vertices should really be local, they need not
// persist now when we use VBOs
plane.vertices.clear();
plane.vertices.shrink_to_fit();
indexed_triangle_set its;
its.vertices.reserve(plane.vertices.size());
its.indices.reserve(plane.vertices.size() / 3);
for (size_t i = 0; i < plane.vertices.size(); ++i) {
its.vertices.emplace_back((Vec3f)plane.vertices[i].cast<float>());
}
for (size_t i = 1; i < plane.vertices.size() - 1; ++i) {
its.indices.emplace_back(0, i, i + 1); // triangle fan
}
plane.vbo.model.init_from(its);
if (Geometry::Transformation(inst_matrix).is_left_handed()) {
// we need to swap face normals in case the object is mirrored
// for the raycaster to work properly
for (stl_triangle_vertex_indices& face : its.indices) {
if (its_face_normal(its, face).cast<double>().dot(plane.normal) < 0.0)
std::swap(face[1], face[2]);
}
}
plane.vbo.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
// vertices are no more needed, clear memory
plane.vertices = std::vector<Vec3d>();
}
m_planes_valid = true;
on_register_raycasters_for_picking();
}
bool GLGizmoFlatten::is_plane_update_necessary() const
{
const ModelObject* mo = m_c->selection_info()->model_object();
if (m_state != On || ! mo || mo->instances.empty())
return false;
if (! m_planes_valid || mo != m_old_model_object
|| mo->volumes.size() != m_volumes_matrices.size())
if (m_planes.empty() || mo != m_old_model_object
|| mo->volumes.size() != m_volumes_matrices.size())
return true;
// We want to recalculate when the scale changes - some planes could (dis)appear.
@@ -367,13 +406,5 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
return false;
}
Vec3d GLGizmoFlatten::get_flattening_normal() const
{
Vec3d out = m_normal;
m_normal = Vec3d::Zero();
m_starting_center = Vec3d::Zero();
return out;
}
} // namespace GUI
} // namespace Slic3r
+27 -17
View File
@@ -1,9 +1,13 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoFlatten_hpp_
#define slic3r_GLGizmoFlatten_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
namespace Slic3r {
@@ -18,13 +22,13 @@ class GLGizmoFlatten : public GLGizmoBase
// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
private:
mutable Vec3d m_normal;
struct PlaneData {
std::vector<Vec3d> vertices; // should be in fact local in update_planes()
GLIndexedVertexArray vbo;
PickingModel vbo;
Vec3d normal;
float area;
int picking_id{ -1 };
};
// This holds information to decide whether recalculation is necessary:
@@ -34,10 +38,9 @@ private:
Vec3d m_first_instance_mirror;
std::vector<PlaneData> m_planes;
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_planes_casters;
const ModelObject* m_old_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
int m_old_instance_id{ -1 };
void update_planes();
bool is_plane_update_necessary() const;
@@ -45,18 +48,25 @@ private:
public:
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
void set_flattening_data(const ModelObject* model_object, int instance_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed(bool is_serializing) override;
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_set_state() override;
virtual CommonGizmosDataID on_get_requirements() const override;
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
void on_render() override;
void on_register_raycasters_for_picking() override;
void on_unregister_raycasters_for_picking() override;
void on_set_state() override;
CommonGizmosDataID on_get_requirements() const override;
};
} // namespace GUI
+39 -67
View File
@@ -20,7 +20,6 @@ namespace GUI {
GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
m_vbo_cylinder.init_from(its_make_cylinder(1., 1.));
}
@@ -63,6 +62,9 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
void GLGizmoHollow::on_render()
{
if (!m_cylinder.is_initialized())
m_cylinder.init_from(its_make_cylinder(1.0, 1.0));
const Selection& selection = m_parent.get_selection();
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
@@ -87,98 +89,75 @@ void GLGizmoHollow::on_render()
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoHollow::on_render_for_picking()
void GLGizmoHollow::render_points(const Selection& selection, bool picking)
{
const Selection& selection = m_parent.get_selection();
//#if ENABLE_RENDER_PICKING_PASS
// m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
//#endif
GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
}
void GLGizmoHollow::render_points(const Selection& selection, bool picking) const
{
GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
if (shader)
shader->start_using();
ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
shader->start_using();
ScopeGuard guard([shader]() { shader->stop_using(); });
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * vol->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
glsafe(::glMultMatrixd(instance_matrix.data()));
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix();
const Transform3d& projection_matrix = camera.get_projection_matrix();
std::array<float, 4> render_color;
shader->set_uniform("projection_matrix", projection_matrix);
ColorRGBA render_color;
const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
size_t cache_size = drain_holes.size();
const size_t cache_size = drain_holes.size();
for (size_t i = 0; i < cache_size; ++i) {
const sla::DrainHole& drain_hole = drain_holes[i];
const bool& point_selected = m_selected[i];
const bool point_selected = m_selected[i];
if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
continue;
// First decide about the color of the point.
if (picking) {
std::array<float, 4> color = picking_color_component(i);
render_color = color;
}
if (picking)
render_color = picking_color_component(i);
else {
if (size_t(m_hover_id) == i) {
render_color = {0.f, 1.f, 1.f, 1.f};
}
if (size_t(m_hover_id) == i)
render_color = ColorRGBA::CYAN();
else if (m_c->hollowed_mesh() &&
i < m_c->hollowed_mesh()->get_drainholes().size() &&
m_c->hollowed_mesh()->get_drainholes()[i].failed) {
render_color = {1.f, 0.f, 0.f, .5f};
}
else { // neigher hover nor picking
render_color[0] = point_selected ? 1.0f : 1.f;
render_color[1] = point_selected ? 0.3f : 1.f;
render_color[2] = point_selected ? 0.3f : 1.f;
render_color[3] = 0.5f;
render_color = { 1.0f, 0.0f, 0.0f, 0.5f };
}
else // neither hover nor picking
render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
}
const_cast<GLModel*>(&m_vbo_cylinder)->set_color(-1, render_color);
m_cylinder.set_color(render_color);
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2)));
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
const Transform3d hole_matrix = Geometry::assemble_transform(drain_hole.pos.cast<double>()) * instance_scaling_matrix_inverse;
if (vol->is_left_handed())
glFrontFace(GL_CW);
// Matrices set, we can render the point mark now.
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
glsafe(::glPushMatrix());
glsafe(::glTranslated(0., 0., -drain_hole.height));
glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
m_vbo_cylinder.render();
glsafe(::glPopMatrix());
q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-drain_hole.normal).cast<double>());
const Eigen::AngleAxisd aa(q);
const Transform3d model_matrix = instance_matrix * hole_matrix * Transform3d(aa.toRotationMatrix()) *
Geometry::assemble_transform(-drain_hole.height * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
m_cylinder.render();
if (vol->is_left_handed())
glFrontFace(GL_CCW);
glsafe(::glPopMatrix());
}
glsafe(::glPopMatrix());
}
bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
{
if (m_c->object_clipper()->get_position() == 0.)
@@ -544,14 +523,11 @@ RENDER_AGAIN:
}
m_imgui->disabled_begin(! m_enable_hollowing);
float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("offset"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
ImGui::PushItemWidth(window_width - settings_sliders_left);
m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width);
m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm", 1.0f, true, _L(opts[0].second->tooltip));
bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider
bool slider_edited =m_imgui->get_last_slider_status().edited; // someone is dragging the slider
@@ -561,9 +537,7 @@ RENDER_AGAIN:
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("quality"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width);
m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f", 1.0f, true, _L(opts[1].second->tooltip));
slider_clicked |= m_imgui->get_last_slider_status().clicked;
slider_edited |= m_imgui->get_last_slider_status().edited;
@@ -574,9 +548,7 @@ RENDER_AGAIN:
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("closing_distance"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width);
m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm", 1.0f, true, _L(opts[2].second->tooltip));
slider_clicked |= m_imgui->get_last_slider_status().clicked;
slider_edited |= m_imgui->get_last_slider_status().edited;
+3 -3
View File
@@ -40,15 +40,15 @@ private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() override;
void on_render_for_picking() override;
void render_points(const Selection& selection, bool picking = false) const;
void render_points(const Selection& selection, bool picking = false);
void hollow_mesh(bool postpone_error_messages = false);
bool unsaved_changes() const;
ObjectID m_old_mo_id = -1;
GLModel m_vbo_cylinder;
GLModel m_cylinder;
float m_new_hole_radius = 2.f; // Size of a new hole.
float m_new_hole_height = 6.f;
mutable std::vector<bool> m_selected; // which holes are currently selected
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoMeasure_hpp_
#define slic3r_GLGizmoMeasure_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "libslic3r/Measure.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
enum class ModelVolumeType : int;
namespace Measure { class Measuring; }
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class GLGizmoMeasure : public GLGizmoBase
{
enum class EMode : unsigned char
{
FeatureSelection,
PointSelection
};
struct SelectedFeatures
{
struct Item
{
bool is_center{ false };
std::optional<Measure::SurfaceFeature> source;
std::optional<Measure::SurfaceFeature> feature;
bool operator == (const Item& other) const {
return this->is_center == other.is_center && this->source == other.source && this->feature == other.feature;
}
bool operator != (const Item& other) const {
return !operator == (other);
}
void reset() {
is_center = false;
source.reset();
feature.reset();
}
};
Item first;
Item second;
void reset() {
first.reset();
second.reset();
}
bool operator == (const SelectedFeatures & other) const {
if (this->first != other.first) return false;
return this->second == other.second;
}
bool operator != (const SelectedFeatures & other) const {
return !operator == (other);
}
};
struct VolumeCacheItem
{
const ModelObject* object{ nullptr };
const ModelInstance* instance{ nullptr };
const ModelVolume* volume{ nullptr };
Transform3d world_trafo;
bool operator == (const VolumeCacheItem& other) const {
return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
this->world_trafo.isApprox(other.world_trafo);
}
};
std::vector<VolumeCacheItem> m_volumes_cache;
EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result;
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
PickingModel m_sphere;
PickingModel m_cylinder;
PickingModel m_circle;
PickingModel m_plane;
struct Dimensioning
{
GLModel line;
GLModel triangle;
GLModel arc;
};
Dimensioning m_dimensioning;
// Uses a standalone raycaster and not the shared one because of the
// difference in how the mesh is updated
std::unique_ptr<MeshRaycaster> m_raycaster;
std::vector<GLModel> m_plane_models_cache;
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
// used to keep the raycasters for point/center spheres
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selected_sphere_raycasters;
std::optional<Measure::SurfaceFeature> m_curr_feature;
std::optional<Vec3d> m_curr_point_on_feature_position;
struct SceneRaycasterState
{
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
bool state{true};
};
std::vector<SceneRaycasterState> m_scene_raycasters;
// These hold information to decide whether recalculation is necessary:
float m_last_inv_zoom{ 0.0f };
std::optional<Measure::SurfaceFeature> m_last_circle;
int m_last_plane_idx{ -1 };
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
Vec2d m_mouse_pos{ Vec2d::Zero() };
KeyAutoRepeatFilter m_shift_kar_filter;
SelectedFeatures m_selected_features;
bool m_pending_scale{ false };
bool m_editing_distance{ false };
bool m_is_editing_distance_first_frame{ true };
void update_if_needed();
void disable_scene_raycasters();
void restore_scene_raycasters_state();
void render_dimensioning();
#if ENABLE_MEASURE_GIZMO_DEBUG
void render_debug_dialog();
#endif // ENABLE_MEASURE_GIZMO_DEBUG
public:
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed(bool is_serializing) override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); }
std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
protected:
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
void on_render() override;
void on_set_state() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
void remove_selected_sphere_raycaster(int id);
void update_measurement_result();
// Orca
void show_tooltip_information(float caption_max, float x, float y);
private:
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoMeasure_hpp_
+24 -3
View File
@@ -50,7 +50,7 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh());
MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh_ptr());
if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal,
m_c->object_clipper()->get_clipping_plane(), &facet)) {
// Is this hit the closest to the camera so far?
@@ -84,6 +84,27 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
return true;
}
bool GLGizmoMeshBoolean::on_mouse(const wxMouseEvent &mouse_event)
{
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
// when control is down we allow scene pan and rotation even when clicking
// over some object
bool control_down = mouse_event.CmdDown();
bool grabber_contains_mouse = (get_hover_id() != -1);
if (mouse_event.LeftDown()) {
if ((!control_down || grabber_contains_mouse) &&
gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false))
// the gizmo got the event and took some action, there is no need
// to do anything more
return true;
}
return false;
}
bool GLGizmoMeshBoolean::on_init()
{
m_shortcut_key = WXK_CONTROL_B;
@@ -131,8 +152,8 @@ void GLGizmoMeshBoolean::on_render()
}
}
float src_color[3] = { 1.0f, 1.0f, 1.0f };
float tool_color[3] = { 0.0f, 150.0f / 255.0f, 136.0f / 255.0f };
ColorRGB src_color = { 1.0f, 1.0f, 1.0f };
ColorRGB tool_color = {0.0f, 150.0f / 255.0f, 136.0f / 255.0f};
m_parent.get_selection().render_bounding_box(src_bb, src_color, m_parent.get_scale());
m_parent.get_selection().render_bounding_box(tool_bb, tool_color, m_parent.get_scale());
}

Some files were not shown because too many files have changed in this diff Show More